PDF标准详解(四)——图形操作符
上一节,我们了解了PDF中cm操作符,它是定义变换矩阵的。同时也了解到re是创建一个矩阵的。上一节也说过,它用来构建一个路径,具体什么是路径,路径有什么作用呢?这些将在本节给出解释
图形操作符是用来在pdf中构建内容并输出到相关设备上进行显示的。pdf中我们能看到的内容几乎都是由图形操作构成的。PDF中主要有6中图形操作符:
- 图形状态操作符(Graphics state operator):CTM当前变换矩阵、 current color、 current clipping path。
- 路径构造操作符(Path construction operators):线的轨迹,,各种图形。
- 绘制路径操作符(Path-painting operators):填充, 描边, 或定义一个剪切区域。
- 其他绘图操作符(自我描述图形对象): 图像(image),shading。
- 文本操作符(Text operator):从字体(代表文本字符的字面/版式(TYPE-FACES)的描述)中选择,显示字符字形字符操作,例如前面显示hello,world 用到的Tj 操作符
- 标记内容操作符(Marked-content Operator): Layers
这一次我们主要介绍前两个,后面的等后续慢慢介绍
路径
路径构建操作符
路径对象主要由直线、矩形框(re)、3次贝塞尔曲线构成。
对于直线来说,我们需要先使用m(moveto) 来将画笔移动到指定位置,然后使用l(lineto) 来表示将画笔移动到某一个点。例如有下面的例子
3 0 obj % 页面内容流
<< >>
stream % 流的开始
400 400 m 100 100 l s
endstream % 流结束
endobj
这里我们定义了一个从 (400, 400) 到(100, 100) 的直线。在画直线的时候,m只能有一个,作为起点,而l可以有多个,每有一个l都表示从画笔的上一个点画一条直线到新的位置。例如我们可以模拟一个画一个矩形
3 0 obj % 页面内容流
<< >>
stream % 流的开始
400 400 m 100 100 l s
100 100 m 300 100 l 300 300 l 100 300 l 100 100 l S
endstream % 流结束
endobj
矩形的例子比较简单,这里就不给出了。我们只需要指定起点坐标并且加上长宽最后用re 操作符作为结束符即可构建
对于贝塞尔曲线来说,我们需要4个点来画出一条曲线,它们的位置如下图所示
我们需要一个起始和结束位置的点,并且加上两个控制点共同组成一条贝塞尔曲线。
贝塞尔曲线我们使用c来作为操作符,在构建的时候需要使用m来规定起始位置的坐标,然后再跟上上图p1, p2, p3 的坐标来控制曲线。例如下面的例子
3 0 obj % 页面内容流
<< >>
stream % 流的开始
100 100 m 200 300 300 400 400 200 c S
endstream % 流结束
endobj
这样我们构建了一条如下图所示的曲线
我们对上面出现的操作符做一个总结
操作符 | 含义 |
---|---|
m | 设置点的起始位置(moveto) |
l | 从当前位置构建一条直线到对应位置 (lineto) |
rg | 构建矩形路径 |
c | 构建贝塞尔曲线 |
路径显示操作符
上述操作符只能构建一个路径,而这个路径究竟该如何显示,用作何种用途,需要另外给出操作,如果仅仅构建路径,那么页面上是不会有任何显示的,例如上述的内容流,我们稍微做一下更改,去掉最后的S
操作符,我们可以发现之前显示的内容现在不显示了
3 0 obj % 页面内容流
<< >>
stream % 流的开始
100 100 m 200 300 300 400 400 200 c % 只构建路径,而不对路径做任何操作,页面不会有路径的内容
endstream % 流结束
endobj
想要显示路径,我们需要使用 S
操作符。上面的路径,我们在最后加上S
就能显示出图形了。
另外我们可以使用h操作符来构建一个闭合的路径,它是在原来图形的基础之上,使用一条直线将起始点到终点的两个点连接起来构成要给封闭的区间。例如上面使用直线画矩形的例子,我们可以删掉最后一个l
操作符,并使用h
闭合,照样能形成矩形
3 0 obj % 页面内容流
<< >>
stream % 流的开始
400 400 m 100 100 l s
100 100 m 300 100 l 300 300 l 100 300 l h S
endstream % 流结束
endobj
去掉h
我们将得到一个开口的矩形。这个读者可以自行尝试,这里就不给出结果了。
对于上面的贝塞尔曲线的例子
3 0 obj % 页面内容流
<< >>
stream % 流的开始
100 100 m 200 300 300 400 400 200 c h S
endstream % 流结束
endobj
加上h
之后将得到下面的结果
描边与填充操作
这里我们采用S对路径勾画出了边框,也就是描边路径,它对应的英文单词是stroke,我们也可以使用f 或者F(fill)来对路径构成的封闭区间进行填充。默认采用黑色进行填充。
3 0 obj % 页面内容流
<< >>
stream % 流的开始
100 100 m 200 300 300 400 400 200 c h f
endstream % 流结束
endobj
当然也可以提前指定画刷颜色,这个我们在后面介绍颜色空间的时候再介绍如何定义画刷和画笔。
另外也可以使用b或者B(both) 来同时进行描边和填充操作。
非0缠绕规则和奇偶绕组规则
上述图形,我们很明确的仅定义了一个简单的区域,当出现重叠的复杂区域时,该如何进行填充呢?这里有两套不同的填充规则,即非0缠绕规则和奇偶绕组规则。
3 0 obj % 页面内容流
<< >>
stream % 流的开始
100 350 200 200 re %生成矩形左上角坐标 (100, 350) 宽高都是200
120 370 160 160 re f %按照非0缠绕规则
400 350 200 200 re %生成矩形左上角坐标 (400, 350) 宽高都是200
420 370 160 160 re f* %按照奇偶缠绕规则
endstream % 流结束
endobj
这里显示的效果如下
我们在这里定义了两组矩形,每组有两个矩形路径进行了重叠。第一组采用非0缠绕规则,第二组采用奇偶规则来填充。
我们先以这两个图形为例,来说明这两个规则
非0规则:
- 初始化环绕数到 0 。
- 从图形中的任意一点 P 向外任意引一条射线。
- 每遇到一条与该线的交叉线,如果射线与路劲的顺时针相交则计数加一,否则计数减一
- 假如环绕数不等于 0 ,则点 P 在多边形内。
- 但是这个方法有局限性 , 不适合相交 , 或者选一条正切的射线 . 因为射线的方向是任意的 , 这个规则简单的选用射线并不碰到这些情况
例如上面我们定义了两个矩形,这两个矩形划分出了两个区域,也就是图中A和B所在区域。我们从A区域随意一点往外引一条射线。从图上看,射线与两条路劲相交,并且都是顺时针相交,所以这里的技术是2。同理,B点与一条顺时针路径路径计数是1。这两个区域的计数都不是0,所以他们都需要进行填充,因此它显示的是上图左侧的效果
奇偶规则:
- 从区域内某一点向外引一条射线。
- 简单计算与该射线相交线的数量。
- 如果这个数是奇数,则认为点在图形内。
根据这个规则,我们看到A点的计数是2,是偶数,Bdian的计数是1,是奇数。按照奇偶规则,B点需要填充,而A点不需要进行填充。所以它显示的是上图右侧的效果
我们再看一个例子
3 0 obj % 页面内容流
<< >>
stream % 流的开始
150 50 m 150 250 l 250 50 l 50 150 l 350 150 l h f
550 50 m 550 250 l 650 50 l 450 150 l 750 150 l h f*
endstream % 流结束
endobj
这里我们画了两个五角星,线条的顺序按照m给出的为起点,每一个l代表笔画移动的一个端点,根据上述给出的值我们可以得到对应路径的环绕方向,具体的分析过程这里就不展开了,有兴趣的小伙伴可以自己尝试着画图分析一下,然后使用pdf阅读软件打开看看效果与预估的是否一样
定义裁剪区域
我们利用一些操作符来定义一个路径,这些路径可以作为图形显示出来,也可以作为一个裁剪区域,在该区域中的内容显示出来,不在该区域的内容则丢弃。
对于给出的路径,我们使用W (非0缠绕)或者 W*(奇偶规则)来定义一个裁剪区域。例如下面有一个例子
3 0 obj % 页面内容流
<< >>
stream % 流的开始
100 100 200 200 re h
W %将上述路径设置为裁剪路径
150 150 m 200 200 l S %在裁剪路径中,所以会显示
0 0 m 500 800 l S %只显示裁剪路径中的内容
endstream % 流结束
endobj
这里我们定义了一个长宽都为200的矩形,并且使用h
将矩形区域封闭,然后使用W来将矩形内部作为裁剪区域,然后在(150, 150) 的位置画一条直线到 (200, 200) 的位置。这两个点都在矩形内部,所以会显示出来,另外再画一条从 (0, 0) 到 (500, 800) 的线条,因为这条线有一部分在裁剪区域外,一部分在裁剪区域内,所以只会显示一部分线条。最终图形呈现的效果如下
总结
本文主要介绍PDF中基本的图形操作符。一般构建图形的操作符有3中
- 使用
m
定义画笔的起始位置,然后使用l
来画一条直线 - 或者直接使用
re
操作符来绘制一个矩形 - 还可以使用
c
来构建贝塞尔曲线
对于构建的路径,可以使用 h
来进行画笔起始位置和终点位置的连线,这个连线一般是一条直线。对于这个路径我们可以使用 S
(stroke) 来对路劲进行描边显示或者使用 f(非0缠绕) f*(奇偶规则)进行内容的填充,又或者使用 b(B) 来描边和填充。
我们可以使用 W (非0缠绕)或者 W*(奇偶规则) 来将路径作为一个裁剪区域