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

时间:2013-12-03 16:01:42

标签: c pdf encoding utf-8 fonts

我正在制作一个输出PDF文档的程序。给定一系列UTF-8编码字符和用于呈现它的字体名称,我想显示构成文档实际内容的相应字形。我希望能够显示国家字符,例如čö。支持像 ae ffi 这样的连字是很棒的。

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

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

但是如果我想显示字符串怎么办? 整天都难以阅读PDF规范。 Prostědočistanemožné! 连接 ffi fi ea 和捷克国家符号ěčé在给定的字体中,我将如何处理?

我正在尝试通过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 ;

但是在映射了256 th 字符后,代码为-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 ;

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

1 个答案:

答案 0 :(得分:8)

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

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

  

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

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

作为一名专业编写生成和使用PDF的代码的软件工程师,请允许我说明当我在代码中处理特殊情况以处理不符合规范的PDF时,我会死掉一小部分内。拜托,请不要考虑将你生产的任何文件发布到野外,直​​到他们至少通过Preflight。这与&#34; Acrobat不同,因此它必须正常。&#34;让我举个例子 - 我已经看到一些野外文件包含缺少FontDescriptor字典关键元素的字体,包括/Ascent/Descent,{{1这些在Acrobat中呈现,但违反了规范,因为每个都是必需的。我知道Acrobat如何处理它 - 它带有一个庞大的字体指标数据库,如果它无法在文件中找到它,就会查找它的值(哎呀,甚至可能忽略文件中的指标)。我没有那么奢侈,所以我必须做一些(可能是昂贵的/无效的)止损措施。

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

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

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

/Creator

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

内容流旨在打印以下文字:/Producer

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

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

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 条目指向包含以下PostScript代码的流:

13 0 obj << 
    /BaseFont /DejaVuSansCondensed 
    /DescendantFonts [ 4 0 R  ]
    /ToUnicode 14 0 R 
    /Type /Font 
    /Subtype /Type0 
    /Encoding /Identity-H 
>>
endobj

CID font specification定义。

并且DescendantFonts数组指向此对象:

/ToUnicode

CIDToGIDMap是一个带有实际地图的压缩流,CIDSystemInfo是/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 (它是一个参考,因为我在我输出的所有unicode字体中共享它.ContDescriptor是一个直接的锅炉板, W数组源自字体度量。

有了这些细节,你是否理解我为什么不轻描淡写地说,然后在你污染我的环境之前离开?#34;

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

但是,如果您需要输出到Type 1字体,那么我的朋友,您的生活变得更加容易,因为您将获取整个UTF8流,将其作为unicode读取,并将每个独特字符读取在,使用this table构建从unicode字符到字形名称和内部字符编号的映射。内部字符数本质上是mod中出现的字符的唯一索引。因此,例如,如果页面上的唯一字符少于257个,则您将只有一种字体被编码以按到达的顺序映射到字符。如果你有&#34; abcba&#34;对于输入,pdf中的输出字符串将为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 >> ,并将映射到具有编码字典的字体,其中差异数组将为<</Registry (Adobe) /Ordering (USC) /Supplement 0>>。如果您有 n 个唯一字符,其中 n &gt; 256,你将拥有( n / 256)+ 1种字体,每种字体都带有编码。

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