用 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 的图像必须转换,
而转换会让颜色偏移。在制版之前发现要好得多。
你自己画的区域
Section titled “你自己画的区域”上面四种都随文件而来,第五种不是: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) 会返回
一个放宽或收紧的副本,用来留出容差。
接下来看哪里
Section titled “接下来看哪里”文档类型这一章列出了五者的每一个属性和方法,各带例子。它是参考 资料——需要某样东西时再去查,不必从头读到尾。