Есть ли способ запустить функцию экспорта PDF в PhantomJS без указания выходного файла с расширением .pdf? Мы хотим использовать stdout
для вывода PDF.
PhantomJS: экспортированный PDF файл в stdout
Ответ 1
Как указано Niko, вы можете использовать renderBase64()
для рендеринга веб-страницы в буфер изображения и вернуть результат в виде строки в кодировке base64.
Но пока это будет работать только для PNG, JPEG и GIF.
Чтобы написать что-то из phantomjs script в stdout, просто используйте API-интерфейс файловой системы.
Я использую что-то подобное для изображений:
var base64image = page.renderBase64('PNG');
var fs = require("fs");
fs.write("/dev/stdout", base64image, "w");
Я не знаю, будет ли формат PDF для renderBase64()
в будущей версии phanthomjs, но в качестве обходного пути что-то в этих строках может работать для вас:
page.render(output);
var fs = require("fs");
var pdf = fs.read(output);
fs.write("/dev/stdout", pdf, "w");
fs.remove(output);
Где output
- путь к файлу pdf.
Ответ 2
Извините за чрезвычайно долгий ответ; У меня такое чувство, что мне нужно будет обратиться к этому методу несколько десятков раз в жизни, поэтому я напишу "один ответ, чтобы править ими всеми". Сначала я немного забуду о файлах, дескрипторах файлов, (названных) трубах и перенаправлении вывода, а затем отвечу на ваш вопрос.
Рассмотрим эту простую программу C99:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
if (argc < 2) {
printf("Usage: %s file_name\n", argv[0]);
return 1;
}
FILE* file = fopen(argv[1], "w");
if (!file) {
printf("No such file: %s\n", argv[1]);
return 2;
}
fprintf(file, "some text...");
fclose(file);
return 0;
}
Очень просто. Он принимает аргумент (имя файла) и печатает в нем некоторый текст. Не может быть проще.
Скомпилируйте его с помощью clang write_to_file.c -o write_to_file.o
или gcc write_to_file.c -o write_to_file.o
.
Теперь запустите ./write_to_file.o some_file
(который печатает в some_file
). Затем запустите cat some_file
. Результат, как и ожидалось, равен some text...
Теперь позвольте получить больше фантазии. Введите (./write_to_file.o /dev/stdout) > some_file
в терминал. Мы просим программу написать ее стандартный вывод (вместо обычного файла), а затем перенаправляем это stdout
в some_file
(используя > some_file
). Для достижения этой цели мы могли бы использовать любое из следующих действий:
-
(./write_to_file.o /dev/stdout) > some_file
, что означает "использоватьstdout
" -
(./write_to_file.o /dev/stderr) 2> some_file
, что означает "использоватьstderr
и перенаправить его с помощью2>
" -
(./write_to_file.o /dev/fd/2) 2> some_file
, что является таким же, как указано выше;stderr
- это третий файловый дескриптор, назначенный процессам Unix по умолчанию (послеstdin
иstdout
) -
(./write_to_file.o /dev/fd/5) 5> some_file
, что означает "использовать ваш шестой файловый дескриптор и перенаправить его наsome_file
"
Если это не ясно, мы используем Unix-канал вместо фактического файла (все-таки все файлы в Unix). Мы можем делать всевозможные причудливые вещи с помощью этого канала: записать его в файл или записать его в именованный канал и поделиться им между различными процессами.
Теперь создадим именованный канал:
mkfifo my_pipe
Если вы наберете ls -l
сейчас, вы увидите:
total 32
prw-r--r-- 1 pooriaazimi staff 0 Jul 15 09:12 my_pipe
-rw-r--r-- 1 pooriaazimi staff 336 Jul 15 08:29 write_to_file.c
-rwxr-xr-x 1 pooriaazimi staff 8832 Jul 15 08:34 write_to_file.o
Обратите внимание на p в начале второй строки. Это означает, что my_pipe
является (названным) каналом.
Теперь укажите, что мы хотим сделать с нашей трубкой:
gzip -c < my_pipe > out.gz &
Это означает: gzip
то, что я помещаю внутри my_pipe
, и записывать результаты в out.gz
. В конце &
запрашивается оболочка для запуска этой команды в фоновом режиме. Вы получите что-то вроде [1] 10449
, и элемент управления вернется к терминалу.
Затем просто перенаправьте вывод нашей программы на этот канал:
(./write_to_file.o /dev/fd/5) 5> my_pipe
или
./write_to_file.o my_pipe
Вы получите
[1]+ Done gzip -c < my_pipe > out.gz
что означает, что команда gzip
завершена.
Теперь сделайте еще один ls -l
:
total 40
prw-r--r-- 1 pooriaazimi staff 0 Jul 15 09:14 my_pipe
-rw-r--r-- 1 pooriaazimi staff 32 Jul 15 09:14 out.gz
-rw-r--r-- 1 pooriaazimi staff 336 Jul 15 08:29 write_to_file.c
-rwxr-xr-x 1 pooriaazimi staff 8832 Jul 15 08:34 write_to_file.o
Мы успешно gzip
наш текст!
Выполните gzip -d out.gz
, чтобы распаковать этот файл gzip
ed. Он будет удален, и будет создан новый файл (out
). cat out
получает нас:
some text...
что мы и ожидали.
Не забудьте удалить трубку с помощью rm my_pipe
!
Теперь вернемся к PhantomJS.
Это простой PhantomJS script (render.coffee
, написанный на CoffeeScript), который принимает два аргумента: URL и имя файла. Он загружает URL-адрес, отображает его и записывает в указанное имя файла:
system = require 'system'
renderUrlToFile = (url, file, callback) ->
page = require('webpage').create()
page.viewportSize = { width: 1024, height : 800 }
page.settings.userAgent = 'Phantom.js bot'
page.open url, (status) ->
if status isnt 'success'
console.log "Unable to render '#{url}'"
else
page.render file
delete page
callback url, file
url = system.args[1]
file_name = system.args[2]
console.log "Will render to #{file_name}"
renderUrlToFile "http://#{url}", file_name, (url, file) ->
console.log "Rendered '#{url}' to '#{file}'"
phantom.exit()
Теперь введите phantomjs render.coffee news.ycombinator.com hn.png
в терминал, чтобы отобразить первую страницу Hacker News в файле hn.png
. Он работает так, как ожидалось. Так что phantomjs render.coffee news.ycombinator.com hn.pdf
.
Повторите то, что мы сделали ранее с нашей программой на C:
(phantomjs render.coffee news.ycombinator.com /dev/fd/5) 5> hn.pdf
Это не работает...:( Почему? Потому что, как указано в Руководство PhantomJS:
render (fileName)
Отображает веб-страницу в буфер изображения и сохраняет ее как указанный файл.
В настоящее время формат вывода автоматически устанавливается на основе файла расширение. Поддерживаемые форматы: PNG, JPEG и PDF.
Это не удается, просто потому, что ни /dev/fd/2
, ни /dev/stdout
не заканчиваются на .PNG
и т.д.
Но ни один страх, названные трубы не помогут вам!
Создайте другой именованный канал, но на этот раз используйте расширение .pdf
:
mkfifo my_pipe.pdf
Теперь скажите просто cat
его inout на hn.pdf
:
cat < my_pipe.pdf > hn.pdf &
Затем запустите:
phantomjs render.coffee news.ycombinator.com my_pipe.pdf
И вот красивый hn.pdf
!
Очевидно, что вы хотите сделать что-то более сложное, просто cat
с выходом, но я уверен, теперь ясно, что вы должны делать:)
TL; DR:
-
Создайте именованный канал, используя расширение ".pdf" (так что он обманывает PhantomJS, считая его файлом PDF):
mkfifo my_pipe.pdf
-
Сделайте все, что вы хотите сделать с содержимым файла, например:
cat < my_pipe.pdf > hn.pdf
который просто
cat
доhn.pdf
-
В PhantomJS отрисуйте этот файл/трубу.
-
Позже вы должны удалить трубку:
rm my_pipe.pdf
Ответ 3
Вы можете напрямую выводить на stdout без необходимости временного файла.
page.render('/dev/stdout', { format: 'pdf' });
См. здесь для истории, когда это было добавлено.
Если вы хотите получить HTML из stdin и вывести PDF в stdout, см. здесь
Ответ 4
Я не знаю, будет ли это касаться вашей проблемы, но вы также можете проверить новый метод renderBase64()
, добавленный в PhantomJS 1.6: https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L623
К сожалению, эта функция еще не задокументирована в wiki:/