使用pdfbox 1.8.9 api创建复选框并将其打印到pdf文件无效

时间:2017-04-25 08:06:51

标签: grails pdfbox

我使用带有pdfbox插件的grails。我想在pdf中打印复选框,有些是经过检查,有些则不是。 要打印复选框,我没有直接的方式(即使使用PDCheckbox类)。因此,我使用另一种方法使用以下代码打印带有刻度线的复选框:

public static writeInputFieldToPDFPage( PDPage pdPage, PDDocument document, Float x, Float y, Boolean ticked) {
    PDFont font = PDType1Font.HELVETICA
    PDResources res = new PDResources()
    String fontName = res.addFont(font)
    String da = ticked?"/" + fontName + " 10 Tf 0 0.4 0 rg":""

    COSDictionary acroFormDict = new COSDictionary()
    acroFormDict.setBoolean(COSName.getPDFName("NeedAppearances"), true)
    acroFormDict.setItem(COSName.FIELDS, new COSArray())
    acroFormDict.setItem(COSName.DA, new COSString(da))

    PDAcroForm acroForm =  new PDAcroForm(document, acroFormDict)
    acroForm.setDefaultResources(res)
    document.getDocumentCatalog().setAcroForm(acroForm)

    PDGamma colourBlack = new PDGamma()
    PDAppearanceCharacteristicsDictionary fieldAppearance =
        new PDAppearanceCharacteristicsDictionary(new COSDictionary())
    fieldAppearance.setBorderColour(colourBlack)
    if(ticked) {
        COSArray arr = new COSArray()
        arr.add(new COSFloat(0.89f))
        arr.add(new COSFloat(0.937f))
        arr.add(new COSFloat(1f))
        fieldAppearance.setBackground(new PDGamma(arr))
    }

    COSDictionary cosDict = new COSDictionary()
    COSArray rect = new COSArray()

    rect.add(new COSFloat(x))
    rect.add(new COSFloat(new Float(y-5)))
    rect.add(new COSFloat(new Float(x+10)))
    rect.add(new COSFloat(new Float(y+5)))

    cosDict.setItem(COSName.RECT, rect)
    cosDict.setItem(COSName.FT, COSName.getPDFName("Btn")) // Field Type
    cosDict.setItem(COSName.TYPE, COSName.ANNOT)
    cosDict.setItem(COSName.SUBTYPE, COSName.getPDFName("Widget"))
    if(ticked) {
    cosDict.setItem(COSName.TU, new COSString("Checkbox with PDFBox"))
    }
    cosDict.setItem(COSName.T, new COSString("Chk"))
    //Tick mark color and size of the mark
    cosDict.setItem(COSName.DA, new COSString(ticked?"/F0 10 Tf 0 0.4 0 rg":"/FF 1 Tf 0 0 g"))
    cosDict.setInt(COSName.F, 4)

    PDCheckbox checkbox = new PDCheckbox(acroForm, cosDict)
    checkbox.setFieldFlags(PDCheckbox.FLAG_READ_ONLY)
    checkbox.setValue("Yes")

    checkbox.getWidget().setAppearanceCharacteristics(fieldAppearance)

    pdPage.getAnnotations().add(checkbox.getWidget())
    acroForm.getFields().add(checkbox)
}

此代码在我的应用程序中运行正常,此方法也添加了带刻度标记的复选框。 但我只能在pdf阅读器中看到那些矩形复选框或刻度线,而不是所有其他阅读器(如chrome默认pdf查看器),甚至当我尝试打印pdf时它不打印复选框,而是打印一些随机的ASCII数字

如果有其他方法可以解决,或者即使我必须重构代码,请告诉我。

2 个答案:

答案 0 :(得分:1)

有什么问题

您的AcroForm复选框字段构造错误:您将其视为文本字段,PDF阅读器应根据字段的默认外观( DA )值创建外观,尤其是< strong> NeedAppearances 是真的。

复选框有所不同:您必须至少为开启状态提供外观流,参见规范ISO 32000-1:

  

复选框字段表示一个或多个复选框,当用户使用鼠标或键盘操作时,这些复选框在两个状态之间切换,打开和关闭。其字段类型应为 Btn ,其按钮和无线电标志(见表226)均应清晰。每个州都可以有一个单独的外观,该外观应由字段小部件注释的外观字典中的外观流定义(参见12.5.5,“外观流”)。关闭状态的外观是可选的,但如果存在,则应以名称关闭存储在外观字典中。 应该用作开启状态的名称。

