跳转到内容

PostScript 常见问题解答/PostScript 编程

来自维基教科书,开放的世界,开放的书籍

PostScript 编程

[编辑 | 编辑源代码]

如何编辑 PS 文件?

[编辑 | 编辑源代码]

很少有人想编辑 HPGL 或 PCL。PostScript 是一种由某些激光打印机使用的另一种控制语言。在大多数情况下,PostScript 文件是根据某种人类可读文档以算法方式生成的。如果可能,获取原始文档。

可视化编辑需要解释 PS 文件、编辑对象以及生成新的 PostScript 文件。很少有应用程序支持 PostScript 图形模型的所有功能。不支持的功能将丢失或被近似。作为一种编程语言,PostScript 可以根据执行环境采取不同的分支。未采取的分支将被解释器丢弃,在编辑器中不留任何痕迹。当打印机驱动程序生成 PS 文件时,它不关心保留高级结构。除了修复拼写错误或更改页码之外,要进行其他操作可能非常困难。

许多主流图形编辑器可以导入 PS 文件。内置解释器的质量各不相同,通常限于 1 级。PDF 导入过滤器往往效果更好;先尝试将 PS 文件转换为 PDF。

如果 PS 文件包含太多可视化编辑器不支持的原语,则可以使用 Ghostscript 中的 pstoedit 实用程序将 PostScript 文件简化为一系列路径操作。

EnFocus Software 的 Tailor 似乎是唯一可用的专有 PostScript 编辑器。(它很久以前就停止了)。CGS 还有一款名为 PDF Tuner 的软件,可以编辑 PostScript 和 PDF 文件。这款软件一直在不断开发中。

手动编辑需要对 PostScript 语言有很好的了解,以及一个强大的文本编辑器。有时用户会创建超过 100M 的 PS 文件。这些文件通常包含二进制数据;有时它们取决于数据的确切大小。文本编辑器不应该扩展制表符或标准化行尾。文件的十六进制视图可能很方便。

重新定义操作符和过程是一种强大的技术,可以改变 PS 文件的行为。新的代码可以包含在 RIP 启动脚本中,由驱动程序从 PPD 复制,或者由输入过滤器直接添加到文件中。token 操作符有助于在操作符重新定义不起作用时,将控制权从 PostScript 解释器中夺走。

习语识别是一种自动化技术,可以在绑定 PS 过程时修补它。尽管它最初是为了在遗留 PostScript 程序中添加平滑阴影而设计的,但它也可以用于修复错误。

如何创建 PS 表格并从数据库填充它?

[编辑 | 编辑源代码]

固定格式表格可以在任何图形编辑器中创建,并包含任何演示图形。可以使用占位符 EPS 文件预留可变内容字段。

可以使用标准的 Unix 文本工具将占位符 EPS 文件替换为可变内容。多年来,类似的技术一直用于 OPI 工作流程。

或者,占位符 EPS 文件可以变得智能并自我打印。可变内容可以从 PostScript VM 或外部文件检索。

可变格式文档需要文本重新排版和重新分页。您可以选择使用可用的资源进行 直接 PostScript 文本格式化,或者在更高层次的页面布局语言(TeX、groff)中创建报告,然后将其转换为 PostScript 或 PDF。

在大多数情况下,不可能替换从标准 PostScript 驱动程序中收集的 PostScript 文档中的单词。驱动程序可能会为了字距调整而将单词分解,而不会使用 xshow,使用十六进制字符串,或者将文本行转换为采样图像。后者是将亚洲字体打印在罗马打印机上的常用方法。

如何连接多个 PS 文件?

[编辑 | 编辑源代码]

与普遍看法相反,PostScript 文件不能连接以获得组合的结果。

 # won't work
 cat page1.ps page2.ps page3.ps > threepages.ps

将文件作为单独的 Ghostscript 参数也不会有帮助。

 # won't work
 gs -sDEVICE=pswrite -sOutputFile=threepages.ps page1.ps page2.ps page3.ps

