我正在尝试从 PDF 文件中提取矢量图形并创建相应的 SVG 文件。为此,我将 SVGOutputDev ( https://github.com/immateriel/pdf2svg/blob/master/SVGOutputDev.cc ) 与 xpdf 库一起使用。现在 SVGOutputDev 还没有实现剪辑路径提取,我正在尝试实现它。虽然我能够自己提取剪辑路径定义,但我无法确定这些定义中的哪些适用于正常笔划或填充区域。例如,请参考http://pastebin.com/jTdzv3YZ对于我从 PDF 页面中提取的 SVG,以及提取过程中看到的 PDF 图形命令序列的相应转储。从该 SVG 可以看出,有多个剪辑路径和一个矩形填充区域。即使在定义填充矩形之前定义了多个剪辑路径,也只有之前定义的圆形剪辑路径矩形定义应与矩形相关联(取决于 PDF 页面在各种 PDF 阅读器上的呈现方式,在白色背景中仅显示 2 个黑色填充的圆圈)。问题是如何知道哪些剪辑路径与 PDF 中定义的常规填充/描边区域相关联?仅供参考,我浏览了 PDF 规范文档的相关部分,但对我来说不是很清楚(“剪切路径操作可能出现在最后一个路径构造运算符之后和终止路径对象的路径绘制运算符之前。虽然剪切路径运算符出现在绘画运算符之前,它不会在它出现的位置改变剪切路径。相反,它会修改后续绘画运算符的效果“)。
1 回答
问题是如何知道哪些剪辑路径与 PDF 中定义的常规填充/描边区域相关联?
简而言之:在执行填充或描边操作时已定义的所有剪辑路径区域的交集适用,但在 Q(恢复状态)操作期间无效的区域除外。
因此,您对示例文件的分析
即使在定义填充矩形之前定义了多个剪辑路径,也只有在矩形定义之前定义的圆形剪辑路径才会与矩形相关联(取决于 PDF 页面在各种 PDF 阅读器上的呈现方式,这表明白色背景中只有 2 个黑色实心圆圈)
错误:不是最后一个剪辑区域,而是矩形定义之前所有剪辑区域的交集定义了当前剪辑区域。由于这些剪辑区域中的每一个都包含在前一个区域中,因此相交的结果确实由这两个圆圈组成。
在文档中:
图形状态应包含当前剪切路径,该路径限制受绘画操作符影响的页面区域。该路径的封闭子路径应定义可以绘制的区域。
初始剪切路径应包括整个页面。
[Clipping Path Operators] 通过与当前路径相交来修改当前的剪切路径,使用[非零绕组数规则/奇偶规则] 来确定哪些区域位于剪切路径内。
没有办法在不参考当前路径的情况下放大当前的剪切路径或设置新的剪切路径。然而,由于剪切路径是图形状态的一部分,它的效果可以通过在一对 q 和 Q 操作符之间封闭剪切路径的修改和那些对象的绘制来定位到特定的图形对象(见 8.4.2, “图形状态堆栈”)。执行 Q 运算符会导致剪切路径恢复为修改剪切路径之前由 q 运算符保存的值。
(当前 PDF 规范ISO 32000-1中的第 8.5.4 节)
实际操作:让我们看一下文档页面的内容流(其中有一个 Mediabox [0, 0, 595, 842]):
q
q
两次推动图形状态。
0 842 m
0 0 l
595 0 l
595 842 l
h
W
n
定义与整个媒体框等效的剪辑路径。
1 w
2 J
0 j
10 M
[]0 d
定义一般图形状态属性(线宽、线帽样式、线连接样式、斜接限制和虚线图案)。
q
再次推送图形状态,这次使用显式设置的剪辑路径和其他图形属性。
0 718.5 m
595 718.5 l
595 123.5 l
0 123.5 l
0 718.5 l
h
W
n
定义一个剪辑路径,其中包含一个与整个媒体框一样宽的矩形,但会切断 124 个用户空间单位高度的顶部和底部条纹。由于此剪辑路径完全包含在之前设置的剪辑路径中,因此此处的交集等于此剪辑路径。因此,当前有效的剪辑区域是这个较小的矩形。
0 718.5 m
595 718.5 l
595 123.5 l
0 123.5 l
0 718.5 l
h
W
n
定义与前一个相同的剪辑路径。因此,与它们相交不会改变任何事情。
148.75 668.92 m
93.98 668.92 49.58 624.52 49.58 569.75 c
49.58 514.98 93.98 470.58 148.75 470.58 c
203.52 470.58 247.92 514.98 247.92 569.75 c
247.92 624.52 203.52 668.92 148.75 668.92 c
h
347.08 470.58 m
292.32 470.58 247.92 426.18 247.92 371.42 c
247.92 316.65 292.32 272.25 347.08 272.25 c
401.85 272.25 446.25 316.65 446.25 371.42 c
446.25 426.18 401.85 470.58 347.08 470.58 c
h
W
n
定义由两个圆形子路径组成的剪辑路径。这两个圆圈不相交;因此我们不必处理“非零绕组数规则”和“奇偶规则”之间的差异。此外,圆圈包含在当前剪辑区域内。因此,新的剪辑区域由这两个圆圈组成。
0 0 0 rg
49.58 668.92 m
545.42 668.92 l
545.42 173.08 l
49.58 173.08 l
49.58 668.92 l
h
f
这将绘制一个包含当前剪切区域的填充黑色矩形。因此,整个剪辑区域(即两个圆圈)被涂成黑色。
Q
q
这会将图形状态恢复到最后推送的状态。即任何后续操作的剪辑路径是第一个包含整个媒体框的路径。再次推送此图形状态。
0 718.5 m
0 123.5 l
595 123.5 l
595 718.5 l
h
W
n
再次定义了在顶部和底部剪掉条形的剪切路径......
Q
q
...并立即通过恢复状态操作删除;状态再次被推动。
0 718.5 m
0 123.5 l
595 123.5 l
595 718.5 l
h
W
n
Q
q
又是一样的...
0 718.5 m
0 123.5 l
595 123.5 l
595 718.5 l
h
W
n
Q
q
... 然后再次。
0 842 m
0 0 l
595 0 l
595 842 l
h
W
n
这再次定义了环绕整个媒体框的剪切路径。由于无论如何这是当前的剪切路径,相交不会改变任何内容。
Q
Q
Q
以前推入堆栈的所有图形状态都将再次删除。