PDF 中字形的精确边界框?

Precise bounding box for glyphs in a PDF?

我正在尝试计算矢量 PDF 中每个文本字形的确切边界框。

这涉及跟踪 CTM、drawing/positioning PDF 指令等,而且还计算 "glyph space" 中每个特定字形的边界(使用嵌入的 GLYF 表中的信息字体)。

我知道 PDF FontDescriptor 包含一个用于每个嵌入字体的粗略边界框,但它是字体中所有字形的组合——即适合字体中所有字形的最小边界框。为了我的目的,我需要更精确的定位。

我的具体应用是从 sheet 音乐的矢量 PDF 中提取音乐语义。因此,一个很好的约束是我可以假设字形不是在同一个 Tj/TJ 运算符中绘制在一起的。每个字形都是独立绘制的。

另外,请注意,我将边界框定义为 "the smallest box that can contain all the drawn parts of the glyph." 没有必要忽略 ascenders/descenders/etc。在其他应用程序中可能被视为 "outside" 边界框。

这里有许多 个活动部件,我发现很难调试。所以这就是我喜欢的帮助:

  1. This example PDF I've created 有 10 个字形。在设备 space 中,这 10 个字形的 "ground truth" 边界框定位是什么?我当前的代码生成以下内容,但它是不正确的。我知道这是不正确的,因为它说第一个字形 ("&") 与第二个字形 ("\u02d9") 水平相交,当您在 PDF reader.[= 中查看 PDF 时,您可以看到这是不正确的35=]
'&'      ( 57.2799755477664, 600.7092061684704,  86.7452642315424, 677.1570718099680)
'\u02d9' ( 82.0030393188000, 633.6851606704608,  96.3090818379936, 644.6969866323168)
'\u0153' (144.7841941848000, 623.9630080194528, 158.6735558539200, 634.5581702962656)
'\u0153' (181.6778111184000, 619.0027260546528, 195.5671727875200, 629.5978883314656)
'w'      (226.1671727148000, 611.3638918288608, 245.0765465300448, 622.3161944071392)
'w'      (320.1063822180000, 631.2050196880608, 339.0157560332448, 642.1573222663392)
'\u0153' (414.0455917212000, 641.3239948962528, 427.9349533903200, 651.9191571730656)
'\u0153' (450.9392086548000, 636.3637129314528, 464.8285703239200, 646.9588752082656)
'\u0153' (487.9878407856000, 631.4034309666528, 501.8772024547200, 641.9985932434656)
'\u0153' (524.8814577192000, 628.9232899842528, 538.7708193883200, 639.5184522610656)
  1. 你是如何计算这些职位的? (考虑到 PDF 的复杂性,我意识到这有很多问题要问。)演练将是一个巨大的帮助,我相信它会在未来帮助其他人。

  2. 有现成的工具吗?

我认为获得真正准确信息的唯一方法是以给定点大小实际渲染字形并收集生成的位图的范围。

即使提取描述字形的路径也不会为您提供完全准确的信息,因为提示可以巧妙地(或在某些情况下,不那么微妙地)改变字形的呈现方式。在任何情况下,提取路径的工作量与渲染位图一样多,甚至更多......

PDF 中的字体大致分为三类:

  1. 带 PostScript 轮廓的字体
  2. 带有 TrueType 轮廓的字体
  3. 用户定义的字体。

您可以使用 FreeType 从具有 PostScript 和 TrueType 轮廓的字体中呈现字形(如果您愿意使用路径,也可以将其设为 return 路径)。

您必须将用户定义的(类型 3)字体视为一系列 PDF 操作,由文本矩阵缩放。所以你需要自己做。

请注意,字体可以通过两种方式组织,常规字体和 CIDFonts 以及获取对应于字符代码的字形数据的方法在两者之间有所不同,但我假设您已经准备好处理它您现有的代码。

在您的情况下,您的工作流程可能会限制您可能看到的字体种类,因此您可能不需要完整实施所有这些。例如,我看到您正在使用带有 TrueType 轮廓的 CIDFonts,但 CIDToGIDMap 是 /Identity,这缩小了问题的范围。

对于额外的复杂性,您需要考虑代表字形 'bounding box' 的是什么。您认为前进宽度和左侧轴承是边界框的一部分,还是只是标记为 ?

的区域

请记住,PDF 可以为字形指定与字体中定义的宽度不同的宽度,并且您的两种字体都包含 /W 修改字体中定义的宽度的数组。

如果您将左侧轴承和前进宽度视为字形的一部分,但有一个 /Widths 数组,其值小于前进宽度,则两个字形可能会出现 'collide',其实中间还有白色的space。 /Widths 所做的只是减少白色 space 的前进宽度,以便字形比正常情况下更靠近。