PostScript 程序会更改 PostScript 解释器状态,并且必须在运行下一个程序之前撤消这些更改。首先,编写以下标题。

 %!
 /begin_file
  { /save_state save def  % save state of PS interpreter
    currentfile
    0 (% $$$ EOF Mark $$$) /SubFileDecode filter cvx exec % safequard against flushfile, etc.
  } bind def
 /end_file
  { clear cleardictstack  % clear after the file
    save_state restore    % restore the state
  } bind def

将每个 PostScript 文件包含在以下包装器中,并将它们追加到标题。

 begin_file
 % Include your file here.
 % $$$ EOF Mark $$$       % don't delete this line
 end_file

即使原始文件符合 DSC 标准,结果也不会符合 DSC 标准。

EPS 文件的合成是通过将这些文件 放置到一个容器 EPS 文件中来完成的。

作业服务器 模式下运行的 PostScript 解释器(大多数打印机,Ghostscript 8.50 或更高版本,使用 -dJOBSERVER)将以 ^D 字符分隔的多个作业视为独立作业,并将其通过管道传递到输入流中。

一般来说,不可能连接多个 PS 程序以获得组合的结果。以下程序将阻止所有尝试向其追加内容的操作。这种技术最常在 OCF 字体安装程序中找到。

 systemdict begin (%stdin) (r) file flushfile

如何打印带重音符号的字符?

[编辑 | 编辑源代码]

单字节 PS 字体可以包含许多字形,但一次只能访问其中的 256 个。将字符代码映射到字形名称的 256 个元素数组称为编码向量。PostScript 语言有几个内置的编码向量。您可以(并且应该始终)重新编码您的字体。ISOLatin1Encoding 可能是一个不错的起点。要重新编码字体,您需要执行以下操作

/Courier findfont               % load the font, for instance, Courier
0 dict copy begin               % copy it to a new dictionary
/Encoding ISOLatin1Encoding def % replace encoding vector
/MyCourier /FontName def        % replace font name
currentdict end
dup /FID undef                  % remove internal data
/MyCourier exch definefont pop  % define the new font 

重新编码的字体可以像任何其他字体一样使用。

有几种方法可以一次打印超过 256 个字形。

  • 创建几个重新编码的字体,并在需要时选择它们。
  • 对稀有字符使用 glyphshow
  • 从几个重新编码的字体创建 OCF 字体。OCF 支持所有可以想象的编码方案。
  • 从 CMap 和 Font 或 CIDFont 资源创建复合字体。

如何在 PostScript 中对齐文本?

[编辑 | 编辑源代码]

常用方法是在应用程序中进行文本格式化。应用程序会发出反映最终格式的 PostScript 代码 - 现在只需将内容转储到 x、y 坐标即可。如果应用程序覆盖了默认的字间距/字内距(例如,用于对齐或字距调整),它应该使用 PostScript 的 ashowwidthshowawidthshowkshowxshowyshowxyshow 操作符,这些操作符接受一个完整的文本字符串,以及一个单独的规范来定位元素。不规范的替代方案,只是对单个字母和使用 moveto 分隔的小单词片段使用 show,会阻碍文本提取程序和精馏器。

要在 PostScript 中执行文本格式化器的常用功能,例如连字符化、多栏文本、脚注、参考文献、词汇表等,需要在 PostScript 中实现合适的算法,并将它们与要渲染的文件一起提供,或者包含在文件中,或者预先下载。现有的实现包括 David Byram-Wigfield 的 TinyDict、Graham Freeman 的 Quikscript 和 Don Lancaster 的 Gonzo Utilities。这三者都功能齐全,具有对齐、分栏、分页和许多其他功能。更简单的构建块包括 Chapman Flack 的 Markup,它可以用作这些工具的前端,或者仅用于执行简单的左/中/右未填充/未对齐文本设置,以及 Hyphenate,这是对 TeX 连字符化算法的实现,许多特定于语言的模式已为此而编译。总的来说,这些在 PostScript 本身中执行大部分计算的方法可以被称为(Byram-Wigfield 的术语)直接 PostScript,并且 Anastigmatix 直接 PostScript 页面 进一步描述并比较了它们。

