这是关于该主题的第二部分。如果你还没有阅读第一部分,请先阅读,以便理解“绕组规则”的问题。
快速回顾一下:HTML5 只支持 Non-Zero(非零)绕组规则,而 PDF 同时支持 Non-Zero 和 Even-Odd(奇偶)两种规则。
这意味着我们必须对使用了 Even-Odd 填充规则的图形做些处理,否则它们在 HTML5 中可能无法正确显示。
为了演示这个问题,我会用以下这个例子:
看上去只是个红色的圆?
我第一次看到的时候也是这么想的。直到我把填充改为描边模式,才意识到它实际上是什么。
为了帮助理解发生了什么,我们再来看一张图,加上箭头,显示路径的绘制方向。
我们首先想到的是,可能需要改变图形的绘制方式。最直接的想法就是改变路径的方向,也就是“转换”成 Non-Zero 绕组规则的等价图形。
交替使用顺时针和逆时针方向可行吗?
值得一试,看看会发生什么。反正只需要一分钟时间试一下。
结果证明,这其实挺头疼的。如果只是用 lineTo 画个方框还好说,但涉及贝塞尔曲线的话,情况就复杂得多。
如果你只是简单地反转绘制指令的顺序,就会得到像这样的问题:
控制点的顺序会影响曲线的绘制方式。那么,我们能否也反转控制点的顺序呢?
哦——这样也不太对。我们使用的是 bezierCurveTo,它的意思是:从当前位置(由上一个绘图命令决定)开始,通过两个控制点绘制一段曲线,终点是我们指定的位置。这就意味着我们还必须反向地重新组织控制点和终点的顺序,这样曲线才能按正确的方式被绘制出来。
做对了之后,我们就能反转路径方向,让图形按预期绘制出来:
那么问题解决了吗?没有。
对于我们的“禁止通行”标志来说,或许可以这样处理——我们可以手动控制路径的顺序和方向。但在现实世界中,这种做法不能适用于通用场景。我们不可能为每个具体的 PDF 文件都手动处理,我们需要的是一套普适的代码逻辑,让它能应对任何 PDF 文件。
这也就意味着交替改变路径方向并不能真正解决问题。比如看一下我们禁止通行标志在这种方式下的效果:
我的下一个想法是:用背景色填充被遮挡的区域。但这也行不通,因为路径的绘制顺序还是关键。即使我们把两个 “D” 字母填充为白色,当外面的圆形被填充时,它还是会把我们之前的工作覆盖掉。
而且如果两个形状只是部分重叠,那么只有重叠部分才需要“未填充”。更关键的是,这些“未填充”的部分是真正的透明区域,如果你用背景色填充,就会失去透明性,导致我们无法在后面放其他图层,比如显示“你不可以做这件事”的提示。
那怎么办?
如果不能改变绘制顺序,也不能改变路径方向,也不能通过填色伪装,我们还能怎么改造图形,让它在使用 Non-Zero 绕组规则时正常显示?
或许我们可以非常巧妙地将图形切割成许多小块,然后分析每一块该如何绘制,再分别填充这些小块。
像这样:
_____
| __|__
|__|__| |
|_____|
_____
| __|
|__|
__
|__|
__
__| |
|_____|
但这就太复杂了。对于我们的需求来说,有点大材小用了。而且,这种做法计算量大,会降低我们转换器的性能。更现实的是,我也没那么聪明,写不出来这样的算法。
其实从我开始研究这些形状开始,脑海里就一直有一个小声音在说:
“要不,我们干脆把这些图形输出成图片得了?”
确实,这样做是可行的,但并不理想。实际上,使用 Non-Zero 和 Even-Odd 绕组规则绘制图形,最终渲染结果真正不同的情况并不多。
所以,如果我们每次遇到 Even-Odd 绘制规则就输出成图片,那会产生大量没必要的图像文件。
这也不理想,因为矢量图形本身非常好用:缩放不会失真,从 PDF 转到 HTML5 非常快速,占用空间也远远小于图片。
那我们还能怎么办?
我们只能妥协。
最合理的做法就是:设定一套规则,只有当图形符合某些特定条件时,才把它输出为图片。
其余情况我们仍然尽可能用原始的路径绘制方式进行呈现。这样,在保持性能和显示效果之间取得一个平衡。
我们的主页:PDF 转 HTML5、Java 图像库、Java PDF SDK - IDRsolutions