新增功能:有关此黑客及其操作方式的更多详细信息,请查看我在2020年大会上的演讲,以及在PDF内" Playing Breakout ...的游戏!
像你们中的许多人一样,我一直认为PDF基本上是一种良性格式,作者在其中布置一些文本和图形,然后PDF坐在读者面前,并且不做任何事情。几年前,我曾听说过有关Adobe Reader中的漏洞的信息,但并没有过多考虑它们可能或为什么存在的可能性。
这就是为什么Adobe首先制作PDF的原因,但我认为我们已经确定它不再是真的了。 1,310页的PDF规范(实际上是非常清晰有趣的阅读)指定了奇特的功能,其中包括:
当然,大多数PDF阅读器(Adobe Reader除外)都不会实现大多数此类内容。但是Chrome确实实现了JavaScript!如果您在Chrome中打开这样的PDF文件,它将运行脚本。在关注有关如何使用JS制作PDF的博客文章之后,我发现了这个事实。
不过有一个陷阱。 Chrome仅实现了巨大的Acrobat JavaScript API表面的一小部分。 Chrome的PDFium阅读器中的API实现主要由以下存根组成:
FX_BOOL文档:: addAnnot(IJS_Context * cc,const CJS_Parameters& params,CJS_Value& vRet,CFX_WideString& sError){//不支持。 return TRUE;} FX_BOOL Document :: addField(IJS_Context * cc,const CJS_Parameters& params,CJS_Value& vRet,CFX_WideString& sError){//不支持。 return TRUE;} FX_BOOL Document :: exportAsText(IJS_Context * cc,const CJS_Parameters& params,CJS_Value& vRet,CFX_WideString& sError){//不安全,不受支持。返回TRUE;}
我了解他们的担忧-自定义Adobe JavaScript API具有绝对巨大的表面积。脚本可以做诸如建立任意数据库连接,检测连接的监视器,导入外部资源以及操纵3D对象之类的操作。
因此,我们在Chrome中遇到了一个奇怪的情况:我们可以进行任意计算,但是我们有一个怪异的,受约束的API界面,在这里烦人的I / O并在程序和用户之间获取数据。[^ situation] [^ es6]
例如,可以通过使用Emscripten将C编译器编译为JS来将C编译器嵌入到PDF中,但是您的C编译器必须通过纯文本格式字段获取输入,然后将其输出通过表单字段吐出。
[^ ps]:实际上,几个星期前,由于PostScript,我对PDF感兴趣。我一直在阅读有关DonWS的随机Don Don Hopkins帖子,该系统据说类似于AJAX,但在80年代在PostScript上完成。
具有讽刺意味的是,PDF是PostScript的一种[反应](https://en.wikipedia.org/wiki/Portable_Document_Format#PostScript),它表现力强(是一种完整的编程语言),并且太难分析和推理了。我认为PDF仍然是一个很大的改进,但是它如何扩展所有这些功能仍然很有趣。它也非常有趣:像任何长寿的数字格式一样(我有一个东西要(对于FAT文件系统而言),PDF本身就是一种历史文档。您可以看到几代工程师,在他们的时间里添加他们需要的东西,同时努力不破坏已经存在的任何东西。
[^ situation]:我不确定为什么Chrome甚至不愿意公开JSruntime。他们从Foxit那里获取了PDF阅读器代码,所以Foxit也许有一些特定的客户依赖JavaScriptform验证?
[^ es6]:Chrome也使用与浏览器相同的运行时,即使它没有公开任何浏览器API。据我所知,这意味着您可以使用双箭头功能和代理之类的ES6功能。
顺便说一句,很抱歉,碰撞检测不佳并且游戏速度不一致。 (不过,这不是重点!)从教程中摘录了大部分游戏。
我可以在Chrome的PDF API实现中找到的第一个用户可见的I / O点在Field.cpp中。
您不能在运行时设置文本字段的填充颜色,但是可以更改其边界矩形并设置其边框样式。您无法读取精确的鼠标位置,但是可以在创建PDF时在字段上设置鼠标输入和鼠标离开的脚本。而且您不能在运行时添加字段:您会陷入PDF增量时间的困境。[^ fortran]。我实际上很好奇他们为什么选择那些特殊的方法。
因此,PDF文件是由脚本生成的,该脚本会预先发出一堆文本字段,其中包括游戏元素:
但是我们在这里也做了一些修改,以使游戏正常运行。
首先,我们发出一条细的长条带。屏幕下半部分各列的文本字段。每当您在x轴上移动鼠标时,某个乐队都会获得一个进入鼠标的事件,因此,当您移动鼠标时,中断板也会移动。
其次,我们发出一个名为“整个”的字段。涵盖了整个屏幕的顶部。 Chrome浏览器不希望PDF的显示发生变化,因此,如果您在JS中移动字段,则会得到相当糟糕的工件。这'整个'当在帧渲染期间打开和关闭该字段时,field解决了该问题。该技巧似乎迫使Chrome清除了伪像。
同样,移动字段似乎会丢弃其外观流。您选择的花哨的任意PDF图形外观消失了,并且取而代之的是基本的填充边框矩形。因此,我的游戏对象通常依赖于更简单的外观特征字典。至少,在那里指定的填充颜色会随着小部件的移动而保持完整。
[^ fortran]:就像老式的FORTRAN中的某些编程刻板印象。 您必须预先声明所有变量,以便编译器可以静态分配它们。 pdfrw Python库在某种程度上完全是此类工作的正确抽象层。 许多库太高级了,只公开图形操作员。 自己生成PDF数据是可能的,但是有点麻烦,因为您需要正确获取数据结构格式和字节偏移量。