如何在PDFBox操作AcroForm表单时正确使用TTF字体并完成文档展平?
Hey there, let's tackle this font issue you're facing. The core problem here is that when you update form fields and flatten the PDF with PDFBox, the tool isn't properly picking up the embedded fonts from your original LibreOffice-generated PDF. Your initial attempt added a default font resource, but missed loading both required fonts and refreshing individual field appearances—two critical steps.
Step-by-Step Solution
Here's a revised code implementation that addresses these gaps, ensuring your custom fonts stay embedded after flattening:
Corrected Code
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; import org.apache.pdfbox.pdmodel.interactive.form.PDField; import org.apache.pdfbox.cos.COSName; import java.io.File; import java.io.IOException; import java.io.InputStream; public class PdfFormModifier { public static void main(String[] args) { try (PDDocument doc = PDDocument.load(new File("/media/stoyank/Elements/Java/tmp/Receipt.pdf"))) { PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm(); if (acroForm == null) { System.err.println("No AcroForm found in the document"); return; } // Set up form resources and load both required fonts PDResources resources = acroForm.getDefaultResources(); if (resources == null) { resources = new PDResources(); } // Load SF Pro Text Light (for all fields except tbxTotal) try (InputStream sfProStream = ClassLoader.getSystemResourceAsStream("SF-Pro-Text-Light.ttf")) { if (sfProStream == null) { System.err.println("Couldn't locate SF-Pro-Text-Light.ttf"); return; } PDType0Font sfProFont = PDType0Font.load(doc, sfProStream, false); resources.put(COSName.getPDFName("SFProTextLight"), sfProFont); } // Load Oswald Regular (for tbxTotal) try (InputStream oswaldStream = ClassLoader.getSystemResourceAsStream("Oswald-Regular.ttf")) { if (oswaldStream == null) { System.err.println("Couldn't locate Oswald-Regular.ttf"); return; } PDType0Font oswaldFont = PDType0Font.load(doc, oswaldStream, false); resources.put(COSName.getPDFName("Oswald-Regular"), oswaldFont); } acroForm.setDefaultResources(resources); // Update fields and refresh individual appearances PDField orderIdField = acroForm.getField("tbxOrderId"); if (orderIdField != null) { orderIdField.setValue("192753"); orderIdField.refreshAppearance(); // Critical: Refresh this field's look } PDField totalField = acroForm.getField("tbxTotal"); if (totalField != null) { totalField.setValue("Total: €192.00"); totalField.refreshAppearance(); // Critical for Oswald font to apply } // Flatten and save acroForm.flatten(); doc.save("/media/stoyank/Elements/Java/tmp/output.pdf"); } catch (IOException e) { e.printStackTrace(); } } }
Key Fixes & Notes
- Load Both Fonts: Your original code only added Oswald, but you need to load both SF Pro Text Light and Oswald Regular since different fields use different fonts.
- Refresh Individual Field Appearances: Calling
refreshAppearance()on each modified field ensures PDFBox regenerates the field's visual using the correct font from your resources. The AcroForm-levelrefreshAppearances()doesn't always handle this reliably for individual fields. - Font Path Validation: Double-check your font file paths. If classloader resources aren't working, use absolute file paths (e.g.,
new FileInputStream("/home/youruser/fonts/SF-Pro-Text-Light.ttf")) to test. - Font Name Matching: Make sure the names you use in
COSName.getPDFName()match the font references in your original PDF. You can inspect the original PDF's field dictionaries with a tool like PDFBox Debugger to confirm the exact font names used. - Euro Symbol Support: Using
PDType0Fontensures proper handling of extended characters like the euro (€), which might fail with older font loading methods.
Verify the Fix
After running the code, use pdffonts to check the output PDF:
pdffonts /media/stoyank/Elements/Java/tmp/output.pdf
You should see both SF Pro Text Light and Oswald Regular listed with emb set to yes, confirming they're embedded correctly.
内容的提问来源于stack exchange,提问作者S. Kostadinov




