如何在PDF内容流中编码各种字形?

Dav*_*vid 2 c pdf fonts encoding utf-8

我正在制作一个输出PDF文档的程序.给定一系列UTF-8编码字符和用于呈现它的字体名称,我想显示构成文档实际内容的相应字形.我希望能够显示国家字符,如čö.支持诸如aeffi之类的连接将会很棒.

问题是,我不知道如何指定要显示的实际字形(例如,在内容流中).

例如,如果我想显示字符串"Hello World",我不必担心编码,我只是写(Hello World)Tj.然后,PDF阅读器将使用适当的字体来呈现此字符串.

但是,如果我想显示字符串 ,那么整天都难以阅读PDF规范.Prostědočistanemožné! 使用连字符ffi,fiea以及给定字体中的捷克国家符号" ě,č"和" é",我将如何处理?

我试图通过PDF规范,但这并不容易.

  • 如何找出与给定字符或连字相对应的"字形代码"?
  • 这段代码是如何在PDF内容流中编码的

非常感谢帮助.


编辑:我可能高估了这个问题.计算显示"普通欧洲文档"所需的字形,我想不出这个数字如何超过256的方式.如果我的假设是正确的,我可以完全重新映射字体的编码.这应该足以涵盖拉丁字母的所有常见符号,数字,标点符号,常见符号等(,[并且仍然有足够的空间用于国家符号,连字和高质量排版的其他元素.(如果字形总数超过256,我可以实现优先级队列来选择最常用的连字.)

话虽这么说,我认为我不需要使用CID键控字体.

我仍然徘徊如何将UTF-8编码的字符映射到任意字体的字形上.我有可用的字体AFM.例如,对于DejaVu字体,字符信息如下所示:

C 63 ; WX 536 ; N question ; B 67 -15 488 743 ;
C 64 ; WX 1000 ; N at ; B 65 -174 930 705 ;
C 65 ; WX 722 ; N A ; B -6 0 732 730 ;
Run Code Online (Sandbox Code Playgroud)

但在映射第256 字符后,代码为-1:

C 255 ; WX 564 ; N ydieresis ; B -3 -223 563 767 ;
C -1 ; WX 722 ; N Amacron ; B -6 0 732 899 ;
C -1 ; WX 596 ; N amacron ; B 49 -15 568 746 ;
Run Code Online (Sandbox Code Playgroud)

例如,如果我11100010 10000010 10101100在输入中有序列(欧元符号),我怎么知道它对应的字形名称,以便我可以将它映射到/Encoding字典中?

pli*_*nth 9

编码因字体类型而异.通常,存在被定义为当前字体的字体资源,并且该字体字典内是对基本字体的引用和描述编码的方式(通过/Encoding密钥).如果该键不存在,则编码将是"标准",但您可以使用其他简单编码,例如/MacRoman和编码/WinAnsi的值,或者您可以指定标准编码和编码增量来显示差异.

到目前为止很容易 - 只要你使用8位字符.对于许多早期的应用程序,他们会创建几个不同的字体,一个使用罗马编码,另一个将罗马字符映射到不可用的字符.为此,您的编码增量将包括对连字和其他通常非编码符号的引用.这适用于Type 1字体,但在TrueType字体部分中的规范明确禁忌:

非符号字体应指定MacRomanEncoding或WinAnsiEncoding作为其编码条目的值,没有差异数组

当你想使用Unicode时,这是完全不同的.在这种情况下,您将使用CID字体(基于字符ID的字体).在这种情况下,有一个由字体引用的过程,用于从字符串中的字符编码映射到字体中的字符ID(反之亦然).我强烈建议您阅读并完全理解复合字体PDF规范中的第9.7节,其中描述了将UTF16BE编码为字符串以使其在PDF中正确呈现所需的一切.这是非常重要的,因为有许多细节,如果错过将导致Acrobat中的空白呈现页面.

作为一名专业编写生成和使用PDF的代码的软件工程师,请允许我说当我的任务是在我的代码中放入特殊情况来处理不符合规范的PDF时,我内心的一小部分就死了.拜托,请不要考虑将你制作的任何文件发布到野外,直​​到它们至少通过预检.这与"Acrobat呈现它所以它必须正常"不同.让我给你举个例子-我已经看到了一些在野外文件,这些文件包括缺少FontDescriptor字典的关键要素字体,包括/Ascent,/Descent,/CapHeight等,这些使得在Acrobat,但违反该规范因为每个都是必需的.我知道Acrobat如何处理它 - 它带有一个巨大的字体度量数据库,如果它无法在文件中找到它,则查找该值(哎呀,它甚至可能忽略文件中的度量标准).我没有那么奢侈,所以我必须做一些(可能很昂贵/无效)的止损措施.

你可能想考虑使用一个库为你做这项工作 - 也许iText有一个足够好的教育许可计划,因为,我明白了,你是一个学生.还有一些基于C的库.也许你可以想办法让GhostScript做你的出价.

如果您不愿意或无法遵循我关于剔除规范或使用表面上这样做的库的建议,请帮我至少填写预告片引用的文档信息词典中的字符串/Creator/Producer字符串(见第14.3.3节和第7.5.5节).这样,当我必须解析/使用/操纵你的文档时,我将有办法直接在你的父母身上施放诽谤.

让我们自上而下,从页面对象开始 - 我正在使用我自己的库中的输出,并且正在剥离我认为你不需要的东西:

1 0 obj << 
    /Type /Page 
    /Parent 18 0 R 
    /Resources << 
       /Font << 
          /U0 13 0 R 
          >>
       /ProcSet [ /PDF /Text ]
       >>   
    /MediaBox [ 0 0 612 792 ]
    /Contents 19 0 R    
    /Dur -1 
    >>
 endobj
Run Code Online (Sandbox Code Playgroud)

U0是对将用于unicode文本的字体的引用.

内容流旨在打印以下文本:Greek: ???? ??? ??????.

BT /U0 24 Tf 72 670 Td 
(\000G\000r\000e\000e\000k\000:\000 \003\223\003\265\003\271\003\254\000 \003\303\003\277\003\305\000 \003\272\003\314\003\303\003\274\003\277\003\302) 
Tj ET
Run Code Online (Sandbox Code Playgroud)

引用的字体字典如下所示:

13 0 obj << 
    /BaseFont /DejaVuSansCondensed 
    /DescendantFonts [ 4 0 R  ]
    /ToUnicode 14 0 R 
    /Type /Font 
    /Subtype /Type0 
    /Encoding /Identity-H 
>>
endobj
Run Code Online (Sandbox Code Playgroud)

其中/ToUnicode包含包含以下PostScript代码的流的入口点:

/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def /CMapName /Adobe-Identity-UCS def /CMapType 2 def 1 begincodespacerange <0000> <FFFF> endcodespacerange 1 beginbfrange <0000> <FFFF> <0000> endbfrange endcmap CMapName currentdict /CMap defineresource pop end end
Run Code Online (Sandbox Code Playgroud)

它由CID字体规范定义.

并且DescendantFonts数组指向此对象:

4 0 obj << 
    /Subtype /CIDFontType2 
    /Type /Font 
    /BaseFont /DejaVuSansCondensed 
    /CIDSystemInfo 7 0 R 
    /FontDescriptor 8 0 R 
    /DW 1000 
    /W 9 0 R 
    /CIDToGIDMap 10 0 R 
>>
Run Code Online (Sandbox Code Playgroud)

CIDToGIDMap是一个带有实际地图的压缩流,CIDSystemInfo是<</Registry (Adobe) /Ordering (USC) /Supplement 0>>(它是一个参考,因为我在我输出的所有unicode字体中共享它.ContDescriptor是一个直接的锅炉板,W数组是从字体指标派生的.

有了这些细节,你能理解为什么我不轻描淡写地说,"在你污染我的环境之前走开"?

我真的开始质疑这项任务的性质.编写简单的PDF是一回事,但编写可以处理任意OpenType/TrueType字体的完整unicode的代码需要您了解CID规范 TrueType规范(提示:我有一个完整的TrueType解析器,可以提取所有指标字体中的任何字形,以便我可以输出/ W数组).

但是,如果您需要输出到Type 1字体,那么我的朋友,您的生活变得更加容易,因为您将获取整个UTF8流,将其作为unicode读取,并为每个独特的字符输入,使用此表构建从unicode字符到字形名称和内部字符编号的映射.内部字符数本质上是mod中出现的字符的唯一索引.因此,例如,如果页面上的唯一字符少于257个,则您将只有一种字体被编码以按到达的顺序映射到字符.如果输入有"abcba",则pdf中的输出字符串(\000\001\002\001\000)将映射到带有编码字典的字体,该字典具有差异数组[0/a/b/c].如果您有n个唯一字符,其中n > 256,那么您将拥有(n/256)+ 1个字体,每个字体都带有编码.

如果您的教师/教授在短时间内只需要1型字体,那么他/她就会对学生抱有不切实际的期望和/或对输出质量的期望值低.你应该问你是否需要处理CID字体,如果你是,那么你的教授至少是一个虐待狂.我花了一个经验丰富的专业人员,用了大约4天时间编写了一个用于提取宽度的TrueType解析器.我的优点是(1)使用托管语言(C#)减少了在C中咬你的屁股的问题,并且还能够使用反射来自动解析和(2)当我没有中断时,我编写的固体代码比典型的学生快10-20倍,所以我的32小时将转换为320学时,或多或少(然后再次,我的代码有不同于你的约束 - 它必须消耗它得到的任何废话字体优雅地),如果你被允许窃取像stb这样的东西,那就叫它200或更少.这只是为了在字体描述符中获取一个特定元素.

  • Preflight是Adobe Acrobat内置的工具(不是Reader,所以不是免费的),它可以对PDF文档运行多种不同类型的验证,包括语法和规范合规性。 (2认同)