javapdffontspdf-generationpdfbox

How to change font of the existing PDTextField in pdfbox Java?


I'm working with a pdf template that has text forms in it. My programme needs to insert text into these forms. The problem arises because the insertion text is Czech. I know that I need to install a special font to work with Czech language:

PDFont formFont = PDType0Font
        .load(doc,
              PDFGenerator.class.getResourceAsStream(
                      "/fonts/AbhayaLibre-Regular.ttf"),
              false);

PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDResources resources = acroForm.getDefaultResources();
String fontName = resources.add(formFont).getName();

Font is set to field this way:

PDTextField template = (PDTextField) acroForm.getField("main_text");
template.setMultiline(template.isMultiline());
template.setDefaultAppearance(template.getDefaultAppearance()
                                      .replaceAll("/\\w+", "/"
                                                           + fontName));
template.setValue("Ahoj světe");

This code works, for example, for templates created in LibreOffice. But for templates created in https://www.sejda.com/pdf-forms, it crashes with an encoding error

Exception in thread "main" java.lang.IllegalArgumentException: U+011B ('ecaron') is not available in the font Helvetica, encoding: WinAnsiEncoding at org.apache.pdfbox.pdmodel.font.PDType1Font.encode(PDType1Font.java:410) at org.apache.pdfbox.pdmodel.font.PDFont.encode(PDFont.java:337)

I can create new PDTextField in programm, set font for it and rectangular. Then program works correctly

PDFont formFont = PDType0Font
        .load(doc,
              PDFGenerator.class.getResourceAsStream(
                      "/fonts/AbhayaLibre-Regular.ttf"),
              false);


PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDResources resources = acroForm.getDefaultResources();
final String fontName = resources.add(formFont).getName();

PDPage page = doc.getPage(0);
PDTextField template = (PDTextField) acroForm.getField("main_text");
PDTextField implementation = new PDTextField(acroForm);

implementation.setPartialName(
        template.getPartialName() + "_generated");
implementation.setMultiline(template.isMultiline());
implementation.setDefaultAppearance(template.getDefaultAppearance()
                                            .replaceAll("/\\w+", "/"
                                                                 + fontName));
implementation.getWidgets().get(0).setRectangle(
        template.getWidgets().get(0).getRectangle());
implementation.getWidgets().get(0).setPage(page);
implementation.setValue("Ahoj světe");
page.getAnnotations().add(implementation.getWidgets().get(0));
acroForm.getFields().add(implementation);

template.setReadOnly(true);
template.setValue(null);

doc.save("1.pdf");

I assume this behaviour is due to DefaultAppearance overlapping somewhere with the value set by the pdf editor, but I can't figure out where to look for that. Is my reasoning correct? What is the correct way to handle foreign languages in the pdfbox form body?

Put the files on the disc. https://drive.google.com/drive/folders/14Ko_55T9wyYaC6rj7uoH5AUYcTOgN7JC?usp=sharing *_new_font - files created after

template.setDefaultAppearance(template.getDefaultAppearance()
                                      .replaceAll("/\\w+", "/"
                                                           + fontName));

execution.

I tried to debug default appearance changing and got that the default appearance of field is changed after setDefaultAppearance, while default appearance of widget may not be changed.

System.out.println("Old field default appearance: "
                   + template.getDefaultAppearance());
System.out.println("Old widget default appearance: "
                   + template.getWidgets().get(0).getCOSObject()
                             .getString(COSName.DA));
String newAppearance = template.getDefaultAppearance()
                               .replaceAll("/\\w+", "/"
                                                    + fontName);
template.setDefaultAppearance(newAppearance);
System.out.println("Nef field default appearance: "
                   + template.getDefaultAppearance());
System.out.println("New widget default appearance: "
                   + template.getWidgets().get(0).getCOSObject()
                             .getString(COSName.DA));

this code gives for seiga

Old field default appearance: /Helv 18 Tf 0.129 0.129 0.129 rg
Old widget default appearance: /Helv 18 Tf 0.129 0.129 0.129 rg
Nef field default appearance: /F3 18 Tf 0.129 0.129 0.129 rg
New widget default appearance: /Helv 18 Tf 0.129 0.129 0.129 rg

and for libre

Old field default appearance: 1 1 1 rg /He 10.006 Tf
Old widget default appearance: 1 1 1 rg /He 10.006 Tf
Nef field default appearance: 1 1 1 rg /F4 10.006 Tf
New widget default appearance: 1 1 1 rg /F4 10.006 Tf

I can remove DA of widget before changing appearance of the field. This will solve my problem. However, I still have questions:

  1. Why setDefault appearance sometimes changes DA of widget and sometimes does not?
  2. Is there a way to set appearance of field so that other objects could not overwrite it?
  3. What objects other than Widget can overwrite field's appearance?

I am using pdfbox 3.0.2


Solution

  • As Tilman Hausherr commented the problem occurs when the widget of the filed has it own DA. The correct way to solve this problem is to remove DA from widget before changing the font

    String newAppearance = template.getDefaultAppearance()
                                   .replaceAll("/\\w+", "/"
                                                        + fontName);
    template.getWidgets().get(0).getCOSObject().removeItem(COSName.DA);
    template.setDefaultAppearance(newAppearance);