在PostScript内部计算任意PostScript代码的边界框
单次PostScript调用内获取内容边界框并实现居中/缩放
核心思路
要在单次PostScript解释器调用中完成「预跑代码收集边界框→计算变换→实际绘图」的流程,关键在于重定义绘图相关操作符,在不实际输出的前提下,收集所有路径(包括文本路径)的边界框,合并得到内容的全局边界框,再基于页面尺寸计算缩放和平移参数,最后应用变换后执行实际绘图。
由于目标代码可能包含多条独立路径或文本,直接使用pathbbox只能获取当前路径的边界,因此需要通过拦截stroke、fill、show等操作,每次执行前先提取当前路径/文本的边界并更新全局边界框。
完整实现代码
% -------------------------- % 1. 初始化全局边界框变量(初始值设为极值) % -------------------------- /bbox [1e38 1e38 -1e38 -1e38] def % -------------------------- % 2. 定义更新全局边界框的过程 % 接收pathbbox返回的四个值:minx miny maxx maxy,与当前全局bbox比较并更新 % -------------------------- /updatebbox { pathbbox % 更新最小x 0 get bbox 0 get lt { exch bbox 0 exch put } if % 更新最小y 1 get bbox 1 get lt { exch bbox 1 exch put } if % 更新最大x 2 get bbox 2 get gt { exch bbox 2 exch put } if % 更新最大y 3 get bbox 3 get gt { exch bbox 3 exch put } if } def % -------------------------- % 3. 保存原操作符,重定义绘图命令以收集边界框 % -------------------------- save % 保存原stroke/fill/show等操作 /origstroke stroke load def /origfill fill load def /origshow show load def /origashow ashow load def /origwidthshow widthshow load def % 重定义stroke:先更新bbox,再执行原stroke /stroke { updatebbox origstroke } def % 重定义fill:先更新bbox,再执行原fill /fill { updatebbox origfill } def % 重定义文本显示命令:先将文本转换为路径,再更新bbox,最后执行原命令 /show { gsave currentpoint moveto true charpath pathbbox % 更新全局bbox 0 get bbox 0 get lt { exch bbox 0 exch put } if 1 get bbox 1 get lt { exch bbox 1 exch put } if 2 get bbox 2 get gt { exch bbox 2 exch put } if 3 get bbox 3 get gt { exch bbox 3 exch put } if grestore origshow } def % 处理其他文本显示命令(ashow/widthshow),逻辑与show一致 /ashow { gsave currentpoint moveto true charpath pathbbox 0 get bbox 0 get lt { exch bbox 0 exch put } if 1 get bbox 1 get lt { exch bbox 1 exch put } if 2 get bbox 2 get gt { exch bbox 2 exch put } if 3 get bbox 3 get gt { exch bbox 3 exch put } if grestore origashow } def /widthshow { gsave currentpoint moveto true charpath pathbbox 0 get bbox 0 get lt { exch bbox 0 exch put } if 1 get bbox 1 get lt { exch bbox 1 exch put } if 2 get bbox 2 get gt { exch bbox 2 exch put } if 3 get bbox 3 get gt { exch bbox 3 exch put } if grestore origwidthshow } def % -------------------------- % 4. 运行目标代码(仅收集边界框,不实际输出) % -------------------------- % 替换为你的任意PostScript代码 200 200 moveto 100 0 lineto 0 100 lineto closepath stroke 500 500 moveto (Hello PostScript!) show % -------------------------- % 5. 恢复原操作符,结束边界框收集阶段 % -------------------------- restore % -------------------------- % 6. 计算缩放和平移参数(以A4页面为例,边距20pt) % -------------------------- /bboxminx bbox 0 get def /bboxminy bbox 1 get def /bboxmaxx bbox 2 get def /bboxmaxy bbox 3 get def /bboxwidth bboxmaxx bboxminx sub def /bboxheight bboxmaxy bboxminy sub def % 页面尺寸(A4:595pt宽,842pt高) /pagewidth 595 def /pageheight 842 def /margin 20 def /usablewidth pagewidth margin 2 mul sub def /usableheight pageheight margin 2 mul sub def % 处理内容为空的极端情况 bboxwidth 0 eq bboxheight 0 eq or { /scalefactor 1 def /translatex margin def /translatey margin def } { % 计算缩放比例:取宽高方向适配可用区域的较小值 /scalefactor usablewidth bboxwidth div usableheight bboxheight div min def % 计算平移量,使内容在可用区域居中 /translatex margin usablewidth scalefactor sub 2 div bboxminx scalefactor mul sub def /translatey margin usableheight scalefactor sub 2 div bboxminy scalefactor mul sub def } ifelse % -------------------------- % 7. 应用变换并实际绘制目标代码 % -------------------------- % 重置图形状态 gsave translate translatex translatey scale scalefactor scalefactor % 再次运行你的任意PostScript代码 200 200 moveto 100 0 lineto 0 100 lineto closepath stroke 500 500 moveto (Hello PostScript!) show grestore % 结束作业 showpage
关键细节说明
- 边界框收集逻辑:通过重定义
stroke、fill等绘图操作,在执行原操作前调用pathbbox获取当前路径的边界,更新全局边界框;对于文本,使用true charpath将文本转换为路径后再提取边界。 - 操作符保护:使用
save/restore保存和恢复原操作符,避免修改全局环境影响后续代码。 - 极端情况处理:当内容为空(边界框宽高为0)时,直接使用默认平移量,避免除以0错误。
- 兼容性:基于PostScript Level 3特性实现,可在任意兼容Level 3的PostScript解释器上运行,不依赖GhostScript等特定实现。
内容的提问来源于stack exchange,提问作者user31708
相关产品推荐
相关产品推荐

