有没有办法在不指定扩展名为 .pdf 的输出文件的情况下触发 PhantomJS 中的 PDF 导出功能?我们想用它stdout
来输出 PDF。
4 回答
很抱歉回答太长了;我有一种感觉,我一生中需要参考这种方法几十次,所以我会写“一个答案来统治他们”。我将首先讨论一下文件、文件描述符、(命名的)管道和输出重定向,然后回答您的问题。
考虑这个简单的 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
用or编译它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
或者
./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
输入到hn.pdf
:
cat < my_pipe.pdf > hn.pdf &
然后运行:
phantomjs render.coffee news.ycombinator.com my_pipe.pdf
看美丽hn.pdf
!
显然你想做一些更复杂的事情,只是cat
输出,但我相信现在你应该做什么很清楚:)
TL;博士:
创建一个命名管道,使用“.pdf”文件扩展名(所以它会让 PhantomJS 误以为它是一个 PDF 文件):
mkfifo my_pipe.pdf
对文件的内容做任何你想做的事情,比如:
cat < my_pipe.pdf > hn.pdf
这简直
cat
就是hn.pdf
在 PhantomJS 中,渲染到这个文件/管道。
稍后,您应该移除管道:
rm my_pipe.pdf
正如 Niko 所指出的,您可以使用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文件的路径在哪里。
我不知道它是否能解决您的问题,但您也可以检查renderBase64()
添加到 PhantomJS 1.6 的新方法:https ://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L623
不幸的是,该功能尚未在 wiki 上记录:/