自动 字距调整 需要在大多数字体附带的 Adobe 字体度量 (AFM) 文件中找到的信息,因此要在 PostScript 中执行此操作需要下载相应的 AFM 文件以及一些用于解析它们的 PostScript 代码(语法很简单)。目前似乎还没有提供执行此操作的直接 PostScript 资源。所有现有的系统都允许手动字距调整,这通常足够了,因为字距调整对于大型显示字体最为重要,而大型显示字体只占典型文档的一小部分。

设计并不局限于两种方法,即应用程序仅发出纯 PostScript 或完整的直接 PostScript。第一种方法往往可以节省打印机上的计算时间,但会以发出大型、不可编辑的 PostScript 文件以及更长的传输时间到打印机为代价,而直接方法可以具有紧凑、可编辑的文件(具有固定的实现代码前导,这些代码可以预先下载一次)并在打印机上执行更多计算。大多数应用程序都会选择折衷方案,在应用程序中执行部分工作在发出的 PostScript 中包含前导,这些前导提供用于在打印机本身完成工作的过程。甚至可以设计一个应用程序,使用现有的直接 PostScript 资源之一作为其前导,并以这种形式发出文档。

如何放置多张图片的副本?

[edit | edit source]

图片可以是采样图像或 EPS 文件。

PostScript 总是向前读取标准输入流。无法重新定位文件或将字符推回。下一张图片需要一份新的数据副本。为了重复使用图片,我们需要将数据从输入流复制到可重复使用的对象中。

少量数据可以存储在 PostScript 字符串中,但字符串长度限制为 64K-1。更多数据可以存储为字符串数组。数组的长度也限制为 64K-1,但这足以耗尽大多数打印机的内存。实际上,低端打印机可能只有 256K 的可用内存。

编码过滤器可用于压缩图片数据。在 3 级 PostScript 中,大多数编码过滤器是可选的。

PostScript 3 级引入了可重复使用的流。它们缓存输入数据,并在读取到文件末尾后可以重新定位。在无磁盘打印机上,可重复使用的流在内存文件系统中实现。

一些打印机和大多数基于主机的 RIP 具有可写文件系统。

为了避免多次渲染,PostScript 2 级引入了表单。它是围绕一个过程的包装器,该过程提示 PostScript 解释器如何缓存渲染结果。表单过程可以执行多次,并且需要可重复使用的数据源。表单很少使用,许多 Adobe OEM 为表单缓存选择非常小的默认大小。

如何重新定义运算符?

[edit | edit source]

最简单的方法是使用 bind 运算符。

 /foo { bar } bind def

不建议在生产环境中使用这种方法,因为 foo 可能已被重新定义为过程。以下方法在所有情况下都有效。

 /baz /foo load def
 /foo { baz bar } bind def

Ghostscript 还提供了特定于实现的运算符重新定义方式。请参阅 Ghostscript 源代码中对 odef 运算符的注释。

良好的重新定义应尽可能保留原始运算符的许多特性。以下对半色调运算符的简单重新定义可能会导致一些意想不到的问题。

 /sethalftone { pop } bind def
 /setscreen { pop pop pop } bind def
 /setcolorscreen { 12 { pop } repeat } bind def

首先,半色调可以从当前文件读取数据。其次,错误处理方面的差异可能很大。早期版本的 Mac OS X 的库存安装生成了以下代码

 featurebegin { 30 60 setscreen % no function!
 } featurecleanup

无论 featurecleanup 如何,上面的重新定义都会破坏堆栈并导致 PostScript 错误。以下是一些更好的重新定义

 /sethalftone_orig /sethalftone load def
 /setscreen_orig /setscreen load def
 /setcolorscreen_orig /setcolorscreen load def
 /sethalftone { gsave sethalftone_orig grestore } bind def
 /setscreen { gsave setscreen_orig grestore } bind def
 /setcolorscreen { gsave setcolorscreen_orig grestore } bind def

