使用 PDFBox 填写表单时如何替换丢失的字体?
How to substitute missing font when filling a form with PDFBox?
我正在尝试使用 PDFBox 2.0.8 填写一堆 PDF 表单。对于某些文档,在设置 PDTextField 的值时出现以下错误:
java.io.IOException: Could not find font: /ArialMT
显然字体没有正确嵌入,而 Microsoft 专有字体通常就是这种情况。
如何告诉 PDFBox 替换字体,例如使用 "normal" Arial 或其他字体?将字段 DA 字符串设置为 "/Helv 0 tf 0 g"
导致 NullPointerException。
将"ArialMT"添加到默认资源:
try (PDDocument doc = PDDocument.load(new File("F2_Datenblatt_022015.pdf")))
{
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDField field = acroForm.getField("Vorname_Name");
// fails with IOException as described in question
//field.setValue("Tilman Hausherr");
// Method 1, just add type1 Helvetica (allows only WinAnsiEncoding glyphs)
//acroForm.getDefaultResources().put(COSName.getPDFName("ArialMT"), PDType1Font.HELVETICA);
// Method 2, add the full Arial font (allows for more different glyphs)
// important: use the method that switches off subsetting
acroForm.getDefaultResources().put(
COSName.getPDFName("ArialMT"),
PDType0Font.load(doc, new FileInputStream("c:/windows/fonts/arial.ttf"), false));
field.setValue("Tilman Hausherr");
doc.save("F2_Datenblatt_022015-mod.pdf");
}
更新:
原来问题中的代码也适用于该文件 - 几乎。它是 "Tf" 而不是 "tf",所以字符串应该是“/Helv 0 Tf 0 g”。我们将研究如何避免 NPE 并获得有意义的异常。
根据 Tilman Hausherr 的评论,我构建了一个独立于操作系统的第一个修复程序(在我的例子中是 Linux)。
acroForm.defaultResources.put(COSName.getPDFName("ArialMT"),
PDType0Font.load (pdDocument, this.javaClass.classLoader.getResourceAsStream("fonts/ARIALMT.ttf"), false))
不过,这仅适用于该特定字体。仍然缺少的 - 实际上是我的问题的主要意图 - 是告诉 PDFBox 回退到某种字体的选项。如果无法提供需要的字体DA
在拍摄者再次出手相救后,我现在可以给出完整的解决方案。同样,这是 Kotlin,不是 Java:
PDDocument.load(file).use { pdDocument ->
val acroForm = pdDocument.documentCatalog.acroForm
acroForm.defaultResources.put(COSName.getPDFName("ArialMT"),
PDType0Font.load (pdDocument, this.javaClass.classLoader.getResourceAsStream("fonts/ARIALMT.ttf"), false))
val pdField: PDField? = acroForm.getField(fieldname)
val value = ...
when (pdField) {
is PDCheckBox -> {
if (value is Boolean) {
when (value) {
true -> pdField.check()
false -> pdField.unCheck()
}
} else {
log.error("RENDER_FORM: Need Boolean for ${pdField.fullyQualifiedName} but got $value")
}
}
is PDTextField -> {
try {
pdField.value = value?.toString() ?: ""
} catch (ioException: IOException) {
pdField.cosObject.setString(COSName.DA, "/Helv 0 Tf 0 g")
pdField.value = value?.toString() ?: ""
log.error("RENDER_FORM: Writing text field failed: ${ioException.message}")
}
}
null -> {
log.error("RENDER_FORMULAR: Formfield $fieldname does not exist in $name")
}
else -> log.error("RENDER_FORMULAR: Formfield $pdField ($fieldname) is of unhandled type ${pdField.fieldType}")
}
val stream = ByteArrayOutputStream()
pdDocument.save(stream)
pdDocument.close()
return stream.toByteArray()
}
我正在尝试使用 PDFBox 2.0.8 填写一堆 PDF 表单。对于某些文档,在设置 PDTextField 的值时出现以下错误:
java.io.IOException: Could not find font: /ArialMT
显然字体没有正确嵌入,而 Microsoft 专有字体通常就是这种情况。
如何告诉 PDFBox 替换字体,例如使用 "normal" Arial 或其他字体?将字段 DA 字符串设置为 "/Helv 0 tf 0 g"
导致 NullPointerException。
将"ArialMT"添加到默认资源:
try (PDDocument doc = PDDocument.load(new File("F2_Datenblatt_022015.pdf")))
{
PDAcroForm acroForm = doc.getDocumentCatalog().getAcroForm();
PDField field = acroForm.getField("Vorname_Name");
// fails with IOException as described in question
//field.setValue("Tilman Hausherr");
// Method 1, just add type1 Helvetica (allows only WinAnsiEncoding glyphs)
//acroForm.getDefaultResources().put(COSName.getPDFName("ArialMT"), PDType1Font.HELVETICA);
// Method 2, add the full Arial font (allows for more different glyphs)
// important: use the method that switches off subsetting
acroForm.getDefaultResources().put(
COSName.getPDFName("ArialMT"),
PDType0Font.load(doc, new FileInputStream("c:/windows/fonts/arial.ttf"), false));
field.setValue("Tilman Hausherr");
doc.save("F2_Datenblatt_022015-mod.pdf");
}
更新: 原来问题中的代码也适用于该文件 - 几乎。它是 "Tf" 而不是 "tf",所以字符串应该是“/Helv 0 Tf 0 g”。我们将研究如何避免 NPE 并获得有意义的异常。
根据 Tilman Hausherr 的评论,我构建了一个独立于操作系统的第一个修复程序(在我的例子中是 Linux)。
acroForm.defaultResources.put(COSName.getPDFName("ArialMT"),
PDType0Font.load (pdDocument, this.javaClass.classLoader.getResourceAsStream("fonts/ARIALMT.ttf"), false))
不过,这仅适用于该特定字体。仍然缺少的 - 实际上是我的问题的主要意图 - 是告诉 PDFBox 回退到某种字体的选项。如果无法提供需要的字体DA
在拍摄者再次出手相救后,我现在可以给出完整的解决方案。同样,这是 Kotlin,不是 Java:
PDDocument.load(file).use { pdDocument ->
val acroForm = pdDocument.documentCatalog.acroForm
acroForm.defaultResources.put(COSName.getPDFName("ArialMT"),
PDType0Font.load (pdDocument, this.javaClass.classLoader.getResourceAsStream("fonts/ARIALMT.ttf"), false))
val pdField: PDField? = acroForm.getField(fieldname)
val value = ...
when (pdField) {
is PDCheckBox -> {
if (value is Boolean) {
when (value) {
true -> pdField.check()
false -> pdField.unCheck()
}
} else {
log.error("RENDER_FORM: Need Boolean for ${pdField.fullyQualifiedName} but got $value")
}
}
is PDTextField -> {
try {
pdField.value = value?.toString() ?: ""
} catch (ioException: IOException) {
pdField.cosObject.setString(COSName.DA, "/Helv 0 Tf 0 g")
pdField.value = value?.toString() ?: ""
log.error("RENDER_FORM: Writing text field failed: ${ioException.message}")
}
}
null -> {
log.error("RENDER_FORMULAR: Formfield $fieldname does not exist in $name")
}
else -> log.error("RENDER_FORMULAR: Formfield $pdField ($fieldname) is of unhandled type ${pdField.fieldType}")
}
val stream = ByteArrayOutputStream()
pdDocument.save(stream)
pdDocument.close()
return stream.toByteArray()
}