首先在 Acrobat 中修改的 PDF AcroForm 字段然后在 iText7 中变为空白
PDF AcroForm field modified first in Acrobat then in iText7 becomes blank
我正在使用 iText7 生成一些带有可编辑表单域的 PDF 文档。
我的代码使用以下代码将字段添加到 PDF 文档:
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
PdfAcroForm form = PdfAcroForm.getAcroForm(drawContext.getDocument(), true);
PdfFormField field = isMultiline
? PdfFormField.createMultilineText(drawContext.getDocument(), getOccupiedAreaBBox(), fieldName, "")
: PdfFormField.createText(drawContext.getDocument(), getOccupiedAreaBBox(), fieldName, "");
field.setFontSize(defaultFontSize - 1);
form.addField(field);
}
以上是iText自定义渲染器的draw
方法class。
生成的 PDF 文档按预期工作。我可以在 Adobe Acrobat 中打开它 Reader,填写可编辑字段,保存,重新打开等等
我的一个要求是允许以编程方式展平表单(显然是在人类用户填写之后),以便此后无法进行编辑。
我用这段代码来做:
PdfDocument pdf = ... // Read a PDF stream and get the document object
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, false);
form.flattenFields();
pdf.close()
现在的问题是,当我用 PDF reader 打开拼合的 PDF 时,字段是空白的。
我进行了一些调试,看起来只有当我尝试将使用 Adobe Reader 编辑(和保存)的字段展平时才会出现此问题(版本十一).
如果我以编程方式编辑字段,例如使用 iText 的 PdfFormField#setValue(String)
方法,扁平化的 PDF 可以很好地呈现值。
Adobe Reader 似乎正在设置表单域的一些属性,以防止它们被正确地展平...实际上是这样吗?有什么解决办法吗?
显然这个问题是由 Adobe Reader 引起的:出于某种原因,它保存了没有 /XObject
子类型的字段定义,iText7 在 form.flattenFields()
方法中检查了它。
这是字段字符串在 Eclipse 调试器中的样子:
由 iText7 生成:
<</BBox [0 0 523 109 ] /Filter /FlateDecode /Length 95 /Matrix [1 0 0 1 0 0 ] /Resources <</Font <</F1 23 0 R >> >> /Subtype /Form /Type /XObject >>
由 Acrobat 保存 Reader:
<</BBox [0.0 0.0 523.0 109.0 ] /Filter /FlateDecode /Length 107 /Resources <</Font <</F1 21 0 R >> >> >>
如您所见,缺少 /Subtype
部分。每当您尝试禁用表单字段时,例如PdfAcroForm#flattenFields()
或 PdfFormField#setReadOnly(boolean)
,iText 尝试使用此代码在 PdfCanvas
上重绘其内容:
if(xObject != null && xObject.getPdfObject().get(PdfName.Subtype) != null) {
[...]
canvas.addXObject(xObject, ...);
}
已跳过,因为 Reader 字段不再是 /XObject
。
我目前的解决方法是用一个新字段替换该字段:
Map<String,PdfFormField> fields = form.getFormFields();
Set<String> keys = new HashSet<>(fields.keySet()); // avoids concurrent modifications
for(String fieldName : keys) {
PdfFormField field = fields.get(fieldName);
PdfDictionary fieldObject = field.getPdfObject();
PdfFormField newField = field.isMultiline()
? PdfFormField.createMultilineText(pdf, fieldObject.getAsRectangle(PdfName.Rect), fieldName,
field.getValueAsString(), defaultFont, defaultFontSize)
: PdfFormField.createText(pdf, fieldObject.getAsRectangle(PdfName.Rect), fieldName,
field.getValueAsString(), defaultFont, defaultFontSize);
form.replaceField(fieldName, newField);
}
form.flattenFields();
虽然字体信息不是动态设置的,但它有点工作,但这是
编辑:
所以最后我用 iText RUPS 打开了 PDF。看起来 Acrobat Reader 确实设置了子类型,但它是 /Widget
。我找到的最后一个解决方法是手动将子类型设置为 /XObject
,然后调用 form.flatten()
。这也保留了字段上的字体信息。
请注意,手动更改子类型可能会破坏其他功能。我不是 PDF 专家,所以我不能确定。不过现在我只是改变子类型并立即展平,所以这应该不是什么大问题。
此代码的最终版本如下所示:
for(String fieldName : keys) {
PdfFormField field = fields.get(fieldName);
PdfDictionary fieldObject = field.getPdfObject();
if(fieldObject.get(PdfName.Subtype) != PdfName.XObject) {
fieldObject.put(PdfName.Subtype, PdfName.XObject);
}
}
form.flattenFields();
pdf.close();
这样看来它正在按预期工作。
我遇到了完全相同的问题。出于某种原因,Adobe Reader 似乎也是原因。我为此找到的另一个解决方案似乎是将字段设置为只读 (iText7):
using (iText.Kernel.Pdf.PdfDocument pdfStamper = new iText.Kernel.Pdf.PdfDocument(pdfReader, pdfWriter))
{
iText.Forms.PdfAcroForm pdfForm = iText.Forms.PdfAcroForm.GetAcroForm(pdfStamper, true);
if (pdfForm != null)
{
System.Collections.Generic.IDictionary<String, iText.Forms.Fields.PdfFormField> FormFields = pdfForm.GetFormFields();
foreach (KeyValuePair<String, iText.Forms.Fields.PdfFormField> kv in FormFields)
{
FormFields[kv.Key].SetReadOnly(true);
}
}
WProps.SetFullCompressionMode(false);
pdfStamper.GetWriter().SetCloseStream(false);
}
我正在使用 iText7 生成一些带有可编辑表单域的 PDF 文档。 我的代码使用以下代码将字段添加到 PDF 文档:
@Override
public void draw(DrawContext drawContext) {
super.draw(drawContext);
PdfAcroForm form = PdfAcroForm.getAcroForm(drawContext.getDocument(), true);
PdfFormField field = isMultiline
? PdfFormField.createMultilineText(drawContext.getDocument(), getOccupiedAreaBBox(), fieldName, "")
: PdfFormField.createText(drawContext.getDocument(), getOccupiedAreaBBox(), fieldName, "");
field.setFontSize(defaultFontSize - 1);
form.addField(field);
}
以上是iText自定义渲染器的draw
方法class。
生成的 PDF 文档按预期工作。我可以在 Adobe Acrobat 中打开它 Reader,填写可编辑字段,保存,重新打开等等
我的一个要求是允许以编程方式展平表单(显然是在人类用户填写之后),以便此后无法进行编辑。
我用这段代码来做:
PdfDocument pdf = ... // Read a PDF stream and get the document object
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, false);
form.flattenFields();
pdf.close()
现在的问题是,当我用 PDF reader 打开拼合的 PDF 时,字段是空白的。
我进行了一些调试,看起来只有当我尝试将使用 Adobe Reader 编辑(和保存)的字段展平时才会出现此问题(版本十一).
如果我以编程方式编辑字段,例如使用 iText 的 PdfFormField#setValue(String)
方法,扁平化的 PDF 可以很好地呈现值。
Adobe Reader 似乎正在设置表单域的一些属性,以防止它们被正确地展平...实际上是这样吗?有什么解决办法吗?
显然这个问题是由 Adobe Reader 引起的:出于某种原因,它保存了没有 /XObject
子类型的字段定义,iText7 在 form.flattenFields()
方法中检查了它。
这是字段字符串在 Eclipse 调试器中的样子:
由 iText7 生成:
<</BBox [0 0 523 109 ] /Filter /FlateDecode /Length 95 /Matrix [1 0 0 1 0 0 ] /Resources <</Font <</F1 23 0 R >> >> /Subtype /Form /Type /XObject >>
由 Acrobat 保存 Reader:
<</BBox [0.0 0.0 523.0 109.0 ] /Filter /FlateDecode /Length 107 /Resources <</Font <</F1 21 0 R >> >> >>
如您所见,缺少 /Subtype
部分。每当您尝试禁用表单字段时,例如PdfAcroForm#flattenFields()
或 PdfFormField#setReadOnly(boolean)
,iText 尝试使用此代码在 PdfCanvas
上重绘其内容:
if(xObject != null && xObject.getPdfObject().get(PdfName.Subtype) != null) {
[...]
canvas.addXObject(xObject, ...);
}
已跳过,因为 Reader 字段不再是 /XObject
。
我目前的解决方法是用一个新字段替换该字段:
Map<String,PdfFormField> fields = form.getFormFields();
Set<String> keys = new HashSet<>(fields.keySet()); // avoids concurrent modifications
for(String fieldName : keys) {
PdfFormField field = fields.get(fieldName);
PdfDictionary fieldObject = field.getPdfObject();
PdfFormField newField = field.isMultiline()
? PdfFormField.createMultilineText(pdf, fieldObject.getAsRectangle(PdfName.Rect), fieldName,
field.getValueAsString(), defaultFont, defaultFontSize)
: PdfFormField.createText(pdf, fieldObject.getAsRectangle(PdfName.Rect), fieldName,
field.getValueAsString(), defaultFont, defaultFontSize);
form.replaceField(fieldName, newField);
}
form.flattenFields();
虽然字体信息不是动态设置的,但它有点工作,但这是
编辑:
所以最后我用 iText RUPS 打开了 PDF。看起来 Acrobat Reader 确实设置了子类型,但它是 /Widget
。我找到的最后一个解决方法是手动将子类型设置为 /XObject
,然后调用 form.flatten()
。这也保留了字段上的字体信息。
请注意,手动更改子类型可能会破坏其他功能。我不是 PDF 专家,所以我不能确定。不过现在我只是改变子类型并立即展平,所以这应该不是什么大问题。
此代码的最终版本如下所示:
for(String fieldName : keys) {
PdfFormField field = fields.get(fieldName);
PdfDictionary fieldObject = field.getPdfObject();
if(fieldObject.get(PdfName.Subtype) != PdfName.XObject) {
fieldObject.put(PdfName.Subtype, PdfName.XObject);
}
}
form.flattenFields();
pdf.close();
这样看来它正在按预期工作。
我遇到了完全相同的问题。出于某种原因,Adobe Reader 似乎也是原因。我为此找到的另一个解决方案似乎是将字段设置为只读 (iText7):
using (iText.Kernel.Pdf.PdfDocument pdfStamper = new iText.Kernel.Pdf.PdfDocument(pdfReader, pdfWriter))
{
iText.Forms.PdfAcroForm pdfForm = iText.Forms.PdfAcroForm.GetAcroForm(pdfStamper, true);
if (pdfForm != null)
{
System.Collections.Generic.IDictionary<String, iText.Forms.Fields.PdfFormField> FormFields = pdfForm.GetFormFields();
foreach (KeyValuePair<String, iText.Forms.Fields.PdfFormField> kv in FormFields)
{
FormFields[kv.Key].SetReadOnly(true);
}
}
WProps.SetFullCompressionMode(false);
pdfStamper.GetWriter().SetCloseStream(false);
}