在Unicode的PDF(Unicode in PDF)

2019-06-18 12:02发布

我的程序产生的要求相对简单的PDF文档,但我在使用Unicode字符,像汉字或奇怪的数学符号的麻烦。 要编写的PDF正常字符串,你把它放在括号:

(something)

也有逃避与八进制码字符的选项:

(\527)

但这只是上升到512个字符。 你如何编码或转义字符更高? 我已经看到了字节流和十六进制编码字符串的引用,但没有一个我读过的引用似乎不愿意告诉我如何真正做到这一点。


编辑:或者,指向我一个很好的Java PDF库,会为我做的工作。 目前我使用的一个是版本gnujpdf的(我已经在固定的几个错误,因为原作者似乎已经AWOL),它允许你对编程的AWT图形界面,最好更换任何应该做的相同。

该方案似乎不是HTML - > PDF,或基于段落和箱子的纲领性模型,感觉很像HTML。 iText的是后者的例子。 这意味着重写我现有的代码,我不相信他们会给我摆出同样的灵活性。


编辑2:我没有意识到之前,但iText库有一个Graphics2D的API,似乎完全处理Unicode的,所以这是我将使用。 虽然它不是一个问题的答案是问,它解决了这个问题对我来说。


编辑3:iText的工作很好地为我。 我猜的教训是,当面对的东西,似乎毫无意义难,找人谁知道比你更多。

Answer 1:

简单的答案是,有没有简单的答案。 如果你看看PDF规范,你会看到一个整章 - 而且是漫长的,在这 - 致力于文字的显示机制。 我实现所有的PDF支持,为我公司,并处理文本是迄今为止运动的最复杂的部分。 您发现该解决方案 - 使用第三方库,为你做的工作 - 确实是最好的选择,除非你有你的PDF文件非常具体的,特殊用途的要求。



Answer 2:

在第3章的PDF参考,这是他们说的关于Unicode是什么:

文本串被编码在任何PDFDocEncoding的或Unicode字符编码。 PDFDocEncoding的是ISO拉丁1编码的超集,在附录D的Unicode是记录由Unicode Consortium Unicode标准被描述(见参考文献)。 对于以Unicode编码的文本字符串中,前两个字节必须254后跟255这两个字节表示Unicode字节顺序标记,U + FEFF,表明该字符串在UTF-16BE(大端)编码方案编码Unicode标准规定。 (这种机制将不让开始使用PDFDocEncoding的与两个字符的字符串刺ydieresis,这是不太可能的词或短语的一个有意义的开头)。



Answer 3:

Algoman的答案是的很多事情。 你可以用一个Unicode的PDF文档在它的,它不是一个火箭科学,但它需要一些工作。 是的,他是正确的,使用一种字体超过255个字符,你必须创建一个复合字体(的CIDFont)PDF对象。 然后你只提到要作为的CIDFont的DescendatFont进入实际使用的TrueType字体。 诀窍是,在这之后,你必须使用字体的标志符号索引 ,而不是字符代码。 为了得到这个指数映射,你必须分析cmap字体的部分-获得与字体的内容GetFontData功能,并采取双手TTF规范。 就是这样! 我只是做了它,现在我有一个Unicode PDF!

解析示例代码cmap节是在这里: https://support.microsoft.com/en-us/kb/241020

是的,不要忘记/ ToUnicode条目@ user2373071指出或用户将无法搜索您的PDF或复制的文本。



Answer 4:

作为dredkin指出的那样,你必须使用的字形,而不是指数的Unicode字符值的页面内容流。 这足以显示PDF Unicode文本,但Unicode文本将无法搜索。 为了使文本搜索的或有它的复制/粘贴工作,你还需要包括/ ToUnicode流。 这应该流文件的实际Unicode字符转换中的每一个字形。



Answer 5:

查看PDF规范的附录d(页995)。 有字体和字符集的PDF消费者应用程序预先定义的数量有限。 要显示其他字符,你需要嵌入包含它们的字体。 也优选的是仅嵌入字体的一个子集,仅包含所需的字符,为了减小文件大小。 我也努力在PDF显示Unicode字符,这是一个重大的麻烦。

退房PDFBox的或iText的。

http://www.adobe.com/devnet/pdf/pdf_reference.html



Answer 6:

现在我已经在这个问题上工作了几天,我了解到的是,Unicode的是PDF(一样好)是不可能的。 使用2字节字符只描述的方式与底座CID-字体的作品。

貌似,CID-字体是PDF格式的内部构造和他们是不是真的在这个意义上的字体 - 他们似乎更喜欢图形的子程序,可以通过解决这些问题来调用(16位地址)。

因此,在PDF 直接使用unicode

  1. 你将不得不普通字体转换为CID-字体,这可能是极其困难 - 你必须从原始字体图形程序,提取字符规格等(?)
  2. 你不能使用CID-字体像正常的字体 - 你不能加载或缩放它们加载的方式和规模的普通字体
  3. 同时,2个字节的字符甚至不包括完整的Unicode空间

恕我直言,这点使其绝对不可行的直接使用unicode。



我在做什么,而不是现在以下列方式间接使用人物是:对于每一个字体,我产生一个代码页(和查找表的快速查找) -在C ++中,这将是这样的

std::map<std::string, std::vector<wchar_t> > Codepage;
std::map<std::string, std::map<wchar_t, int> > LookupTable;

然后,每当我想要把某些Unicode字符串在页面上,我重复它的字符,看看他们在查找表 - 如果它们是新的,我将它们添加到这样的代码页:

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€LLO世界!” 有可能成为<01020303040506040703080905>现在你可以把这个字符串到PDF并将它打印出来,用TJ运营商像往常一样...

但你现在有一个问题:PDF不知道你的意思是“H”,由一个01为了解决这个问题,还必须包括在PDF文件中的代码页。 这是通过添加一个/编码的字体对象并设置其差异进行

对于 “H€LLO世界!” 例如,这种字体,对象将工作:

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";

请注意,我用的是全局字体注册-我用同样的字体名称/ F1,/ F2,...在整个PDF文档。 相同的字体注册对象在所有页面的/资源输入引用。 如果你这样做不一样(例如使用每页一个字体注册) -你可能需要的代码适应你的情况...

那么,你如何找到字形的名称(/欧元“€”的,/ exclam“!”等)? 在上面的代码,这是通过简单地调用“提供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了不少,但我为一体,可以忍受的...



Answer 7:

我不是一个PDF专家,(如费鲁乔说),Adobe的PDF格式的规格应该告诉你一切,但一个念头在我脑海中突然出现了:

你确定你使用的是支持你所需要的所有字符的字体?

在我们的应用中,我们创建从HTML页面(与第三方库)PDF,我们有这个问题,西里尔字母...



文章来源: Unicode in PDF