我的程序根据要求生成相对简单的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。

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

我现在已经在这个主题上工作了几天,而且我学到的是pdf中unicode(一样好)是不可能的。使用双字节字符时,基座所描述的方式仅适用于CID-Fonts。

看起来,CID-Fonts是一个pdf内部结构,它们在这个意义上并不是真正的字体 - 它们看起来更像是图形子程序,可以通过寻址它们来调用它们(使用16位地址)。 / p>

所以直接在pdf 中使用unicode

  1. 你必须将普通字体转换为CID字体,这可能非常困难 - 你必须从原始字体(?)生成图形例程,提取字符指标等。
  2. 您不能像普通字体一样使用CID字体 - 您无法按照加载和缩放普通字体的方式加载或缩放它们
  3. 另外,2字节字符甚至不包括完整的Unicode空间
  4. 恕我直言,这些要点使得直接使用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,我们遇到了西里尔字符的问题......

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top