(ISO 32000-1第12.7.4.2.3节&#34;复选框&#34;)

因此,您必须构建一个 AP (&#34;外观&#34;)条目,而不是构建 DA 条目,而该条目本身至少是一个字典 N (&#34;正常出现&#34;)条目,本身是一个字典,至少包含一个状态外观的条目,建议称为。< / p>

该规范提供了一个显示典型复选框定义的示例:

1 0 obj
   << /FT /Btn
      /T (Urgent)
      /V /Yes
      /AS /Yes
      /AP << /N << /Yes 2 0 R /Off 3 0 R>>
   >>
endobj
2 0 obj
   << /Resources 20 0 R
      /Length 104
   >>
stream
   q
      0 0 1 rg
      BT
         /ZaDb 12 Tf
         0 0 Td
         (4) Tj
      ET
   Q
endstream
endobj
3 0 obj
   << /Resources 20 0 R
      /Length 104
   >>
stream
   q
      0 0 1 rg
      BT
         /ZaDb 12 Tf
         0 0 Td
         (8) Tj
      ET
   Q
endstream
endobj

20 0 obj中的资源似乎包含一个名为 ZaDb 的字体资源,引用ZapfDingbats。)

顺便提一下,你提到有一个PDF查看器实际上按原样显示文档的勾号。你可能想告诉他们的开发他们在那里做错了。

一个例子

在评论中,您要求提供示例代码,并指出如果它适用于当前的2.0.x版本的PDFBox,则可以。所以我尝试了并提出了这段代码:

PDDocument document = new PDDocument();

PDPage page = new PDPage();
document.addPage(page);

PDAcroForm acroForm = new PDAcroForm(document);
document.getDocumentCatalog().setAcroForm(acroForm);


COSDictionary normalAppearances = new COSDictionary();
PDAppearanceDictionary pdAppearanceDictionary = new PDAppearanceDictionary();
pdAppearanceDictionary.setNormalAppearance(new PDAppearanceEntry(normalAppearances));
pdAppearanceDictionary.setDownAppearance(new PDAppearanceEntry(normalAppearances));

PDAppearanceStream pdAppearanceStream = new PDAppearanceStream(document);
pdAppearanceStream.setResources(new PDResources());
try (PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdAppearanceStream))
{
    pdPageContentStream.setFont(PDType1Font.ZAPF_DINGBATS, 14.5f);
    pdPageContentStream.beginText();
    pdPageContentStream.newLineAtOffset(3, 4);
    pdPageContentStream.showText("\u2714");
    pdPageContentStream.endText();
}
pdAppearanceStream.setBBox(new PDRectangle(18, 18));
normalAppearances.setItem("Yes", pdAppearanceStream);

pdAppearanceStream = new PDAppearanceStream(document);
pdAppearanceStream.setResources(new PDResources());
try (PDPageContentStream pdPageContentStream = new PDPageContentStream(document, pdAppearanceStream))
{
    pdPageContentStream.setFont(PDType1Font.ZAPF_DINGBATS, 14.5f);
    pdPageContentStream.beginText();
    pdPageContentStream.newLineAtOffset(3, 4);
    pdPageContentStream.showText("\u2718");
    pdPageContentStream.endText();
}
pdAppearanceStream.setBBox(new PDRectangle(18, 18));
normalAppearances.setItem("Off", pdAppearanceStream);

PDCheckBox checkBox = new PDCheckBox(acroForm);
acroForm.getFields().add(checkBox);
checkBox.setPartialName("CheckBoxField");
checkBox.setFieldFlags(4);

List<PDAnnotationWidget> widgets = checkBox.getWidgets();
for (PDAnnotationWidget pdAnnotationWidget : widgets)
{
    pdAnnotationWidget.setRectangle(new PDRectangle(50, 750, 18, 18));
    pdAnnotationWidget.setPage(page);
    page.getAnnotations().add(pdAnnotationWidget);

    pdAnnotationWidget.setAppearance(pdAppearanceDictionary);
}

// checkBox.setReadOnly(true);
checkBox.check();
// checkBox.unCheck();


document.save(new File(RESULT_FOLDER, "CheckBox.pdf"));
document.close();

CreateCheckBox test testCheckboxForSureshGoud

务必使用

checkBox.check();

checkBox.unCheck();

否则框的状态未定义。

答案 1 :(得分:0)

@mkl 有一个很好的答案,但我认为如果您已经有一个 Document 并且只想添加一个 PDCheckbox (scala),我认为可以稍微简化一下:

  def addCheckboxField(
    doc: PDDocument,
    form: PDAcroForm,
    name: String,
    pg: Int, // page number
    x: Float,
    y: Float,
    width: Float,
    height: Float
  ) = {

    val normalAppearances = new COSDictionary()

    normalAppearances.setItem(
      "Yes", {
        val appearanceStream = new PDAppearanceStream(doc)
        appearanceStream.setResources(new PDResources())
        appearanceStream
      }
    )

    val appearanceDictionary = new PDAppearanceDictionary()
    appearanceDictionary.setNormalAppearance(new PDAppearanceEntry(normalAppearances))
    appearanceDictionary.setDownAppearance(new PDAppearanceEntry(normalAppearances))

    val field = new PDCheckBox(form)
    field.setPartialName(name)

    val widget = field.getWidgets.get(0)
    widget.setAppearance(appearanceDictionary)

    form.getFields.add(field)

    val page = doc.getPage(pg)

    widget.setRectangle(new PDRectangle(x, y, width, height))
    widget.setPage(page)
    widget.setPrinted(true)

    page.getAnnotations().add(widget)

    // do what you want with it
    field.unCheck()
  }

可能还可以进行其他简化,但这对我有用。

PDFBox 版本:2.0.21