我使用 MuPDF 快速 bash 得到了答案:

<span bbox="39.21884 163.68216 42.53509 163.99687" font="PlantinMTStd-Regular" size="11.935925">
<char bbox="39.21884 163.68216 42.53509 163.99687" x="39.21884" y="163.99687" c=" "/>

<span bbox="57.200607 163.69899 73.08967 165.2394" font="OpusStd" size="19.841537">
<char bbox="57.200607 163.69899 73.08967 165.2394" x="57.200607" y="165.2394" c="&amp;"/>

<char bbox="82.003044 151.29828 90.63545 152.83868" x="82.003044" y="152.83868" c="&#x2d9;"/>

<char bbox="144.7842 161.21884 153.1744 162.75925" x="144.7842" y="162.75925" c="&#x153;"/>

<char bbox="181.67781 166.17912 190.06801 167.71953" x="181.67781" y="167.71953" c="&#x153;"/>

<char bbox="226.16718 173.61955 236.8826 175.15996" x="226.16718" y="175.15996" c="w"/>

<char bbox="320.10638 153.77843 330.8218 155.31883" x="320.10638" y="155.31883" c="w"/>

<char bbox="414.0456 143.85785 422.4358 145.39825" x="414.0456" y="145.39825" c="&#x153;"/>

<char bbox="450.9392 148.81815 459.3294 150.35855" x="450.9392" y="150.35855" c="&#x153;"/>

<char bbox="487.98785 153.77843 496.37805 155.31883" x="487.98785" y="155.31883" c="&#x153;"/>

<char bbox="524.8815 156.25856 533.27167 157.79897" x="524.8815" y="157.79897" c="&#x153;"/>

为了完整起见,这里是使用 txtwrite 设备和 -dTextFormat=0 来自 Ghostscript 的相同信息:

<page>
<span bbox="39 164 43 164" font="PlantinMTStd-Regular" size="11.9357">
<char bbox="39 164 39 164" c=" "/>
</span>
<span bbox="57 165 73 165" font="OpusStd" size="19.8411">
<char bbox="57 165 57 165" c="&amp;"/>
</span>
<span bbox="82 153 91 153" font="OpusStd" size="19.8411">
<char bbox="82 153 82 153" c="&#x2d9;"/>
</span>
<span bbox="145 163 153 163" font="OpusStd" size="19.8411">
<char bbox="145 163 145 163" c="&#x153;"/>
</span>
<span bbox="182 168 190 168" font="OpusStd" size="19.8411">
<char bbox="182 168 182 168" c="&#x153;"/>
</span>
<span bbox="226 175 237 175" font="OpusStd" size="19.8411">
<char bbox="226 175 226 175" c="w"/>
</span>
<span bbox="320 155 331 155" font="OpusStd" size="19.8411">
<char bbox="320 155 320 155" c="w"/>
</span>
<span bbox="414 145 422 145" font="OpusStd" size="19.8411">
<char bbox="414 145 414 145" c="&#x153;"/>
</span>
<span bbox="451 150 459 150" font="OpusStd" size="19.8411">
<char bbox="451 150 451 150" c="&#x153;"/>
</span>
<span bbox="488 155 496 155" font="OpusStd" size="19.8411">
<char bbox="488 155 488 155" c="&#x153;"/>
</span>
<span bbox="525 158 533 158" font="OpusStd" size="19.8411">
<char bbox="525 158 525 158" c="&#x153;"/>
</span>
</page>

不过看起来确实存在错误,urx 值在 char bbox 中不正确,但在 span bbox 中正确。

您可能还想查看此 Adob​​e GitHub 存储库:

afdko 子目录包含许多命令行工具,可用于测试、检查和转换字体文件。我使用了这个 repo 中的 tx 工具来打印一些关于用 mutool extract 从你的 PDF sample:

中提取的字体文件的信息
$ mutool extract pdf_example.pdf

 extracting font QNAAAA+PlantinMTStd-Regular-0013.ttf
 extracting font QSAAAA+OpusStd-0018.ttf

然后:

$  tx -mtx  QSAAAA+OpusStd-0018.ttf 

 tx: --- QSAAAA+OpusStd-0018.ttf
 tx: (ttr) cmap table missing
 ### glyph[tag] {gname,enc,width,{left,bottom,right,top}}
 glyph[0] {.notdef,-,0,{0,0,0,0}}
 glyph[1] {g1,-,1640,{4,-1313,1489,2540}}
 glyph[2] {g2,-,891,{0,-276,721,279}}
 glyph[3] {g3,-,866,{0,-266,700,268}}
 glyph[4] {g4,-,1106,{0,-276,953,276}}

也许这个,或者这个 repo 中的其他 28 个命令行工具之一,也可能对您有用...