跳转到内容

PDFLang 是一门什么样的语言

PDFLang 是一门只做一件事的脚本语言:把「正确的 PDF 应该是什么样」写下来,让机器 拿文件去比对。

脚本的扩展名是 .pdfl,执行它的是 pdfl,一个用 Rust 写的命令行工具。

总得有人判断一个 PDF 能不能付印、能不能归档、能不能发给客户。

这个判断背后的规则是具体的。油墨总量低于 300%。字体全部内嵌。每一页都有裁切框。 条码与旁边印着的号码一致。没有哪一页被误旋转。

这些规则通常同时存在于三个地方:做这行最久的那个人的脑子里、一份没人打开的检查 清单里,以及一个只有作者能维护的脚本里。然后那个人离职了,清单过时了,脚本变得 没人敢碰。

PDFLang 的存在,是为了让规则集中在一个文件里,而且懂这些规则的人自己就能读懂。

profile "press-ready" {
check "油墨量在限值内" tags: ["prepress"] {
require prepress::validate_tac_limits(300)
}
check "字体已内嵌" {
doc.fonts.each { |font|
assert font.is_embedded, "字体 #{font.name} 没有内嵌"
}
}
}

四个词撑起了这门语言的大部分:

  • profile 把一组 check 归拢起来并命名。
  • check 是一条规则,会成为报告中的一节。
  • require 在前置条件不成立时终止该 check。
  • assert 记录一条结果,带上你写的信息。

没有类、没有继承、没有类型声明、没有分号。这是决定,不是遗漏:它设定的读者懂印前, 不懂编程。

同样的意图体现在更小的地方。页码从 1 开始,就像编译器之外的所有人那样数。尺寸可以 直接写 3mm 和 1in。百分比就写 300%。

七个命名空间,每个对应一类工作:

  • text:: — 提取、规范化、检索、各类校验、个人信息
  • struct:: — 对象、元数据、XMP、安全、哈希
  • visual:: — 分辨率、视觉比对、感知哈希、质量
  • prepress:: — 油墨量、分色、专色、字体、各类页面框
  • codes:: — 条码与二维码的检测、解码、校验
  • fix:: — 页面框、页面、水印、合并与拆分、优化
  • data:: — 来自文件之外的术语表、数据集和查找表

一份报告。删减之后,一条失败的 check 是这样的:

{
"script_name": "prepress.pdfl",
"input_file": "magazine.pdf",
"status": "FAIL",
"error_count": 2,
"diagnostics": [
{
"id": "PDFL-093751a2",
"severity": "error",
"check_name": "Ink coverage",
"message": "page 7: 324% ink (limit 300%)"
}
]
}

留意那个 message。它不是堆栈跟踪,而是一句可以直接转发给送来文件的供应商的话 ——这正是这门语言要你亲手把它写在规则旁边的原因。

默认是 JSON。CSV、HTML 和 PDF 是给需要阅读报告而不是解析报告的人用的;当结果需要 出现在团队本来就会看的地方时,还有 SARIF 和 JUnit。

Terminal window
pdfl run press-ready.pdfl magazine.pdf > report.json

退出码承载了结论,因此 Shell 脚本或 CI 任务无需解析任何内容即可据此分支:

退出码 含义
0 全部通过
1 仅有警告
2 校验错误
3 脚本语法错误
10 文档根本读不了

pdfl 自己打印的信息——诊断、错误、命令行帮助——是英文的,这是命令行工具的惯例。 本文档翻译成七种语言,但一条失败的 check 仍会报出 page 7: 324% ink (limit 300%)。

你自己脚本里的信息属于你。用读报告的人所说的语言去写。

语言手册是唯一值得从头读到尾的一章。它之后的内容都是参考: 每个命名空间一章、CLI 命令,以及围绕完整案例组织的 实用范例——印刷厂、法律出版社、实验室、CI 流水线。

如果你更愿意读代码而不是读文字,示例是保留了注释的可运行配置。

DigitalOcean感谢 DigitalOcean 为本站提供托管服务。