nodejs - phantom html to pdf node js
PhantomJS: PDF exportado a stdout (4)
¿Hay alguna manera de desencadenar la función de exportación de PDF en PhantomJS sin especificar un archivo de salida con la extensión .pdf? Nos gustaría usar stdout
para generar el PDF.
Como señaló Niko, puede usar renderBase64()
para representar la página web en un búfer de imagen y devolver el resultado como una cadena codificada en base64.
Pero por ahora esto solo funcionará para PNG, JPEG y GIF.
Para escribir algo desde una secuencia de comandos phantomjs a stdout simplemente use la API del sistema de archivos.
Utilizo algo como esto para las imágenes:
var base64image = page.renderBase64(''PNG'');
var fs = require("fs");
fs.write("/dev/stdout", base64image, "w");
No sé si el formato PDF de renderBase64()
estará en una versión futura de phanthomjs, pero como solución temporal, algo en este sentido puede funcionar para usted:
page.render(output);
var fs = require("fs");
var pdf = fs.read(output);
fs.write("/dev/stdout", pdf, "w");
fs.remove(output);
Donde la output
es la ruta al archivo pdf.
No sé si solucionaría su problema, pero también puede verificar el nuevo método renderBase64()
agregado a PhantomJS 1.6: https://github.com/ariya/phantomjs/blob/master/src/webpage.cpp# L623
Lamentablemente, la función aún no está documentada en la wiki: /
Perdón por la respuesta extremadamente larga; Tengo la sensación de que necesitaré referirme a este método varias docenas de veces en mi vida, así que escribiré "una respuesta para gobernarlos a todos". Primero voy a balbucear un poco sobre los archivos, los descriptores de archivos, los conductos (con nombre) y la redirección de salida, y luego respondo tu pregunta.
Considere este sencillo programa 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;
}
Muy sencillo. Toma un argumento (un nombre de archivo) e imprime un texto en él. No podría ser más simple.
Compilarlo con clang write_to_file.c -o write_to_file.o
o gcc write_to_file.c -o write_to_file.o
.
Ahora, ejecute ./write_to_file.o some_file
(que se imprime en some_file
). Luego ejecuta cat some_file
. El resultado, como se esperaba, es some text...
Ahora vamos a ser más elegantes. Escriba (./write_to_file.o /dev/stdout) > some_file
en la terminal. Estamos pidiendo al programa que escriba en su salida estándar (en lugar de un archivo normal), y luego estamos redirigiendo ese stdout
a some_file
(usando > some_file
). Podríamos haber usado cualquiera de los siguientes para lograr esto:
(./write_to_file.o /dev/stdout) > some_file
, que significa "usestdout
"(./write_to_file.o /dev/stderr) 2> some_file
, que significa "usarstderr
, y redirigirlo usando2>
"(./write_to_file.o /dev/fd/2) 2> some_file
, que es el mismo que el anterior;stderr
es el tercer descriptor de archivo asignado a los procesos de Unix por defecto (después destdin
ystdout
)(./write_to_file.o /dev/fd/5) 5> some_file
, que significa "usar el sexto descriptor de archivo y redirigirlo asome_file
"
En caso de que no esté claro, estamos usando un conducto Unix en lugar de un archivo real (después de todo, todo es un archivo en Unix). Podemos hacer todo tipo de cosas sofisticadas con este tubo: escríbalo en un archivo, o escríbelo en una tubería con nombre y compártela entre diferentes procesos.
Ahora, creemos una tubería con nombre:
mkfifo my_pipe
Si escribe ls -l
ahora, verá:
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
Tenga en cuenta la p al comienzo de la segunda línea. Significa que my_pipe
es una tubería (con nombre).
Ahora, especifiquemos qué queremos hacer con nuestra tubería:
gzip -c < my_pipe > out.gz &
Significa: gzip
lo que puse dentro de my_pipe
y escribir los resultados en out.gz
El &
al final le pide al shell que ejecute este comando en segundo plano. Obtendrás algo como [1] 10449
y el control volverá a la terminal.
Luego, simplemente redirija la salida de nuestro programa C a esta tubería:
(./write_to_file.o /dev/fd/5) 5> my_pipe
O
./write_to_file.o my_pipe
Obtendrás
[1]+ Done gzip -c < my_pipe > out.gz
lo que significa que el comando gzip
ha terminado.
Ahora, haz otra 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
¡Hemos gzip
éxito nuestro texto!
Ejecute gzip -d out.gz
para descomprimir este archivo gzip
ed. Se eliminará y se creará un nuevo archivo ( out
). cat out
nos atrapa:
some text...
que es lo que esperábamos
¡No olvides quitar la tubería con rm my_pipe
!
Ahora volvemos a PhantomJS.
Este es un simple script PhantomJS ( render.coffee
, escrito en CoffeeScript) que toma dos argumentos: una URL y un nombre de archivo. Carga la URL, la procesa y la escribe en el nombre de archivo dado:
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()
Ahora escriba phantomjs render.coffee news.ycombinator.com hn.png
en la terminal para phantomjs render.coffee news.ycombinator.com hn.png
la página principal de Hacker News en el archivo hn.png
. Funciona como se esperaba. Lo mismo ocurre con phantomjs render.coffee news.ycombinator.com hn.pdf
.
Repitamos lo que hicimos antes con nuestro programa C:
(phantomjs render.coffee news.ycombinator.com /dev/fd/5) 5> hn.pdf
No funciona ... :( ¿Por qué? Porque, como se indica en el manual de PhantomJS :
render (fileName)
Renderiza la página web a un buffer de imagen y la guarda como el archivo especificado.
Actualmente, el formato de salida se establece automáticamente en función de la extensión del archivo. Los formatos admitidos son PNG, JPEG y PDF.
Falla, simplemente porque ni /dev/fd/2
ni /dev/stdout
terminan en .PNG
, etc.
¡Pero sin miedo, las tuberías con nombre te pueden ayudar!
Cree otro conducto con nombre, pero esta vez use la extensión .pdf
:
mkfifo my_pipe.pdf
Ahora, dígale que simplemente se lance a hn.pdf
:
cat < my_pipe.pdf > hn.pdf &
Entonces corre:
phantomjs render.coffee news.ycombinator.com my_pipe.pdf
¡Y he aquí el hermoso hn.pdf
!
Obviamente, quieres hacer algo más sofisticado que simplemente obtener el resultado, pero estoy seguro de que ahora está claro lo que debes hacer :)
TL; DR:
Cree un conducto con nombre, utilizando la extensión de archivo ".pdf" (por lo que engaña a PhantomJS para que piense que es un archivo PDF):
mkfifo my_pipe.pdf
Haga lo que quiera con el contenido del archivo, como:
cat < my_pipe.pdf > hn.pdf
que simplemente se lo da a
hn.pdf
En PhantomJS, preséntelo en este archivo / tubería.
Más adelante, debes quitar la tubería:
rm my_pipe.pdf
Puede enviar directamente a stdout sin necesidad de un archivo temporal.
page.render(''/dev/stdout'', { format: ''pdf'' });
Consulte aquí para obtener información sobre cuándo se agregó esto.
Si desea obtener HTML de stdin y enviar el PDF a stdout, consulte aquí