如何连接字符串?

[edit | edit source]

PostScript 没有内置的字符串连接运算符。以下过程(从 GNU Ghostscript 复制)似乎是最短的。

 /concatstrings % (a) (b) -> (ab)  
   { exch dup length    
     2 index length add string    
     dup dup 4 2 roll copy length
     4 -1 roll putinterval
   } bind def  

多个字符串的连接可以比反复调用 concatstrings 更有效地完成。为方便迭代,字符串存储在数组中。

 /concatstringarray  %  [(a) (b) ... (z)] --> (ab...z)  
    { 0 1 index { length add } forall string     
      0 3 2 roll      
        { 3 copy putinterval
          length add 
        }
      forall pop  
    } bind def

最小值和最大值函数在哪里?

[edit | edit source]

PostScript 没有内置的 minmax 运算符,但它们可以轻松地用过程编码。

 /min { 2 copy gt { exch } if pop } bind def
 /max { 2 copy lt { exch } if pop } bind def

Ghostscript 在 systemdict 中具有 .min.max 运算符。为了向后兼容,Ghostscript 还提供了定义为以下内容的 minmax 过程

 /max { .max } bind def
 /min { .min } bind def

如何对数组进行排序?

[edit | edit source]

Ghostscript 在 gs_init.ps 中有一个 冒泡排序 过程。

 % <array> <lt-proc> .sort <array>
 /.sort 
   { 1 index length 1 sub -1 1 
       { 2 index exch 2 copy get 3 copy	% arr proc arr i arr[i] arr i arr[i]    
         0 1 3 index 1 sub 
           { 3 index 1 index get	        % arr proc arr i arr[i] arr imax amax j arr[j]      
             2 index 1 index 10 index exec
               {                               % ... amax < arr[j]	
                 4 2 roll      
               } 
             if pop pop    
           } 
         for             			% arr proc arr i arr[i] arr imax amax    
         4 -1 roll exch 4 1 roll put put  
       } 
     for
     pop
   } bind def

net.anastigmatix.Order 提供插入排序、快速排序、堆排序、数组最小值、最大值以及同时最小值和最大值,以及任意顺序统计信息

 /net.anastigmatix.Order /ProcSet findresource begin
 [7 49 73 58 30 72 44 78 23 9 40 65 92 42 87] //PolyCmp QuickSort

这个 希尔排序 过程对任何其元素可以通过过程比较和交换的元素进行排序。

 %!
 % Shell sort in PostScript
 % Copyright (c) 2002 by Alex Cherepanov.  All rights reserved.
 % Distributed under GPL, http://www.gnu.org/licenses/gpl.txt
 
 %
 % Shell sort procedure based on compare and exchange operation
 % < i > < j > exch_less <bool> compares keys at positions i and j
 %                          exchange if not in order, retutn *i<*j
 % <len> is the number of elements
 %
 /shellsort                       % {} len -> -
   { dup dup 2 idiv               % {} len m p
       { dup 0 eq { exit } if
         exch                     % {} len p m
         15 le { dup 2 idiv } { dup } ifelse
         16#fffffffe and 1 add    % {} len p m'
         1 1 4 index 3 index sub  % {} len p m' 1 1 len-_m
           { 1 index neg 1        % {} len p m' is -m 1
               { 1 sub            % {} len p m' ii-1
                 2 copy add       % {} len p m' ii-1 m+ii-1
                 5 index exec
                   { exit
                   }
                 if
               }
             for
           }
         for
         exch 2 idiv              % {} len m' p'
       }
     loop
     pop pop pop pop              % -
   } bind def
 
 %
 % Sample unsorted array
 %
 /sample_array [ 1 8 76 3 7 0 7 8 55 86 5 58 57 55 3 6 9 6 66 4 3 4 8 65 8 8 55
                 4 5 88 55 3 6 7 44 5 7 4 7 43 3 ] def
 %
 % Sample array compare and exchange
 % The elements at positions i and j are compared and exchanged
 %
 % if (*i<*j)
 %   return true
 % (*i,*j) = (*j,*i)
 % return false
 %
 /array_exch_less        % i j -> bool
   { 6 index 3 1 roll    % [] i j  % get the element just below {exch}
     2 index exch        % [] i [] j
     4 copy              % [] i [] j [] i [] j
     get                 % [] i [] j [] i *j
     3 1 roll get        % [] i [] j *j *i
     2 copy ge           % [] i [] j *j *i *i<*j
       { pop pop pop pop pop pop //true
       }
       { exch            % [] i [] j *i *j
         4 1 roll        % [] i *j [] j *i
         put put //false
       }
     ifelse
   } bind def
 
 %
 % Sort the array
 %
 /array_sort  % [unsorted] -> [sorted]
   { //array_exch_less 1 index length shellsort
   } bind def
 
 %
 % Print the sorted array
 %
 sample_array array_sort ==
