Исправление иероглифов в полях форм PDFBox с подмножественными шрифтами
Узнайте, почему поля форм Apache PDFBox отображают иероглифы с подмножественными шрифтами TrueType и найдите проверенные решения для исправления проблем с отображением символов в PDF-формах.
Почему поля формы отображают бессмысленные символы при использовании подмножественных шрифтов TrueType в Apache PDFBox?
Я использую Apache PDFBox для создания PDF с полями формы и встраивания пользовательского шрифта TrueType (FreeSans.ttf) с включенным подмножествением. Однако при установке значения в текстовое поле вместо фактического текста отображаются бессмысленные символы.
Окружение:
- Версии Apache PDFBox: 2.x, 3.x
- Затронутые шрифты: Любые шрифты, загруженные из внешнего источника, кроме 14 стандартных шрифтов
Пример кода:
package org.example;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import java.io.InputStream;
public class Main {
public static void main(String[] args) {
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
// Загрузка внешнего шрифта TrueType с включенным подмножеством
PDType0Font font;
try (InputStream fontStream = Main.class.getResourceAsStream("/FreeSans.ttf")) {
if (fontStream == null) {
throw new RuntimeException("FreeSans.ttf не найден в ресурсах");
}
font = PDType0Font.load(document, fontStream, true);
}
// Добавление шрифта в ресурсы формы
PDResources formResources = new PDResources();
String fontResourceName = formResources.add(font).getName();
acroForm.setDefaultResources(formResources);
acroForm.setDefaultAppearance("/" + fontResourceName + " 12 Tf 0 0 0 rg");
// Создание текстового поля со значением
PDTextField textField = new PDTextField(acroForm);
textField.setPartialName("testField");
textField.setDefaultAppearance("/" + fontResourceName + " 12 Tf 0 0 0 rg");
// Установка прямоугольника виджета и добавление на страницу
textField.getWidgets().get(0).setRectangle(new PDRectangle(100, 700, 200, 20));
textField.getWidgets().get(0).setPage(page);
page.getAnnotations().add(textField.getWidgets().get(0));
acroForm.getFields().add(textField);
textField.setValue("Hello, this is a filled form field!");
acroForm.setNeedAppearances(false);
acroForm.refreshAppearances();
document.save("output.pdf");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Вывод в консоль:
WARNING: Using fallback font LiberationSans for CID-keyed TrueType font FreeSans
WARNING: widget of field testField has no rectangle, no appearance stream created
Что может вызывать эту проблему, и как правильно отображать текст в полях формы при использовании подмножественных шрифтов TrueType с Apache PDFBox?
Содержание
- Основное понимание проблемы
- Подмножества шрифтов и CID-ключевые шрифты
- Проблемы генерации потока внешнего вида
- Решения и обходные пути
- Лучшие практики для шрифтов полей формы
- Пример исправленного кода
Основное понимание проблемы
Основная проблема связана с тем, как PDFBox обрабатывает кодировку шрифтов и генерацию потока внешнего вида для полей форм. При использовании подмножеств TrueType шрифтов, PDFBox преобразует каждый кодовую точку Unicode в имя и ищет это имя в карте, сгенерированной на основе инвертированной кодировки шрифта. Этот процесс может нарушаться при включенном подмножестве, что приводит к неправильному отображению символов.
Сообщения об ошибках в выводе консоли выявляют конкретные проблемы:
- “Using fallback font LiberationSans for CID-keyed TrueType font FreeSans” - PDFBox переходит к использованию другого шрифта, так как не может обработать кодировку исходного шрифта
- “widget of field testField has no rectangle, no appearance stream created” - Генерация потока внешнего вида не удалась, что критично для отображения полей форм
Подмножества шрифтов и CID-ключевые шрифты
При встраивании шрифта PDFBox может встроить весь шрифт или создать его подмножество, включающее только глифы, используемые в документе. Однако с CID-ключевыми TrueType шрифтами этот процесс создания подмножества вызывает проблемы:
Создание подмножеств шрифтов может повредить карту кодировки, от которой зависит PDFBox для отображения текста в полях форм. Процесс создания подмножеств изменяет внутреннюю структуру шрифта, что может нарушить сопоставление Unicode с глифами, от которого зависят поля форм для правильного отображения текста.
Согласно документации PDFBox, метод addToSubset и связанные функции создания подмножеств предназначены для общего отображения текста в PDF, но они не работают хорошо с полями AcroForm, поскольку поля требуют другого подхода к обработке кодировки шрифтов и потоков внешнего вида.
Проблемы генерации потока внешнего вида
Поля форм в PDF требуют наличия потоков внешнего вида для правильного отображения. Эти потоки содержат визуальное представление текущего значения поля. Когда PDFBox генерирует потоки внешнего вида для полей форм с использованием подмножеств шрифтов, могут создаваться поврежденные потоки из-за проблем с кодировкой.
Проблема возникает потому, что:
- PDFBox создает потоки внешнего вида, отображая текст с указанным шрифтом
- При использовании подмножеств шрифтов, сопоставление кодировки становится неполным или неправильным
- Поток внешнего вида содержит неверные ссылки на глифы
- Когда просмотрщик PDF отображает поле, он показывает неправильные символы
Как отмечено в обсуждении на Stack Overflow, это известная проблема, которую разработчики PDFBox признали.
Решения и обходные пути
Вот несколько эффективных решений для исправления проблемы с бессмысленными символами:
1. Отключите создание подмножеств для полей форм
Наиболее надежное решение - отключить создание подмножеств при загрузке шрифтов для использования в AcroForm. Согласно документации исходного кода PDFBox, конструктор с тремя параметрами в PDType0Font.load явно указывает не использовать создание подмножеств для шрифтов, используемых в AcroForm.
// Загрузка внешнего TrueType шрифта с ОТКЛЮЧЕННЫМ созданием подмножеств
PDType0Font font = PDType0Font.load(document, fontStream, false);
2. Включите NeedAppearances
Установите setNeedAppearances(true), чтобы заставить PDFBox правильно генерировать потоки внешнего вида:
acroForm.setNeedAppearances(true);
3. Настройте сопоставление шрифтов
Правильное сопоставление шрифтов может решить многие проблемы, связанные со шрифтами. Как упоминается в FAQ PDFBox, вы можете настроить сопоставление шрифтов через FontMapperImpl.java или использовать файл PDFBox_External_Fonts.properties.
4. Используйте стандартные 14 шрифтов, когда это возможно
Для производственных форм рассмотрите возможность использования стандартных 14 PDF шрифтов (Helvetica, Times, Courier и т.д.), которые нatively поддерживаются просмотрщиками PDF и не требуют встраивания.
Лучшие практики для шрифтов полей форм
- Избегайте создания подмножеств для шрифтов, используемых в полях AcroForm
- Тщательно тестируйте с разными типами шрифтов и наборами символов
- Используйте стандартные PDF шрифты по возможности для лучшей совместимости
- Устанавливайте правильные прямоугольники полей для обеспечения генерации потоков внешнего вида
- Настраивайте резервные шрифты для случаев, когда основной шрифт нельзя использовать
Пример исправленного кода
Вот исправленная версия вашего кода, которая решает проблему с бессмысленными символами:
package org.example;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDResources;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
import org.apache.pdfbox.pdmodel.interactive.form.PDTextField;
import java.io.InputStream;
public class Main {
public static void main(String[] args) {
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
// Загрузка внешнего TrueType шрифта с ОТКЛЮЧЕННЫМ созданием подмножеств
PDType0Font font;
try (InputStream fontStream = Main.class.getResourceAsStream("/FreeSans.ttf")) {
if (fontStream == null) {
throw new RuntimeException("FreeSans.ttf не найден в ресурсах");
}
// ИСПРАВЛЕНИЕ: Установите создание подмножеств в false для использования в AcroForm
font = PDType0Font.load(document, fontStream, false);
}
// Добавление шрифта в ресурсы формы
PDResources formResources = new PDResources();
String fontResourceName = formResources.add(font).getName();
acroForm.setDefaultResources(formResources);
acroForm.setDefaultAppearance("/" + fontResourceName + " 12 Tf 0 0 0 rg");
// Создание текстового поля со значением
PDTextField textField = new PDTextField(acroForm);
textField.setPartialName("testField");
textField.setDefaultAppearance("/" + fontResourceName + " 12 Tf 0 0 0 rg");
// Установка прямоугольника виджета и добавление на страницу
textField.getWidgets().get(0).setRectangle(new PDRectangle(100, 700, 200, 20));
textField.getWidgets().get(0).setPage(page);
page.getAnnotations().add(textField.getWidgets().get(0));
acroForm.getFields().add(textField);
textField.setValue("Привет, это заполненное поле формы!");
// ИСПРАВЛЕНИЕ: Включите needAppearances для правильного отображения полей формы
acroForm.setNeedAppearances(true);
acroForm.refreshAppearances();
document.save("output.pdf");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Основные исправления в этом исправленном коде:
- Изменено
PDType0Font.load(document, fontStream, true)наPDType0Font.load(document, fontStream, false)для отключения создания подмножеств - Изменено
acroForm.setNeedAppearances(false)наacroForm.setNeedAppearances(true)для включения правильной генерации потоков внешнего вида
Источники
- Apache PDFBox FAQ - Сопоставление шрифтов
- Stack Overflow - Проблема с интервалом текста TrueType при заполнении формы Apache PDFBox
- API PDFBox 2.0.1 - PDTrueTypeFont
- Документация по обработке шрифтов Apache PDFBox
- PDFBOX-3332 - Проблема с интервалом текста TrueType при заполнении формы
Заключение
Проблема с бессмысленными символами в полях форм Apache PDFBox при использовании подмножеств TrueType шрифтов вызвана проблемами с кодировкой шрифтов во время генерации потока внешнего вида. Ключевые решения - отключить создание подмножеств шрифтов для использования в AcroForm и включить правильную генерацию потока внешнего вида. Следуя изложенным выше лучшим практикам и реализуя исправления в коде, вы можете гарантировать, что поля формы будут отображать текст правильно независимо от используемого шрифта. Всегда тестируйте свои PDF формы с разными шрифтами и просмотрщиками PDF для обеспечения совместимости на разных платформах.