c - PDF内容流中的各种字形如何编码?

标签 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个字符后,代码为-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词典中映射它?

最佳答案

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

到目前为止很简单-只要您使用8位字符即可。对于许多早期的应用程序,他们将创建几种不同的字体,一种使用罗马编码,另一种将罗马字符映射为不可用的字符。为了做到这一点,您的编码增量将包含对连字和其他通常未编码符号的引用。这非常适合Type 1字体,但是在TrueType字体部分中的规范特别禁止使用:


非符号字体应指定MacRomanEncoding或WinAnsiEncoding作为其Encoding条目的值,并且不带Differences数组


当您要使用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


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


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

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


具有/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


CID font specification定义。

并且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 
>>


CIDToGIDMap是具有实际地图的压缩流,CIDSystemInfo是<</Registry (Adobe) /Ordering (USC) /Supplement 0>>(这是一个引用,因为我在我输出的所有unicode字体中共享它。FontDescriptor是一个简单的样板,W数组派生自W字体指标。

有了所有这些细节,您是否理解我为什么不轻率地说“在污染环境之前就走开”?

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

但是,如果只需要输出为Type 1字体,那么我的朋友,您的生活就容易了很多,因为您可以将整个UTF8流作为Unicode读取,并且对于其中出现的每个唯一字符,您都会使用this table从Unicode字符到字形名称和内部字符编号构建映射。内部字符号本质上是mod中字符的唯一索引。因此,例如,如果页面上的唯一字符少于257个,则将只编码一种字体,以按到达顺序将其映射到这些字符。如果输入的是“ abcba”,则pdf中的输出字符串将为(\000\001\002\001\000),并将映射到带有差异字典为[0/a/b/c]的编码字典的字体。如果您有n个大于256的n个唯一字符,则将具有(n / 256)+ 1种字体,每种字体都有编码。

如果您的老师/教授在短时间内想要除Type 1字体以外的任何内容,则他/她对学生的期望不切实际,并且/或者对输出质量的期望不高。您应该询问是否需要处理CID字体,如果是,那么您的教授至少是个虐待狂。我,一个经验丰富的专业人员,花了大约4天的时间来编写TrueType解析器来提取宽度。我的优势在于(1)使用托管语言(C#),它减少了将在C语言中困扰您的问题,并且还能够使用反射来自动进行解析;以及(2)当我没有中断时,我编写的代码比普通学生快大约10-20倍,所以我的32个小时会转化为320个小时的学习时间,或多或少(然后,我的代码与您的约束有所不同-它必须使用任何废话字体)正常),因此,如果允许您窃取类似stb之类的东西,我们可以称其为200以下。这只是为了在字体描述符中获得一个特定的元素。

关于c - PDF内容流中的各种字形如何编码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20355884/

相关文章:

python - 错误的编码 - PhpMyAdmin/MySql

regex - 纠正由错误编码引起的乱码的最佳方法

c - C 代码中的 GPA 计算器

node.js - Node wkhtmltopdf 在 Node Webkit 中创建损坏的 PDF

c - 反转c中的单词

pdf - GhostScript 替代方案

javascript - 制作一个 anchor 标记,点击时触发打印操作

java - 获得正确的编码

c - 即使设置 PATH 变量后也是 "gcc is not recognized"。剩下什么?

c 查询txt记录