[edit | edit source]

从 2 级开始,glyphshow 运算符允许显示一个命名的字形

% display a "registered trademark" symbol
/registered glyphshow

在哪里可以获取更多 PostScript 代码示例?

[edit | edit source]

关于 PostScript 的书籍包含许多简单的 PS 程序。

Ghostscript 发行版在示例目录中包含一些示例 PostScript 文件。Ghostscript 的很大一部分是用 PostScript 语言实现的,包括 PDF 1.4 解释器。

Adobe 发布了一些 3 级功能的示例程序。

免费示例库包含许多优秀的(和糟糕的)PostScript 文件。该库收集了有趣的手动编码或从应用程序中收集的 PostScript、PDF、PCL 和 TIFF 文件示例。

要了解排版过程,请参阅“实用 PostScript”数字排版的初学者指南,90 页(电子书 416k PDF)。[[1]]

如何调试 PostScript 程序?

[edit | edit source]

你应该尽快学习的第一项技能是如何解释 PostScript 错误消息。它们并不总是容易理解。错误报告因产品而异。一些产品不会提供 PostScript 错误详细信息(例如 EPS 导入到 Photoshop),最好避免将它们用于任何学习目的。

标准 PostScript 错误格式类似于:

PS>0 1 2 3 4 5 put
%%[ Error: typecheck; OffendingCommand: put ]%%
PS><< >> image
%%[ Error: undefined; OffendingCommand: image ]%%

你会看到这包括错误名称和导致错误的命令。请不要将此描述为“错误命令”错误,因为所有消息都这么说,并记住错误名称和错误命令都很重要。后面的消息意味着在图像字典中找不到必需的键。

如果你碰巧使用的是 Ghostscript,你可能会得到更多细节。请注意,错误消息包括堆栈和操作符名称。

GS>0 1 2 3 4 5 put
Error: /typecheck in --put--
Operand stack:   0   1   2   3   4   5
Execution  stack:   %interp_exit   .runexec2   --nostringval--    
--nostringval--   --nostringval--   2   %stopped_push   --nostringval--  
--nostringval--   %loop_continue   2   3   %oparray_pop   --nostringval--    
--nostringval--   false   1   %stopped_push   .runexec2   --nostringval--    
--nostringval--   --nostringval--   2   %stopped_push   --nostringval--   
--nostringval--   --nostringval--
Dictionary stack:   
--dict:1044/1123(ro)(G)--   --dict:0/20(G)--   --dict:69/200(L)--
Current allocation mode is local
Current file position is 16

你可以查看操作符的描述以及堆栈以查看你的操作数,并且通常可以看出它为什么在抱怨。然后你就可以(有时)找出原始代码中问题的所在。请记住,失败的 PostScript 操作符不会改变操作数堆栈。

Ghostscript 在命令行参数传递的文件中发生错误时退出。使用操作符 run 来保持会话并交互式地检查操作数堆栈。

 $ gs
 GS> (foo.ps) run

