跳转到内容

用 PDFLang 拆开一个 PDF

在屏幕上,PDF 看起来是平的一整块,像一张页面的照片。其实不是。它是一盒零件,而人们 关于一个文件提出的大部分问题——这个能印好吗?有没有漏掉什么?——本质上都是关于其中 某个零件的问题。

PDFLang 给你五种伸手进去的方式。前四种在脚本开始时就已经在那里,第五种由你自己画。

每个脚本都从一个叫 doc 的变量开始。不用打开,也不用加载:doc 就是正在被检查的 那个文件。

check "我在看什么" {
print("文件:", doc.filename)
print("页数:", doc.page_count)
print("标题:", doc.title)
}

doc.title 和 doc.author 来自文件的元数据——PDF 关于它自己的信息,你在页面上永远 看不到。它常常是空的,因为导出文件的人从来没填过。这本身就值得检查:

check "文件说明了自己是什么" {
assert doc.title != "", "这个 PDF 的元数据里没有标题"
}

doc 还带着三个列表:doc.pages、doc.fonts 和 doc.images。它们就是普通列表, 对列表能做的事对它们都能做。

doc.pages 把页面一张一张交给你。每一页都知道自己的尺寸、承载了多少油墨,以及哪些 页面框有定义。

尺寸的单位是点,这是 PDF 思考时用的单位:1 英寸 72 点,所以 A4 是 595 × 842。不过你 不必用点来思考——写 210mm 或 8.5in,比较照样成立:

check "全部是 A4" {
doc.pages.each { |page|
assert page.width > 200mm,
"第 #{page.number} 页太窄了: #{page.width}pt"
}
}

注意 page.number。页码从 1 开始,跟人说话时一样,所以失败信息里出现的数字,就是 一个人会去翻的那一页。

然后是那些框。一张可付印的页面带着若干矩形,说明该怎么处理它:TrimBox 是裁刀 落下的位置,BleedBox 是稍大一圈的区域,颜色必须越过裁切线延伸出去,这样偏移 一毫米也不会留出白边。缺了它们的文件,在屏幕上可以完美无缺,到了印厂却没法用:

check "可以上裁切机了" {
doc.pages.each { |page|
assert page.has_trim_box, "第 #{page.number} 页没有 TrimBox"
assert page.has_bleed_box, "第 #{page.number} 页没有 BleedBox"
}
}

页面还会报告 page.tac:这一页油墨最重的地方落了多少墨,用百分比表示。四种油墨都 满格就是 400%,没有哪台印刷机吃得下;300% 是常见的上限。

doc.fonts 列出文件用到的字体,而每一种字体都回答一个重要的问题:它内嵌了吗?

内嵌的字体跟着 PDF 一起走。没有内嵌的字体只留下一个名字,打开文件的机器就用手头 有的字体去替代。文字不会消失——它会变形。断行的位置变了,原本一行放得下的标题变成 两行,而在印出来之前没人会注意到。

check "每种字体都跟着文件走" {
doc.fonts.each { |font|
assert font.is_embedded, "字体 '#{font.name}' 没有内嵌"
}
}

doc.images 给出文件里所有的图像,page.images 给出某一页上的。值得弄懂的属性是 dpi。

DPI 是每英寸的点数——印出来的每英寸里有多少细节。PDFLang 不会去报告图像元数据里存 的那个数字。它报告的是有效分辨率:图像实际拥有的像素,除以它被印出来的尺寸。 把一张 1000 像素的照片拉到 20 厘米宽,它就是一张低分辨率图像,不管它自称是什么。 决定印刷是否清晰的,正是这个数字。

check "清晰到可以付印" {
doc.images.each { |img|
assert img.dpi >= 300,
"第 #{img.page_number} 页的图像: #{img.dpi} DPI,需要 300"
}
}

另一个常用的是 img.color_space。胶印在 CMYK 下工作;还停留在 RGB 的图像必须转换, 而转换会让颜色偏移。在制版之前发现要好得多。

上面四种都随文件而来,第五种不是:region 是你自己定义的矩形,好让你针对页面的 某一部分提问,而不是整页。

footer = region(0, 0, 595, 60, "页脚")
band = region(20mm, 250mm, 60mm, 15mm, "警示带")

只有一件事要记住:原点在哪里。在 PDF 里,0, 0 是页面的左下角,不是左上角。 Y 向上增长。

正是 region 让你能写出对一份活件真正重要的规则。不是「这个文件里有没有那段法定 文字」,而是「那段法定文字在不在为它预留的带子里」:

check "警示带" {
band = region(0, 700, 595, 142, "带")
content = text::extract_from_region(1, band)
assert content.contains("凭处方销售"),
"带子里缺少强制性文字"
}

region 之间也可以比较。footer.intersects(header) 告诉你两个区域是否重叠,这就是 抓出闯进别人地盘的元素的办法。footer.expand(5mm) 和 footer.inset(3mm) 会返回 一个放宽或收紧的副本,用来留出容差。

文档类型这一章列出了五者的每一个属性和方法,各带例子。它是参考 资料——需要某样东西时再去查,不必从头读到尾。

如果想看这五者在真实活件里一起工作,实用范例和 示例就是地方。

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