Есть ли способ активировать функцию экспорта PDF в PhantomJS без указания выходного файла с расширением .pdf? Мы хотели бы использовать stdout
для вывода PDF.
PhantomJS: экспорт PDF в стандартный вывод
Ответы (4)
Как указал Нико, вы можете использовать renderBase64()
для рендеринга веб-страницы в буфер изображения и возврата результата в виде строки в кодировке base64.
Но пока это будет работать только для PNG, JPEG и GIF.
Чтобы написать что-то из сценария phantomjs в стандартный вывод, просто используйте 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 файлу.
fs.read
на var pdf = fs.open(output, 'rb').read();
— чтение файла в двоичном виде было важно (иначе перенаправление stdout в файл приводило к неправильному PDF). Однако позже мне удалось заставить это работать вообще без временного файла - см. stackoverflow.com/a /17282463/137067
- person philfreo; 24.06.2013
Вы можете выводить напрямую на стандартный вывод без необходимости во временном файле.
page.render('/dev/stdout', { format: 'pdf' });
См. здесь, чтобы узнать, когда это было добавлено.
Если вы хотите получить HTML из стандартного ввода и вывести PDF на стандартный вывод, см. здесь
page.viewportSize
настройкам.
- person Rick Mohr; 09.03.2015
Извините за очень длинный ответ; У меня есть ощущение, что мне придется обращаться к этому методу несколько десятков раз в жизни, поэтому я напишу «один ответ, чтобы править всеми». Сначала я немного расскажу о файлах, файловых дескрипторах, (именованных) каналах и перенаправлении вывода, а затем отвечу на ваш вопрос.
Рассмотрим эту простую программу 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
, и управление вернется к терминалу.
Затем просто перенаправьте вывод нашей программы на C в этот канал:
(./write_to_file.o /dev/fd/5) 5> my_pipe
Or
./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 (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 а>:
рендеринг(имя файла)
Отображает веб-страницу в буфер изображения и сохраняет ее как указанный файл.
В настоящее время выходной формат устанавливается автоматически на основе расширения файла. Поддерживаемые форматы: 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
s этоhn.pdf
В PhantomJS выполните рендеринг в этот файл/канал.
Позже вы должны удалить трубу:
rm my_pipe.pdf
Я не знаю, решит ли это вашу проблему, но вы также можете проверить новый метод renderBase64()
, добавленный в PhantomJS 1.6: https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L623
К сожалению, эта функция еще не задокументирована на вики :/