使用 = 和 == 命令来打印有关操作数堆栈上对象的信息。要查看字典的内容,将字典推送到堆栈上,然后使用 {== ==} forall。例如,要查看用户字典的内容,使用 userdict {== ==} forall。

尝试在不同的解释器上运行程序。不同的错误消息可能会揭示更多关于问题的信息。 Ghostscript 在使用 -dOSTACKPRINT 和 -dESTACKPRINT 选项运行时,可以分别打印操作数和执行堆栈的详细转储。堆栈的顶部元素最后打印。

错误处理程序是一个 PostScript 过程,它在 PostScript 解释器遇到错误时运行。它最初由 PS 解释器安装,但可以由作业替换。PostScript 驱动程序提供的错误处理程序非常基本 - 删除它。在 Ghostscript 上,你将获得使用默认错误处理程序的更好的错误消息。在其他解释器上,你可以使用 Adobe 提供的 ehandler.ps

如果问题仍然不明确,尝试通过删除或注释掉代码片段来创建最小的仍然存在问题的程序。使用 { ... } pop 结构是禁用 PostScript 代码部分的便捷方法。将每个版本的 文件保存在不同的名称下,以便进行多级撤消。当文件被简化为一个单一过程时,尝试用内联代码替换它。当过程被埋在其他代码中或多次重新定义时,尝试在执行位置加载它并打印。例如:/foo load ==

你可以通过在过程或重新定义操作符中添加打印操作符来跟踪 PostScript 程序。 Ghostscript 包含一个脚本 traceop.ps,它可以帮助你在操作符上设置跟踪。在将脚本包含到 Postscript 程序的最前面之后,在你想开始跟踪对操作符 foo 的所有调用时,放置 /foo traceop。然后修改过程 /tracebefore/traceafter 以打印一些相关的诊断信息。例如,/tracebefore { count traceprint } def 将在进入 /foo 之前打印整个堆栈。(/traceprint 基本上执行一个 {print ==only } for,如上所述。)然后正常执行操作符 /foo。如果程序在光栅设备上无法正确渲染,那么在 PS 到 PDF 的转换过程中很可能也会出现同样的问题。PDF 文档保留了更多信息,可以被解释为图形操作的跟踪。

LaserTalk 曾经是 Adobe 开发人员工具包的一部分。它是一个 PostScript 打印机的可视化前端。LaserTalk 只跟踪了执行的顶层。

PSAlter 来自 Quite Software,是市场上唯一的可视化 PostScript 调试器。

如何包含一个 EPS 文件?

[edit | edit source]

