代码之家  ›  专栏  ›  技术社区  ›  Brad

用Java PDFBOX库编写俄罗斯方块PDF

  •  11
  • Brad  · 技术社区  · 15 年前

    我正在使用一个叫Java的库 PDFox 试图将文本写入PDF。它非常适合英语文本,但是当我试图在pdf中写俄语文本时,这些字母看起来很奇怪。问题似乎出在使用的字体上,但我对此不太确定,所以我希望有人能指导我完成这一步。以下是重要的代码行:

    PDTrueTypeFont font = PDTrueTypeFont.loadTTF( pdfFile, new File( "fonts/VREMACCI.TTF" ) );  // Windows Russian font imported to write the Russian text.
    font.setEncoding( new WinAnsiEncoding() );  // Define the Encoding used in writing.
    // Some code here to open the PDF & define a new page.
    contentStream.drawString( "отделом компьютерной" ); // Write the Russian text.
    

    winansiencoding源代码是: Click here

    ----------------2009年11月18日编辑

    经过一些调查,我现在确定这是一个编码问题,可以通过使用名为 字典编码 .

    我不知道如何使用它,但我一直在尝试以下方法:

    COSDictionary cosDic = new COSDictionary();
    cosDic.setString( COSName.getPDFName("Ercyrillic"), "0420 " ); // Russian letter.
    font.setEncoding( new DictionaryEncoding( cosDic ) );
    

    这不起作用,因为似乎我在用错误的方式填充字典,当我用这个写一个PDF页面时,它看起来是空白的。

    编码源代码的字典是: Click here

    6 回复  |  直到 6 年前
        1
  •  0
  •   ivict    7 年前

    尝试使用此结构:

    PDFont font = PDType0Font.load( pdfFile, new File( "fonts/VREMACCI.TTF" ) );  // Windows Russian font imported to write the Russian text.
    // Some code here to open the PDF & define a new page.
    contentStream.beginText();
    contentStream.setFont(font, 12);
    contentStream.showText( "отделом компьютерной" ); // Write the Russian text.
    contentStream.endText();
    
        2
  •  5
  •   plinth    12 年前

    长话短说——为了从TrueType字体以PDF格式进行Unicode输出,输出必须包含大量详细且看似多余的信息。归根结底是这样的-在TrueType字体中,字形存储为字形ID。这些glyph id与特定的unicode字符相关联(而iirc,unicode glyph内部可能指几个代码点,如_指e和一个尖锐的重音,我的记忆是模糊的)。除了说明字符串中的utf16be值与TrueType字体中的字形ID之间存在映射,以及utf16be值与Unicode之间的映射(即使是标识),PDF实际上不支持Unicode。

    • 子类型0的字体字典
      • 具有下面描述的条目的DescendantFonts数组
      • 将utf16be值映射到unicode的to unicode项
      • 设置为identity-h的编码

    我自己工具上的一个单元测试的输出如下所示:

    13 0 obj
    << 
       /BaseFont /DejaVuSansCondensed 
       /DescendantFonts [ 4 0 R  ]   
       /ToUnicode 14 0 R 
       /Type /Font 
       /Subtype /Type0 
       /Encoding /Identity-H 
    >> endobj
    
    14 0 obj
    << /Length 346 >> stream
    /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
    

    endstream%注意流的格式错误

    • 子类型cidfonttype2的字体字典
      • 一个CysSyTimeNoFo
      • 字体描述符
      • DW和W
      • 从字符ID映射到字形ID的cidtogidmap

    这是同一个测试中的一个-这是DescendantFonts数组中的对象:

    4 0 obj
    << 
       /Subtype /CIDFontType2 
       /Type /Font 
       /BaseFont /DejaVuSansCondensed 
       /CIDSystemInfo 8 0 R 
       /FontDescriptor 9 0 R 
       /DW 1000 
       /W 10 0 R 
       /CIDToGIDMap 11 0 R 
    >>
    
    8 0 obj
    << 
       /Registry (Adobe)
       /Ordering (UCS)
       /Supplement 0 
    >>
    endobj
    

    我为什么要告诉你这个?它与pdfbox有什么关系?就这样:PDF格式的Unicode输出,坦率地说,是一个巨大的麻烦。Acrobat是在Unicode出现之前开发出来的,没有Unicode的CJK编码从一开始就很痛苦(我知道-那时我在Acrobat上工作)。后来又添加了Unicode支持,但它真的让人觉得它被利用了。我们希望您只需要说/encoding/unicode,并使用以thorn和y-dieresis字符开头的字符串,然后就可以了。没有这样的运气。如果你不把每一个细节都放进去(真的,还有Acrobat,嵌入一个PostScript程序来翻译成Unicode?WTH?)你在Acrobat上得到一个空白页。我发誓,我不是在编这个。

    在这一点上,我为一个单独的公司编写了PDF生成工具(现在是.NET,所以它对您没有帮助),我把隐藏所有这些胡言乱语作为设计目标。所有文本都是Unicode的-如果您只使用那些与WinAnsi相同的字符代码,那么这就是您所得到的。用其他的东西,你就可以得到其他的东西。如果PDFBox能为你工作,我会很惊讶的-这是一个严重的麻烦。

        3
  •  1
  •   taras Hardik Kamdar    6 年前

    解决方案非常简单。

    1)必须找到与要显示的字符兼容的字体。
    2)本地下载字体的.ttf文件。
    3)从应用程序加载字体

    例如,如果要使用希腊字符,则必须执行以下操作:

    content = new PDPageContentStream(document, page);
    pdfFont = PDType0Font.load( document, new File( "arialuni.ttf" ) )
    content.setFont(pdfFont, fontSize);
    
        4
  •  0
  •   PhiLho    15 年前

    可能需要编写俄语编码类,它应该像 WinAnsiEncoding 一个,我想。
    现在,我不知道该放什么!

    或者,如果这不是您已经做的,也许您应该用UTF-8编码源文件并使用默认编码。
    我看到了一些与从现有PDF文件中提取俄语文本相关的消息(当然是使用pdfbox),但我不知道输出是否相关。
    您还可以写入pdfbox邮件列表。

        5
  •  0
  •   Kevin Day    15 年前

    测试这是否是一个编码问题应该非常容易(只需切换到UTF16编码)。

    我假设您已经尝试使用编辑器或其他具有vremacci字体的工具,并确认它显示了您期望的方式?

    您可能想尝试在IText中执行相同的操作,只是为了了解问题是否与pdfbox库本身相关…如果您的主要目标是生成PDF文件,IText无论如何可能是更好的解决方案。

    编辑-对评论的长答复:

    好的-抱歉在编码问题上来回…您的核心问题(您可能已经知道)是,写入内容流的字节的编码与用于查找glyph的编码不同。现在我会尽力帮助你:

    我看了一下pdfbox中的字典编码类,它看起来很不直观…“字典”是一个PDF字典。所以您基本上需要做的是创建一个PDF字典对象(我认为pdfbox将其称为cosObject类型),然后向它添加条目。

    字体的编码在PDF中定义为词典(见上述规范第266页)。字典包含基本编码名称,以及可选的差异数组。从技术上讲,差异数组不应该与真正的字体一起使用(尽管我已经看到它在某些情况下使用过——但是不要使用它)。

    然后,您将为CMAP指定一个用于编码的条目。这个CMAP将是您字体的编码。

    我的建议是取一个现有的PDF文件来做你想做的,然后得到一个字体的字典结构的转储,这样你就可以看到它是什么样子的。

    这绝对不适合胆小的人。我可以提供一些帮助-如果你需要一个字典转储,给我一个带PDF样本的超链接,我将运行它通过我在IText开发中使用的一些算法(我是IText文本提取子系统的维护者)。

    编辑- 11/17 / 09

    好的-这是来自russian.pdf文件的字典转储(子字典以缩进方式列出,并按它们在包含字典中出现的顺序排列):

    (/CropBox=[0, 0, 595, 842], /Parent=Dictionary of type: /Pages, /Type=/Page, /Contents=[209 0 R, 210 0 R, 211 0 R, 214 0 R, 215 0 R, 216 0 R, 222 0 R, 223 0 R], /Resources=Dictionary, /MediaBox=[0, 0, 595, 842], /StructParents=0, /Rotate=0)
        Subdictionary /Parent = (/Type=/Pages, /Count=6, /Kids=[195 0 R, 1 0 R, 3 0 R, 5 0 R, 7 0 R, 9 0 R])
        Subdictionary /Resources = (/ExtGState=Dictionary, /ProcSet=[/PDF, /Text], /ColorSpace=Dictionary, /Font=Dictionary, /Properties=Dictionary)
            Subdictionary /ExtGState = (/GS0=Dictionary of type: /ExtGState)
                Subdictionary /GS0 = (/OPM=1, /op=false, /Type=/ExtGState, /SA=false, /OP=false, /SM=0.02)
            Subdictionary /ColorSpace = (/CS0=[/ICCBased, 228 0 R])
            Subdictionary /Font = (/C2_1=Dictionary of type: /Font, /C2_2=Dictionary of type: /Font, /C2_3=Dictionary of type: /Font, /C2_4=Dictionary of type: /Font, /TT2=Dictionary of type: /Font, /TT1=Dictionary of type: /Font, /TT0=Dictionary of type: /Font, /C2_0=Dictionary of type: /Font, /TT3=Dictionary of type: /Font)
                Subdictionary /C2_1 = (/DescendantFonts=[243 0 R], /BaseFont=/LDMIEC+TimesNewRomanPS-BoldMT, /Type=/Font, /Subtype=/Type0, /Encoding=/Identity-H, /ToUnicode=Stream)
                Subdictionary /C2_2 = (/DescendantFonts=[233 0 R], /BaseFont=/LDMIBO+TimesNewRomanPSMT, /Type=/Font, /Subtype=/Type0, /Encoding=/Identity-H, /ToUnicode=Stream)
                Subdictionary /C2_3 = (/DescendantFonts=[224 0 R], /BaseFont=/LDMIHD+TimesNewRomanPS-ItalicMT, /Type=/Font, /Subtype=/Type0, /Encoding=/Identity-H, /ToUnicode=Stream)
                Subdictionary /C2_4 = (/DescendantFonts=[229 0 R], /BaseFont=/LDMIDA+Tahoma, /Type=/Font, /Subtype=/Type0, /Encoding=/Identity-H, /ToUnicode=Stream)
                Subdictionary /TT2 = (/LastChar=58, /BaseFont=/LDMIFC+TimesNewRomanPS-BoldMT, /Type=/Font, /Subtype=/TrueType, /Encoding=/WinAnsiEncoding, /Widths=[250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 333], /FontDescriptor=Dictionary of type: /FontDescriptor, /FirstChar=32)
                    Subdictionary /FontDescriptor = (/Type=/FontDescriptor, /StemV=136, /Descent=-216, /FontWeight=700, /FontBBox=[-558, -307, 2000, 1026], /CapHeight=656, /FontFile2=Stream, /FontStretch=/Normal, /Flags=34, /XHeight=0, /FontFamily=Times New Roman, /FontName=/LDMIFC+TimesNewRomanPS-BoldMT, /Ascent=891, /ItalicAngle=0)
                Subdictionary /TT1 = (/LastChar=187, /BaseFont=/LDMICP+TimesNewRomanPSMT, /Type=/Font, /Subtype=/TrueType, /Encoding=/WinAnsiEncoding, /Widths=[250, 0, 0, 0, 0, 833, 778, 0, 333, 333, 0, 0, 250, 333, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278, 0, 564, 0, 444, 0, 722, 667, 667, 722, 611, 556, 0, 722, 333, 389, 0, 611, 889, 722, 722, 556, 0, 667, 556, 611, 0, 722, 944, 0, 722, 0, 333, 0, 333, 0, 500, 0, 444, 500, 444, 500, 444, 333, 500, 500, 278, 0, 500, 278, 778, 500, 500, 500, 0, 333, 389, 278, 500, 500, 722, 0, 500, 444, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 500], /FontDescriptor=Dictionary of type: /FontDescriptor, /FirstChar=32)
                    Subdictionary /FontDescriptor = (/Type=/FontDescriptor, /StemV=82, /Descent=-216, /FontWeight=400, /FontBBox=[-568, -307, 2000, 1007], /CapHeight=656, /FontFile2=Stream, /FontStretch=/Normal, /Flags=34, /XHeight=0, /FontFamily=Times New Roman, /FontName=/LDMICP+TimesNewRomanPSMT, /Ascent=891, /ItalicAngle=0)
                Subdictionary /TT0 = (/LastChar=55, /BaseFont=/LDMIBN+TimesNewRomanPS-BoldItalicMT, /Type=/Font, /Subtype=/TrueType, /Encoding=/WinAnsiEncoding, /Widths=[250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 250, 0, 500, 500, 500, 0, 0, 0, 0, 500], /FontDescriptor=Dictionary of type: /FontDescriptor, /FirstChar=32)
                    Subdictionary /FontDescriptor = (/Type=/FontDescriptor, /StemV=116.867004, /Descent=-216, /FontWeight=700, /FontBBox=[-547, -307, 1206, 1032], /CapHeight=656, /FontFile2=Stream, /FontStretch=/Normal, /Flags=98, /XHeight=468, /FontFamily=Times New Roman, /FontName=/LDMIBN+TimesNewRomanPS-BoldItalicMT, /Ascent=891, /ItalicAngle=-15)
                Subdictionary /C2_0 = (/DescendantFonts=[238 0 R], /BaseFont=/LDMHPN+TimesNewRomanPS-BoldItalicMT, /Type=/Font, /Subtype=/Type0, /Encoding=/Identity-H, /ToUnicode=Stream)
                Subdictionary /TT3 = (/LastChar=169, /BaseFont=/LDMIEB+Tahoma, /Type=/Font, /Subtype=/TrueType, /Encoding=/WinAnsiEncoding, /Widths=[313, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 546, 0, 546, 0, 0, 546, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 929], /FontDescriptor=Dictionary of type: /FontDescriptor, /FirstChar=32)
                    Subdictionary /FontDescriptor = (/Type=/FontDescriptor, /StemV=92, /Descent=-206, /FontWeight=400, /FontBBox=[-600, -208, 1338, 1034], /CapHeight=734, /FontFile2=Stream, /FontStretch=/Normal, /Flags=32, /XHeight=546, /FontFamily=Tahoma, /FontName=/LDMIEB+Tahoma, /Ascent=1000, /ItalicAngle=0)
            Subdictionary /Properties = (/MC0=Dictionary of type: /OCMD)
                Subdictionary /MC0 = (/Type=/OCMD, /OCGs=Dictionary of type: /OCG)
                    Subdictionary /OCGs = (/Usage=Dictionary, /Type=/OCG, /Name=HeaderFooter)
                        Subdictionary /Usage = (/CreatorInfo=Dictionary, /PageElement=Dictionary)
                            Subdictionary /CreatorInfo = (/Creator=Acrobat PDFMaker 6.0 äëÿ Word)
                            Subdictionary /PageElement = (/SubType=/HF)
    

    这里有很多活动部件。您可能希望将字体中只有3或4个字符的测试文档放在一起…这里使用了很多类型1字体(除了TT字体),因此很难知道您的特定问题涉及到什么。

    (你确定你不想至少用IText试试这个吗?;-)我不是说它会奏效,只是说它可能值得一试)。

    作为参考,以上字典转储是使用com.lowagie.text.pdf.parser.pdfContentReaderTool类获取的。

        6
  •  -1
  •   daNIL    15 年前

    试试这个:

    phrase lefttitle=新短语(“____)、-__、____”,fontfactory.getfont(“Tahoma”,“CP1251”,true,25));

    这至少适用于最新的(5.0.1)IText