PDFBox无法正确呈现Simsun(中文)字体

时间:2019-04-17 16:37:17

标签: java fonts pdfbox

上下文

我正在编写一个Java代码,该代码将使用一些用户输入的PDFBox填充PDF表单。 一些输入是中文。

生成PDF时,日志中没有任何错误,但是呈现的文本绝对不相同。

我目前拥有的东西

这是我的工作:

  • 在PDF文件中,我使用Adobe Pro为该字段指定了SimSun字体。 该字体处理简体中文字符。

  • 我在服务器上安装了SimSun字体。

  • PDFBox不显示任何错误(如果我从服务器中删除SimSun字体,则PDFBox会回退到无法渲染字符的另一种字体上)。所以我想它能够找到并使用它。

我尝试过的

我能够做到这一点,但是我不得不在代码中手动加载字体并将其添加到PDF中(请参见下面的示例)。 但这不是解决方案,因为这意味着我每次都必须加载字体并将其添加为PDF。对于许多其他语言,我也必须这样做。

据我了解,PDFBox应该能够使用服务器上安装的所有字体。

下面是一个测试类,尝试3种不同的方法。到目前为止,只有最后一个有效:

经典世代

只需将中文字符放在文本字段中,而无需进行任何更改。 字符显示不正确(某些字符丢失并且显示的字符与输入不匹配)。

rendering-issue

使用嵌入字体生成

尝试使用PDResource.add(font)方法将SimSun字体嵌入PDF中。 结果与第一种方法相同。

嵌入字体并使用它

我嵌入了SimSun字体,并且我也重写了TextField中使用的字体,以使用我刚刚添加的SimSun字体。 这种方法有效。

correct-render

大量阅读后,我发现问题可能出在我使用的字体版本上。 Windows 8(我用来创建表单)uses v5.04 of Simsun font

我在笔记本电脑和服务器上都使用 v2.10 ,它们都是基于Linux的(我找不到v5.04)。

但是,我不知道:

  • 如果问题确实来自此。
  • 如果我有权使用由Microsoft(和Apple)开发的这种字体。
  • 在哪里可以找到它的最新版本。

我尝试使用其他字体,但是:

  • 我只找到支持汉字的OTF字体(而不是TTF)。
  • PDFBox不支持OTF(尚未)。 It is planed for v3.0.0

因此,如果有人对如何实现此工作而不必在代码中嵌入和更改字体名称有一个想法,那将很棒!

Here are the PDF I used和测试我所讨论的3种方法的代码。 pdf中的TextField名为comment

package org.test;

import org.apache.pdfbox.cos.COSDictionary;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.cos.COSString;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDField;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Hello world!
 */
public class App {
    private static final String SIMPLIFIED_CHINESE_STRING = "我不明白为什么它不起作用。";

    public static void main(String[] args) throws IOException {
        System.out.println("Hello World!");
        // Test 1
        classicGeneration();

        // Test 2
        generationWithEmbededFont();

        Test 3
        generationWithFontOverride();
        System.out.println("Bye!");
    }

    /**
     * Classic PDF generation without any changes to the PDF.
     */
    private static void classicGeneration() throws IOException {
        PDDocument document = loadPdf();
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();
        PDField commentField = acroForm.getField("comment");
        commentField.setValue(SIMPLIFIED_CHINESE_STRING);
        document.save(new File("result-classic-generation.pdf"));
    }

    /**
     * Trying to embed the font in the PDF. It doesn't seem to work.
     * The result is the same as classicGeneration method.
     */
    private static void generationWithEmbededFont() throws IOException {
        PDDocument document = loadPdf();
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

        PDFont font = PDType0Font.load(document, new File("/usr/share/fonts/SimSun.ttf"));
        PDResources res = acroForm.getDefaultResources();
        if (res == null) {
            res = new PDResources();
        }
        COSName fontName = res.add(font);
        acroForm.setDefaultResources(res);

        PDField commentField = acroForm.getField("comment");
        commentField.setValue(SIMPLIFIED_CHINESE_STRING);
        document.save(new File("result-with-embeded-font.pdf"));
    }

    /**
     * Embed the font in the PDF and change the font used in the TextField to use this one.
     * Here the PDF is correctly rendered and all the characters are displayed.
     * @throws IOException
     */
    private static void generationWithFontOverride() throws IOException {
        PDDocument document = loadPdf();
        PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm();

        PDField commentField = acroForm.getField("comment");

        // Load the font
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("SimSun.ttf");
        PDFont font = PDType0Font.load(document, resourceAsStream);
        PDResources res = acroForm.getDefaultResources();
        if (res == null) {
            res = new PDResources();
        }
        COSName fontName = res.add(font);
        acroForm.setDefaultResources(res);

        // Change the font used by the TextField
        COSDictionary dict = commentField.getCOSObject();
        COSString defaultAppearance = (COSString) dict.getDictionaryObject(COSName.DA);
        if (defaultAppearance != null) {
            String currentFont = dict.getString(COSName.DA);

            // Retrieve the current font size and color used for the field in order to use the same but with the new font.
            String regex = "[\\w]* ([\\w\\s]*)";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(currentFont);

            // Default font size if we fail to extract the current one
            String fontSize = " 11 Tf";

            if (matcher.find()) {
                fontSize = " " + matcher.group(1);
            }

            // Change the font of the TextField.
            dict.setString(COSName.DA, "/" + fontName.getName() + fontSize);
        }

        commentField.getCOSObject().addAll(dict);

        commentField.setValue(SIMPLIFIED_CHINESE_STRING);
        document.save(new File("result-with-font-override.pdf"));
    }

    // HELPER

    private static PDDocument loadPdf() throws IOException {
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("sample.pdf");
        return PDDocument.load(stream);
    }

}

0 个答案:

没有答案