EPS 格式在 Adobe 技术说明 5002 中有说明,Encapsulated PostScript File Format Specification (PDF 0.2M) 和 PostScript Language Reference Manual 的第二版。规范包括创建 EPS 文件的指南(它们只是包含几个必需的标题注释并承诺不执行某些操作的 PostScript 程序),以及导入 EPS 文件的指南。EPS 文件文本可以简单地粘贴到封闭的 PostScript 程序中,位于 %%BeginDocument:%%EndDocument 注释之间。(%%BeginDocument: 关键字可以后跟描述性文本,例如 EPS 文件名。)封闭程序有责任在插入的 EPS 之前建立一个合适的状态,并在之后恢复之前状态。程序应该

  • 使用 save 对状态进行快照
  • showpage 重新定义为无操作
  • 将颜色空间、颜色、线帽、线宽、线连接、斜角限制、虚线模式、当前路径、套印和笔触调整模式设置为指定的默认值
  • 呈现一个空的运算数堆栈和默认的字典堆栈(但包含无操作的 showpage
  • 计算并设置一个坐标变换,将 EPS 图像放在它想要的地方
  • 设置一个与图像的边界框匹配的剪切路径

在插入的 EPS 代码之后,封闭程序应该丢弃 EPS 代码可能留在运算数和字典堆栈上的任何内容,恢复之前的内容,然后在先前的 save 对象上执行 restore,这将撤消所有其他准备工作。

输出 PostScript 的应用程序通常会省略在每个插入 EPS 图像的地方执行所有这些操作的代码,并且它们经常会偷工减料,省略了做出简化假设的代码。特别是在手动编写 PostScript 时,使用例如 Anastigmatix StatEPSF、ReadyEPSF、ExecEPSF 过程会更方便,它们会自动执行必要的步骤。以下代码是将 EPS 放在 (100, 550) 的位置,旋转 0 度,在两个轴上缩放 1 所需的全部内容

 % in the document setup:
 /net.anastigmatix.Import /ProcSet findresource begin
 % at the point of import:
 { StatEPSF 100 550  0  1 1 ReadyEPSF ExecEPSF }
 currentfile exch exec
 %%BeginDocument: helloworld.eps
 ... the EPS file contents go here
 %%EndDocument

当然,没有任何东西可以阻止应用程序以这种方式生成 PostScript。

一些 EPS 文件包含预览图像。在 DOS/Windows 格式中,它们是二进制数据,在将 EPS 文本粘贴到另一个程序时必须将其剥离。EPSI 预览格式采用 PostScript 注释的形式,可以删除以节省空间,但即使保留也不会造成问题。

PostScript 是否支持用于 CJK 字体的 Unicode?

[edit | edit source]

PostScript 并没有专门支持 Unicode。它(从第 2 级开始)包含对多字节(2、3 或 4 字节)字体的通用支持。Unicode 只是多字节编码的一种特殊情况。没有真正的 Unicode 字体,但你可以使用支持 Unicode 范围的多字节 CID 字体。

没有免费可重新分发的 Unicode CMaps。它们可以从 MS Windows 代码页和相应的 Adobe CMaps 推导出。

开发人员经常寻找一种“简单”的方法,只需包含 Unicode 字符串,而无需检查范围、语言等,但这只是一个白日梦。

一些 PostScript 引擎也提供对没有内部 CID 映射的 TrueType 字体的支持。在这种情况下,这些字体不需要 CID 映射,但只需要一个读取过滤器来解码存储字节序列的字符串的 Unicode 编码流;可以采用几种解码过滤器,包括支持 UTF-8 和 UTF-16 的那些,因为 PostScript 的“read”操作符只需要过滤器返回任何方便的整数,该整数用作字体字典中字形的索引。

过滤器可以在 PostScript Level 2 中被链接并像其他文件对象一样被 read 操作符使用。请注意,大多数 PostScript 应用程序期望 bytesavailableread 在不等待或遇到文件结尾(或字符串结尾)的情况下可以返回的整数数量。

然后,read 操作符不仅限于返回范围为 0..255 的整数,因此 read 的实际返回值是代码单元(或代码点或字形 ID),使用任何方便的编码,这种编码被支持为所选字体的字形索引,并且 read 操作符实际上将从其源(文件、字符串或过程)读取解码过滤器所需的尽可能多的字节。其他标准过滤器支持数据解压缩,或从任何来源解码十六进制或 ASCII85 序列。

因此,与其使用 forall 操作符枚举字符串中的字符,不如将字符串传递给 Unicode 解码过滤器,并使用 read 循环从过滤器对象中枚举字符(或字形索引),然后使用返回的整数索引在复合字体中查找字形来渲染字符。如果 PostScript 引擎不支持 Unicode 或 TrueType 的本机支持,你将不得不将 TrueType 字体转换为 CID 映射字体,并使用一个过滤器,该过滤器将返回可与加载到文档序言或作为资源传输到打印文档之前的 CID 映射字体一起使用的 CID 值。

你可能还需要一个布局引擎(如 Pango)将 Unicode 代码点处理成一系列字形 ID,并预先计算它们在 PostScript 渲染的文档中的位置,这些位置将在 PostScript 引擎使用相关的过滤器进行编码和解码。这种文本布局引擎在任何情况下都需要支持 BiDi 算法,以及渲染复杂脚本、连字、上下文形式或字体中描述的变体,这些变体用在你的文档设计中……

华夏公益教科书