PDF格式的Unicode
-
02-07-2019 - |
题
我的程序根据要求生成相对简单的PDF文档,但是我遇到了unicode字符的麻烦,比如汉字或奇怪的数学符号。要在PDF中编写普通字符串,请将其放在括号中:
(something)
还可以选择使用八进制代码转义字符:
(\527)
但这最多只能达到512个字符。你如何编码或逃避更高的字符?我已经看到了对字节流和十六进制编码字符串的引用,但我读过的所有引用似乎都不愿意告诉我如何实际执行它。
编辑或者,请指出一个可以为我完成工作的优秀Java PDF库。我目前正在使用的是gnujpdf的一个版本(我已经修复了几个错误,因为原作者似乎已经失败了),这允许你针对AWT图形界面进行编程,理想情况下,任何替换都应该做同样的。
替代品似乎是HTML - <!> gt; PDF,或基于段落和方框的编程模型,感觉非常像HTML。 iText是后者的一个例子。这意味着重写我现有的代码,我不相信他们会给我相同的灵活性。
编辑2:我以前没有意识到,但iText库有一个Graphics2D API,似乎完美地处理unicode,所以这就是我将要使用的。虽然这不是问题的答案,但它解决了我的问题。
编辑3: iText对我很有用。我想这一课是,当面对一些看似毫无意义的事情时,找一个比你更了解它的人。
解决方案
简单的答案是没有简单的答案。如果你看一下PDF规范,你会看到整整一章<!>#8212;那个<!>#8212;致力于文本显示的机制。我为我的公司实施了所有PDF支持,处理文本是迄今为止最复杂的练习部分。你发现的解决方案<!>#8212;使用第三方库为您完成工作<!>#8212; <!> nbsp;确实是最佳选择,除非您对PDF文件有非常具体的特殊用途要求。
其他提示
在第3章的PDF参考中,这就是他们对Unicode的看法:
文本字符串以。编码 PDFDocEncoding或Unicode字符编码。 PDFDocEncoding是一个 ISO Latin 1编码的超集,见附录D. Unicode 由Unicode Consortium在Unicode标准中描述(参见参考书目)。 对于以Unicode编码的文本字符串,前两个字节必须为254后跟 这两个字节表示Unicode字节顺序标记U + FEFF,表示 该字符串是以指定的UTF-16BE(big-endian)编码方案编码的 在Unicode标准中。 (这种机制阻止了使用字符串开始 PDFDocEncoding与两个字符刺ydieresis,这是不可能的 是一个有意义的词或短语的开头。)
阿尔戈曼的答案在很多方面都是错误。您可以制作带有unicode的PDF文档'并且它不是火箭科学,虽然它需要一些工作。
是的,他是对的,要在一种字体中使用超过255个字符,你必须创建一个复合字体(CIDFont)pdf对象。
然后,您只需提及要用作CIDFont的DescendatFont条目的实际TrueType字体。
诀窍是,之后你必须使用字体的字形索引而不是字符代码。要获得此索引映射,您必须解析cmap
字体部分 - 使用GetFontData
函数获取字体内容并接受TTF规范。
就是这样!我刚刚做了,现在我有一个unicode pdf!
解析<=>部分的示例代码在此处: https://support.microsoft的.com / EN-US / KB / 241020
是的,不要忘记/ @Unodeode条目@ user2373071指出或用户将无法搜索您的PDF或从中复制文本。
正如dredkin指出的那样,你必须在页面内容流中使用字形索引而不是Unicode字符值。这足以在PDF中显示Unicode文本,但Unicode文本无法搜索。要使文本可搜索或对其进行复制/粘贴工作,您还需要包含/ ToUnicode流。此流应将文档中的每个字形转换为实际的Unicode字符。
请参阅PDF规范的附录D(第995页)。 PDF使用者应用程序中预定义的字体和字符集数量有限。要显示其他字符,您需要嵌入包含它们的字体。还优选仅嵌入字体的子集,仅包括所需字符,以减小文件大小。我也在努力在PDF中显示Unicode字符,这是一个很大的麻烦。
查看PDFBox或iText。
我现在已经在这个主题上工作了几天,而且我学到的是pdf中unicode(一样好)是不可能的。使用双字节字符时,基座所描述的方式仅适用于CID-Fonts。
看起来,CID-Fonts是一个pdf内部结构,它们在这个意义上并不是真正的字体 - 它们看起来更像是图形子程序,可以通过寻址它们来调用它们(使用16位地址)。 / p>所以直接在pdf 中使用unicode
- 你必须将普通字体转换为CID字体,这可能非常困难 - 你必须从原始字体(?)生成图形例程,提取字符指标等。
- 您不能像普通字体一样使用CID字体 - 您无法按照加载和缩放普通字体的方式加载或缩放它们
- 另外,2字节字符甚至不包括完整的Unicode空间 醇>
恕我直言,这些要点使得直接使用unicode 绝对不可行。
我现在正在做的是以下列方式使用字符间接: 对于每种字体,我都会生成一个代码页(以及一个快速查找的查找表) - 在c ++中,这就像
std::map<std::string, std::vector<wchar_t> > Codepage;
std::map<std::string, std::map<wchar_t, int> > LookupTable;
然后,每当我想在页面上放置一些unicode-string时,我会迭代它的字符,在查找表中查找它们 - 如果它们是新的,我将它们添加到代码页中,如下所示: / p>
for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++)
{
if(LookupTable[fontname].find(*i) == LookupTable[fontname].end())
{
LookupTable[fontname][*i] = Codepage[fontname].size();
Codepage[fontname].push_back(*i);
}
}
然后,我生成一个新字符串,其中原始字符串中的字符被代码页中的位置替换为:
static std::string hex = "0123456789ABCDEF";
std::string result = "<";
for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++)
{
int id = LookupTable[fontname][*i] + 1;
result += hex[(id & 0x00F0) >> 4];
result += hex[(id & 0x000F)];
}
result += ">";
例如,<!>; H <!>#8364; llo World!<!> quot;可能会变成<!> lt; 01020303040506040703080905 <!> gt; 现在你可以把这个字符串放到pdf中并像往常一样使用Tj操作符打印...
但你现在有一个问题:pdf不知道你的意思是<!>“H <!>”;通过01.要解决此问题,您还必须在pdf文件中包含代码页。这是通过向Font对象添加 / Encoding 并设置其差异
来完成的。对于<!>“H <!>#8364; llo World!<!>例如,这个Font-Object可以工作:
5 0 obj
<<
/F1
<<
/Type /Font
/Subtype /Type1
/BaseFont /Times-Roman
/Encoding
<<
/Type /Encoding
/Differences [ 1 /H /Euro /l /o /space /W /r /d /exclam ]
>>
>>
>>
endobj
我用这段代码生成它:
ObjectOffsets.push_back(stream->tellp()); // xrefs entry
(*stream) << ObjectCounter++ << " 0 obj \n<<\n";
int fontid = 1;
for(std::list<std::string>::iterator i = Fonts.begin(); i != Fonts.end(); i++)
{
(*stream) << " /F" << fontid++ << " << /Type /Font /Subtype /Type1 /BaseFont /" << *i;
(*stream) << " /Encoding << /Type /Encoding /Differences [ 1 \n";
for(std::vector<wchar_t>::iterator j = Codepage[*i].begin(); j != Codepage[*i].end(); j++)
(*stream) << " /" << GlyphName(*j) << "\n";
(*stream) << " ] >>";
(*stream) << " >> \n";
}
(*stream) << ">>\n";
(*stream) << "endobj \n\n";
请注意,我使用全局字体寄存器 - 我在整个pdf文档中使用相同的字体名称/ F1,/ F2,....所有页面的 / Resources 条目中都引用了相同的font-register对象。如果你这样做的方式不同(例如你每页使用一个字体寄存器) - 你可能必须根据你的情况调整代码......
那么如何找到字形的名称(/€为<!>“; <!>#8364; <!>;; / exclam为<!>”; <!>;等等)?在上面的代码中,这是通过简单地调用<!> quot; GlyphName(* j)<!>“;来完成的。我使用
中的列表中的BASH脚本生成了此方法http://www.jdawiseman.com/papers/trivia/character- entities.html
它看起来像这个
const std::string GlyphName(wchar_t UnicodeCodepoint)
{
switch(UnicodeCodepoint)
{
case 0x00A0: return "nonbreakingspace";
case 0x00A1: return "exclamdown";
case 0x00A2: return "cent";
...
}
}
主要问题我保持开放状态,只要您使用相同字体的最多254个不同字符,此才有效。要使用超过254个不同的字符,您必须为相同的字体创建多个代码页。
在pdf中,不同的代码页由不同的字体表示,所以要在代码页之间切换,你必须切换字体,这理论上可能会使你的pdf上升很多,但我一个人可以忍受这个...
我不是PDF专家,(正如Ferruccio所说)Adobe的PDF规范应该告诉你一切,但是脑子里出现了一个想法:
您确定使用的字体支持您需要的所有字符吗?
在我们的应用程序中,我们从HTML页面(带有第三方库)创建PDF,我们遇到了西里尔字符的问题......