java - PDFBox不打印表单域
问题描述
我正在尝试打印在 LibreOffice 中创建并包含填写的表单字段的后处理(填充)PDF 模板。
PDFBox svn很好,并且有很多示例如何做到这一点。获取 PDF 和它的 AcroFormat 很容易,甚至可以按预期编辑和保存修改后的 PDF 到磁盘。但这不是我的目标。我想要一个 PDF,其中填写了字段,然后被删除,只剩下文本。
我在stackoverflow上尝试了有关PDFBox的所有内容,从扁平化acroform到在字段和其他元信息上设置只读属性,安装必要的字体等等。每次我将 PDF 打印到文件时,文本字段中的文本(已编辑和未编辑)都会消失,文本字段也消失了。
但是后来我尝试使用 PDFBox从头开始创建 PDF,并且打印效果符合预期。文本字段在生成的模板中,打印的 pdf 文件包含我想要的文本,并删除了相应的表单。因此,我使用 PDFBox 中的PDF 调试器来分析 PDF 的结构,并注意到在调试器的预览中,我的 PDF 不包含从 LibreOffice 导出的文本字段中的文本。但是在树结构中,PDF 注释显然存在(/DV 和 /V),并且看起来很安静,类似于正在运行的 pdfbox 创建的版本。
为了测试,我创建了一个简单的 pdf,其中只有一个名为“test”和内容“Foobar”的文本字段。还更改了背景和边框颜色,以查看是否成功打印了任何内容。
PDDocument document = null;
try {
document = PDDocument.load(new File("<filepath>\\<filename>"));
} catch (final InvalidPasswordException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
PrintFields.createDummyPDF("<filepath>\\<filename>");
PrintFields.printFields(document); //debug output
//Getting pdf meta infos
final PDDocumentCatalog docCatalog = document.getDocumentCatalog();
final PDAcroForm acroForm = docCatalog.getAcroForm();
docCatalog.setAcroForm(acroForm);
//setting the appearance
final PDFont font = PDType1Font.HELVETICA;
final PDResources resources = new PDResources();
resources.put(COSName.getPDFName("Helv"), font);
acroForm.setDefaultResources(resources);
String defaultAppearanceString = "/Helv 0 Tf 0 g";
acroForm.setDefaultAppearance(defaultAppearanceString);
for(final PDField f : acroForm.getFields()) {
if(f instanceof PDTextField) {
defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
final List<PDAnnotationWidget> widgets = ((PDTextField)f).getWidgets();
widgets.get(0).setAppearanceState(defaultAppearanceString);
}
}
for(final PDField f : acroForm.getFields()) {
f.setReadOnly(true);
}
// save modified pdf to file
document.save("<filepath>\\<filename>");
//print to file (to pdf)
if (job.printDialog()) {
try {
// Desktop.getDesktop().print();
job.print();
} catch (final PrinterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// copied from pdfbox examples
public static void createDummyPDF(final String path) throws IOException
{
// Create a new document with an empty page.
try (PDDocument document = new PDDocument())
{
final PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
// Adobe Acrobat uses Helvetica as a default font and
// stores that under the name '/Helv' in the resources dictionary
final PDFont font = PDType1Font.HELVETICA;
final PDResources resources = new PDResources();
resources.put(COSName.getPDFName("Helv"), font);
// Add a new AcroForm and add that to the document
final PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);
// Add and set the resources and default appearance at the form level
acroForm.setDefaultResources(resources);
// Acrobat sets the font size on the form level to be
// auto sized as default. This is done by setting the font size to '0'
String defaultAppearanceString = "/Helv 0 Tf 0 g";
acroForm.setDefaultAppearance(defaultAppearanceString);
// Add a form field to the form.
final PDTextField textBox = new PDTextField(acroForm);
textBox.setPartialName("SampleField");
// Acrobat sets the font size to 12 as default
// This is done by setting the font size to '12' on the
// field level.
// The text color is set to blue in this example.
// To use black, replace "0 0 1 rg" with "0 0 0 rg" or "0 g".
defaultAppearanceString = "/Helv 12 Tf 0 0 1 rg";
textBox.setDefaultAppearance(defaultAppearanceString);
// add the field to the acroform
acroForm.getFields().add(textBox);
// Specify the widget annotation associated with the field
final PDAnnotationWidget widget = textBox.getWidgets().get(0);
final PDRectangle rect = new PDRectangle(50, 750, 200, 50);
widget.setRectangle(rect);
widget.setPage(page);
// set green border and yellow background
// if you prefer defaults, just delete this code block
final PDAppearanceCharacteristicsDictionary fieldAppearance
= new PDAppearanceCharacteristicsDictionary(new COSDictionary());
fieldAppearance.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE));
fieldAppearance.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE));
widget.setAppearanceCharacteristics(fieldAppearance);
// make sure the widget annotation is visible on screen and paper
widget.setPrinted(true);
// Add the widget annotation to the page
page.getAnnotations().add(widget);
// set the field value
textBox.setValue("Sample field");
document.save(path);
}
}
//copied from pdfbox examples
public static void processFields(final List<PDField> fields, final PDResources resources) {
fields.stream().forEach(f -> {
f.setReadOnly(true);
final COSDictionary cosObject = f.getCOSObject();
final String value = cosObject.getString(COSName.DV) == null ?
cosObject.getString(COSName.V) : cosObject.getString(COSName.DV);
System.out.println("Setting " + f.getFullyQualifiedName() + ": " + value);
try {
f.setValue(value);
} catch (final IOException e) {
if (e.getMessage().matches("Could not find font: /.*")) {
final String fontName = e.getMessage().replaceAll("^[^/]*/", "");
System.out.println("Adding fallback font for: " + fontName);
resources.put(COSName.getPDFName(fontName), PDType1Font.HELVETICA);
try {
f.setValue(value);
} catch (final IOException e1) {
e1.printStackTrace();
}
} else {
e.printStackTrace();
}
}
if (f instanceof PDNonTerminalField) {
processFields(((PDNonTerminalField) f).getChildren(), resources);
}
});
我希望 document.save() 和 job.print() 生成的 pdf 在查看器中看起来相同,但事实并非如此。如果我在禁用只读的情况下使用 document.save() 生成的 pdf,我可以使用 FoxitReader 之类的 PDF 查看器来填写表格并再次打印。这会产生正确的输出。使用 job.print() 版本会导致(文本)表单字段中包含的文本消失。有谁知道为什么会这样?
我正在使用 PDFBox 2.0.13(最新版本)和 LibreOffice 6.1.4.2。 这是引用的文件,您可以在此处下载调试器(jar 文件,可使用 java -jar 运行)。
解决方案
推荐阅读
- r - 如何根据部分文件名读取excel文件
- python - Python 正则表达式与某些文本不匹配
- excel - 在 vba 中将定期会议从 Outlook 变为 excel 的问题
- c - 使用 pthread 在 C 中将顺序循环转换为并行
- java - 从 AWS Lambda 调用 Google Cloud KMS
- python - 如何在 TensorFlow Number Recognition 中使用自己的手绘图像
- reactjs - 如何打开一个文本字段并设置它的值在反应的下拉列表中选择一些值
- c# - ASP.NET POST 接收问题
- reactjs - 为什么在先前抛出错误后重新加载组件时 React useEffect 挂钩未激活?
- javascript - 如何为已展平的嵌套对象生成类型?