13. 变更记录
每个版本改了什么,以及这些改动可能在你这边破坏什么。
版本仍处于 0.x,因此次版本号的提升允许带来破坏。一旦发生,条目会写清楚破坏
了什么、如何适配。这里不会有悄悄的改动。
0.19.1
Section titled “0.19.1”- 一个脚本可以让进程异常终止,而不是被拒绝。 源码里的嵌套变成了调用栈上的
嵌套:大约两千个括号就撑爆栈,运行以 SIGABRT 死掉——没有退出码 3,没有消息,
也无从捕获。六种写法都能做到:
((((…))))、!!!!…、----…、嵌套数组、嵌套 代码块,以及"#{"#{…}"}"。解析器现在会计数深度,超过 128 层就拒绝——远多于 任何人手写的脚本,也远低于栈耗尽的位置。最要命的是:这正是你拿到别人的脚本时 第一个会运行的命令——pdfl lint不是报告,而是直接死了。 - 一个包可以指定安装目录之外的文件。 检查拒绝了
..和开头的/,却放过了C:/Windows/evil.dll和\\server\share\x——两者都不含前面那些,而在 Windows 上它们是绝对路径,与安装目录拼接时会把目录本身丢掉。现在每个路径的每个 组成部分都必须是普通名字,判定规则不随解包的平台而变。 - 一个包可以在读取过程中耗尽内存。 每个条目在查阅清单之前就被整个读进内存 ——包括清单从未提到的条目——于是 299 KB 的压缩零字节占用 380 MB,再大一点就 把机器拿走了。解包现在到 64 MB 为止,远高于任何由脚本和查找表组成的真实包。
--pages 1-20000000在文档还没打开时就分配了页码——用掉 214 MB,然后什么也 没说。超过一百万页的范围会被拒绝。
- 上面的修复在原本没有上限的地方设了上限,因此少数以前能通过的东西现在不行了:
嵌套超过 128 层的脚本、解包超过 64 MB 的包、文件名里带
:或\的包,以及 超过一百万页的页码范围。每一项都远超真实脚本、包或文档的实际内容——但它们以前 都不会失败,所以写下来,而不是留给你去发现。
0.19.0
Section titled “0.19.0”查看器的模式被移除了。 Flip、Fade、Blend 滑块和 Differences
滑块,以及驱动它们的 space 和 d 键,都不再存在。
受影响的只有生成的查看器——报告、退出码和选项都没有变化,因此任何读取
pdfl输出的东西都察觉不到。三个面板已经回答了那些模式要回答的问题:两个文件同时在 屏幕上,而不是轮流出现。如果你自己写过提到某个模式或某个键的说明,需要过一遍。
pixelcompare的查看器改成三个面板并排——原件、新件,以及两者叠加并把差异 着色——不再是一个面板加一组模式。第三个面板擦除时,左右两个参照保持不动,于是 你能看清擦除是在哪两者之间切换。- 每个面板都带着同样的一对线——一竖一横——位置相同,六条一起移动。它们跟着指针 走:没有要抓的东西,也没有要瞄的目标,交点就是鼠标所在的位置。在差异面板里 两条线都是切口,新件出现在竖线右边、横线下边,两线相交处就是被揭开区域的那个 角;在另外两个面板里,它们是落在页面同一列、同一行上的尺。横线从最上方开始, 所以在指针进入面板之前,竖线仍是一条通高的普通擦除。
- 滚轮让三个面板一起缩放,最多 8 倍,围绕指针下的那一点;缩小时到整页适配为止。 按住任意一个鼠标键拖动可以移动页面本身,三个面板一起动,且无论缩放多少都与 指针同速。任何缩放下线条都保持粗细。Reset view 把缩放、平移和线条送回 起始状态,在没有东西可还原时禁用;换页也会这样,因为缩放属于设定它的那一页。
- 面板按窗口定尺寸:任何窗口形状下,整个比较都不用滚动就在屏幕上,并且各自 保持页面的比例。若两个文件对某页的尺寸各执一词,每一页都完整放进共同的框里, 而不是被拉伸。
- 它一打开就停在有差异的页面上,All 会把其余的放回来。在长文档里,那些 页面正是打开它的原因。
0.18.0
Section titled “0.18.0”loading 所见的标识符会变。 它由消息推导而来,而消息不再带 pdfium 放进去的
那些换行。
只有读不了的文档产生的所见会受影响——你自己的 check 不会产生这种。如果某条 基线批准了这样的标识符,需要重新记录一次。这也是本次发布是次版本而不是补丁的 原因:标识符是基线的立足点,它不该悄悄改变。
pdfl pack会用打包那台机器的分隔符记录嵌套文件——在 Windows 上是data\batches.csv。包在一台机器上打好、在另一台上安装,所以除/之外的写法 都会得到一个能装上、却找不到自己文件的包;我们自己的校验也找不到。在 Linux 和 macOS 上打的包从未受影响。- PDF 读不了时,
--output和--output-file会被忽略。无论要求什么格式,run、fix和compare都往 stdout 打印 JSON——于是一条要求把 JUnit 写进 文件的流水线,既没拿到文件,报告又流进了它没在读的流里:明明退出码正确地给出10,损坏的输入看上去却像这次运行从未发生。失败报告现在和其他报告走同一条路。 - pdfium 的错误以跨三行排印的 Rust enum 形式进入报告。一条诊断是 CSV 行里的一个 字段、XML 里的一个属性,所以现在它被折成一行。
0.17.0
Section titled “0.17.0”-
pdfl pixelcompare为每个阶段显示进度条——光栅化两个文件、比较、写出查看器。 只有当 stderr 是终端时才绘制,因为进度条靠覆盖自己那一行来工作,而日志文件没有 可移动的光标;被重定向时它保持安静。--quiet在任何情况下都会让它闭嘴。 -
pdfl pixelcompare的--jobs <n>同时比较这么多页,默认每个 CPU 一个:150 dpi 的 41 页从 3.6 秒降到 1.2 秒。只有比较是并行的——pdfium 把每一次调用都串行在一把 全局锁后面,所以光栅化并行不了,这也是提速三倍而不是八倍的原因。取值不影响报告: 页面按页码顺序折回,因此诊断、它们的顺序和指纹都完全一致,查看器写出的文件也一样。这里默认每个 CPU 一个,而
test和watch默认--jobs 1。那边一个任务是 一个子进程,各自抱着一份文档;这边页面已经在内存里,线程共享它们。
0.16.0
Section titled “0.16.0”pdfl pixelcompare按外观而不是文字逐页比较两个 PDF,并报告不同像素的占比。 只是发生位移的页面会先对齐再比较,这样一个像素的偏移不会淹没真正要紧的改动。pixelcompare的--viewer <文件夹>会写出一个自包含的应用——不用 CDN、不用 打包器、不用服务器——可以在两个文件之间擦除、翻转或淡入淡出,差异就地着色: 红色表示消失的墨,绿色表示新增的墨,蓝色表示分量相同而颜色不同。页码条可以 筛成 Changed only,方向键也跟随筛选——在长文档里,翻过那些没动过的页面 才是真正费时的部分。
0.15.0
Section titled “0.15.0”if/else if/else,作为表达式:它的值是所走分支的最后一个表达式, 与函数已经遵循的规则相同。因此它既能当值用 (const LIMIT = if coated { 300 } else { 260 }),也能当作语句的守卫,不需要 第二套语法。没有走到的分支返回null,且每个分支有自己的作用域——给分支外 已经存在的变量赋值,仍然更新的是外面那个。
0.14.0
Section titled “0.14.0”--var现在也能到达pdfl test和pdfl watch,不只是pdfl run。此前两者 都没有把它转给自己派生的子进程,因此读取vars.*的脚本根本无法被测试或监视—— 不论内容是什么,每个用例或文件都会以“was not provided”失败。
0.13.0
Section titled “0.13.0”pdfl pack不再打包电子表格(.xlsx、.xls、.ods),并会说明留下了 哪个文件。没有任何data::函数能打开它们,带着它的软件包安装顺利,却在第一次 查询时失败。若你此前打包了电子表格,请先导出为.csv或.json。
run的--tags TAG,筛选运行哪些 check。可重复;check 只要带有其中任一 标签就会运行。inspect与lint的--json,以及doc的--output json。所有子命令 都可被程序读取了。--output sarif与--output junit,凡是可以选择报告格式的地方都支持——run、compare、watch和fix。SARIF 是 GitHub code scanning 读的格式,JUnit 是 任何 CI 的测试面板读的格式。pdfl completions <shell>输出 bash、zsh、fish、elvish 或 powershell 的补全 脚本。- 每个命令都可用的
--quiet,关闭 stderr 上的进度与确认信息。错误照常显示,print()也不受影响——那是脚本自己的输出,吞掉它就改变了脚本的行为。 data::load_dataset与data::lookup_value除.csv外也读.json:数组的 数组,或对象的数组——由第一个对象按文件中的书写顺序确定列名。pdfl test <script>用脚本跑一整个文件夹的 PDF,并把每份报告与记录在旁边的那份 比对:配置文件一旦开始查出不一样的东西,失败的是测试,而不是下游某个人的一天。--update用于记录预期报告。pdfl test的--jobs <n>同时运行这么多用例,每个都是独立进程。8 个 41 页的 文件:--jobs 1用 8.9 秒,--jobs 8用 1.1 秒。默认仍是1,因为每个任务都 把一份文档放在内存里;--jobs 0表示每个 CPU 一个。pdfl watch也有--jobs <n>:文件由子进程校验,因此批量的一轮同样可以扩展 (8 个 41 页的文件从 9.5 秒到 1.2 秒)。写出的报告与--jobs取值无关。pdfl watch的--events:等待文件系统通知,而不是用定时器。这是可选项而非 默认值——在 NFS 或 SMB 挂载上,inotify 只报告本机写入的内容,网络热文件夹会 就此失声。如果监视器建不起来,watch 会说明并退回定时器。pdfl watch的--journal <文件>:只追加的已校验记录,每个文件一行 JSON。用 同一个 journal 再跑会跳过它覆盖的文件——在五千个里跑到四千个被打断的批处理, 只需再跑剩下的一千个——但仍然报告它们的结论,所以续跑的批处理绝不会声称文件夹 是干净的。pdfl watch的--timeout <秒>:超过这个秒数就杀掉某个文件的分析,并报告为 被拒——一条发现,check_name: "timeout"——而不是让批处理卡住。.pdfl脚本的 递归已经有深度限制,所以这个选项是为脚本造不成的情况而设的:pdfium 在畸形或 恶意的 PDF 上死循环或卡住。
- 没有任何 check 携带的标签会报错,而不是空通过。否则标签拼错的流水线将 什么都不校验,却报告文件干净。
rule不带标签,因此--tags会跳过它——这与不带标签的 check 是同一个答案。- JSON 报告新增
checks_run,即运行过的 check 与 rule。它不提升schema_version,因为忽略未知字段的读取方不会因此出错。JUnit 需要它:诊断只 会点到有所发现的 check,而一次干净的运行若报告为零个测试,CI 会认为这次运行 从未发生。
pdfl watch现在在最新的那个文件稳定下来时就醒来,而不是最多再等一整个轮询 间隔。在--debounce 3000下,一个到达的文件约 3 秒后被报告,而不是最多 6 秒。
0.12.0
Section titled “0.12.0”- 脚本可以从命令行接收值:
--var 名称=值,以vars.名称读取。未传入的值会 指出应当提供它的那个参数,而不是解析为空。 - 四个用
visual::比较两份文档的完整示例:proof.pdfl、reprint.pdfl、scope.pdfl和intake.pdfl。
无。从不提及 vars 的脚本行为与此前完全一致。
0.11.0
Section titled “0.11.0”诊断标识符的形态变了。 原先是 PDFL-001,即本次运行内的计数;现在由发现
本身推导而来,例如 PDFL-093751a2。
凡是按
PDFL-\d+匹配的都不再匹配。作为交换,在某个发现之上插入 check 后, 它的标识符不再改变——这正是“已批准基线”得以维持的前提。
无法读取的输入以 10 退出,而不是 2。 此前损坏的文件与被判不合格的文件
对流水线来说无从区分。
如果你的 CI 把
2当作“这个文件被拒绝”,那么在文件根本未被判定时它现在会 看到10。发现仍使用0、1、2;脚本语法错误仍是3。
- check 可以声明自身发现的严重级别:
check "..." severity: warning { ... }—error(默认)、warning、info。 正是它让--fail-on warning有了作用对象。 - JSON 报告以
schema_version开头,消费方由此知道自己在读哪种形态。它仅在 读取旧输出的一方会被破坏时才提升;新增字段不会提升它。
0.10.1
Section titled “0.10.1”- PDF 报告有一部分是葡萄牙语:小节标题写作
Diagnósticos,每条诊断都带(linha N)。二者现已改为英文,这本就是文档一直承诺的。
0.10.0
Section titled “0.10.0”发布目标由 x64 改名为 amd64,因此所有资产名称都变了。
不再发布可移植归档,仅保留 Linux amd64 的那一个。
凡是下载
pdfl-<版本>-linux-x64.tar.gz,或使用 Linux amd64 之外任何可移植 归档的,都需要修改。在 CI 中请从.deb安装——本文档中的范例已相应更新—— 或者在无法安装的场合使用 Linux amd64 的 tarball。
- 将文档与源码逐一比对后发现的两处缺漏:
text::detect_personal_data与text::detect_pii接受一个未被记录的可选字符串参数;fix::reorder_pages在不同语言的文档里写法不一致。
- 各平台的安装包:Linux 的
.deb、macOS 的.dmg、Windows 的-setup.exe与.msi。 - macOS Intel 构建,自 Apple Silicon 运行器交叉编译。
- Windows 安装包构建时路径解析到了错误的目录,因而从未生成过文件。
- 发布包里带着 pdfium 的 C 头文件和构建文件,这些只与针对 pdfium 编译的人有关。 每个包约 550 KB。
- Windows x64 加入发布平台。
随包的
pdfium.dll位于pdfium\bin,而非pdfium\lib。如果你自行打包pdfl,请保持发布时的布局:可执行文件在自身旁边寻找该库。
发布资产的名称中带上了版本号,形如 pdfl-<版本>-<目标>.tar.gz,内部目录
同样如此。
.../releases/latest/download/<名称>不再解析,因为该端点需要精确的文件名。 请改用模式下载:gh release download --pattern 'pdfl-*-linux-amd64.*'。
- 代码、README 和示例改为英文。文档仍保持七种语言。
v0.6.1
Section titled “v0.6.1”首个公开版本。语言、解释器和十个 CLI 命令,附七种语言的文档。