PDFBox неправильно отображает шрифт Simsun (китайский) - PullRequest
0 голосов
/ 17 апреля 2019

Контекст

Я пишу код Java, который заполняет PDF-формы, используя PDFBox, с некоторыми пользовательскими данными.Некоторые входные данные написаны на китайском языке.

Когда я сгенерировал PDF, у меня не было ошибок в журналах, но отображаемый текст абсолютно не совпадает.

То, что у меня сейчас есть

Вот что я делаю:

  • В файле PDF я указал шрифт SimSun для поля с помощью Adobe Pro.Этот шрифт обрабатывает упрощенные китайские символы.

  • У меня установлен шрифт SimSun на моем сервере.

  • PDFBox не отображает никаких ошибок (еслиЯ удаляю шрифт SimSun со своего сервера, затем резервный PDFBox для другого шрифта, который не может отображать символы).Так что я думаю, что он может найти шрифт и использовать его.

Что я пробовал

Мне удалось сделать эту работу, но мне пришлось вручную загрузить шрифтв коде и добавьте его в PDF (см. примеры ниже).Но это не решение, так как это означает, что мне придется каждый раз загружать шрифт и добавлять его в PDF.Я также должен был бы сделать то же самое для многих других языков.

Насколько я понял, PDFBox должен иметь возможность использовать любые шрифты, установленные на сервере.

Ниже приведен тестовый класс, которыйпробует 3 разных подхода.Пока работает только последний:

Классическое поколение

Просто поместите китайские иероглифы в текстовое поле, ничего не меняя.Символы отображаются неправильно (некоторые из них отсутствуют, а отображаемые не соответствуют вводу).

rendering-issue

Генерация со встроенным шрифтом

Попробуйте внедрить шрифт SimSun в PDF-файл с помощью метода PDResource.add(font).Результат такой же, как и в первом методе.

Вставить шрифт и использовать его

Я встраиваю шрифт SimSun и Я также перезаписываю шрифт, используемый в TextField , чтобыиспользуйте шрифт SimSun, который я только что добавил.Этот подход работает.

correct-render

После нескольких чтений я обнаружил, что проблема может быть связана с версией используемого мной шрифта.Windows 8 (которую я использую для создания формы) использует v5.04 шрифта Simsun .

Я использую v2.10 на моем ноутбуке и на моих серверах.основанный на Linux (я не могу найти v5.04).

Однако я не знаю:

  • Если проблема действительно в этом.
  • Если у меня есть право использовать этот шрифт, так как он разработан Microsoft (и Apple).
  • Где найти его последнюю версию.

Я пытался использоватьдругой шрифт, но:

Так что, если у кого-то есть идея о том, как сделать это без необходимости вставлять и изменять имя шрифта в коде, это будетбудьте великолепны!

Вот PDF-файл, который я использовал , и код, который тестирует 3 метода, о которых я говорил.TextField в PDF-файле называется 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);
    }

}
...