2016-10-19 123 views
0

我正在嘗試創建一個簡單的pdf多頁文檔,其中包含字段。要做到這一點,我有一個模板pdf,它在代碼中根據需要多次克隆此模板以創建文檔本身。PDFBox將數據插入多頁文檔

問題出現在插入一些數據。我嘗試插入文檔的數據類型不應該跨頁面更改。而不是,它在所有頁面中保持靜態,就像表示這個文檔包含的頁面數量的「頁面」數字一樣。

現在,在我的模板pdf中,我有一些文本字段,比如「Shipper1」和「Pages」。我希望能夠將我的數據插入到這些文本字段中,以便文檔中的所有頁面在其「Shipper1」和「Pages」字段中都具有此值。

我的代碼目前只在第一頁上做。它完美地顯示數據。另一方面,當我轉到另一頁時,數據不會顯示在那裏。它只是顯示一個空的字段。

這裏就是我發起PDF文檔代碼:

static void initiatePdf() { 
     // Initiate a new PDF Box object and get the acro form from it 
     File file = new File(Constants.Paths.EMPTY_DOC) 
     PDDocument tempDoc 

     Evaluator evaluator = new Evaluator(metaHolder) 
     int numPages = evaluator.getNumOfPagesRequired(objects) 

     FieldRenamer renamer = new FieldRenamer() 

     PDResources res = new PDResources() 
     COSDictionary acroFormDict = new COSDictionary() 

     List<PDField> fields = [] 

     Closure isFieldExist = {List<PDField> elements, String fieldName -> 
      elements.findAll{it.getFullyQualifiedName() == fieldName}.size() > 0 
     } 

     for(int i = 0; i < numPages; i++) { 
      tempDoc = new PDDocument().load(file) 

      PDDocumentCatalog docCatalog = tempDoc.getDocumentCatalog() 
      PDAcroForm acroForm = docCatalog.acroForm 

      PDPage page = (PDPage) docCatalog.getPages().get(0) 

      renamer.setCurrentForm(acroForm) 

      if(i == 0) { 
       res = acroForm.getDefaultResources() 
       acroFormDict.mergeInto(acroForm.getCOSObject()) 
       renamer.renameFields(1) 
      } else 
       renamer.renameFields(i*10+1) 

      List<PDField> newFields = acroForm.fields.findAll { PDField newField -> 
       isFieldExist(fields, newField.getFullyQualifiedName()) == false 
      } 

      fields.addAll(newFields) 
      document.addPage(page) 
     } 

     PDAcroForm acroForm = new PDAcroForm(document, acroFormDict); 
     acroForm.setFields(fields) 

     acroForm.setDefaultResources(res); 

     document.documentCatalog.setAcroForm(acroForm) 
    } 

幾件事情第一: metaHolder實例保存所有 駐留在ACRO表單中的字段的信息。信息是:字段名稱,字段控件寬度,字段字體和字體大小

evaluatorEvaluator類的實例。其目的是分析動態數據並確定將包含所有文本數據的頁面數量。

這裏就是我試圖填充文本字段:

static void populateData() { 
    def properties = ["$Constants.Fields.SHIPPER" : "David"] 
    FieldPopulater populater = new FieldPopulater(document, metaHolder) 

    populater.populateStaticFields(properties) 
} 

FieldPopulater類:

package app.components 

import app.StringUtils 
import app.components.entities.DGObject 
import app.components.entities.FieldMeta 
import org.apache.pdfbox.pdmodel.PDDocument 
import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm 
import org.apache.pdfbox.pdmodel.interactive.form.PDField 

/** 
* Created by David on 18/10/2016. 
*/ 
class FieldPopulater { 

    PDAcroForm acroForm 
    FormMetaHolder metaHolder 
    FieldPopulater(PDDocument document, FormMetaHolder metaHolder) { 
     this.acroForm = document.getDocumentCatalog().acroForm 
     this.metaHolder = metaHolder 
    } 

    void populateStaticFields(properties) { 
     List<PDField> fields = [] 
     properties.each {fieldName, data -> 
      FieldMeta fieldMeta = metaHolder.getMetaData(fieldName) 

      fields = acroForm.fields.findAll { PDField field -> 
       String currentName = field.getFullyQualifiedName() 
       char lastChar = currentName[-1] 
       if(Character.isDigit(lastChar)) { 
        currentName = currentName.substring(0,currentName.size()-1) 
       } 

       currentName == fieldName 
      } 

      if(fields.size() > 1) { 
       int counter = 1 
       String tempData = data 
       String currentFitData 
       while(tempData.isEmpty() != true) { 
        int maxWords = Utils.getMaxWords(tempData, fieldMeta) 
        currentFitData = StringUtils.getTextByWords(tempData, maxWords) 
        tempData = StringUtils.chopTextByWords(tempData, maxWords) 

        PDField field = fields.find{it.getFullyQualifiedName()[-1] == "$counter"} 
        field?.setValue(currentFitData) 

        counter++ 
       } 
      } else { 
       PDField tempField = fields[0] 
       tempField.setValue(data) 
      } 
     } 
    } 
} 

其結果是,在第一頁,現場「託運人」有一個值「David」 在第二頁中,「託運人」字段爲空。

這是一張圖片。第一頁:

第二頁:

這裏有什麼問題嗎?

更新:我試圖將每個新的acro表單的小部件添加到當前頁面,以便每個字段都會有幾個孩子小部件代表該字段,但它仍然不起作用。

// All the widgets that are associated with the fields 
List<PDAnnotationWidget> widgets = acroForm.fields.collect {PDField field -> field.getWidgets().get(0)} 

page.annotations.addAll(widgets) 

更新:我也試過一個字段的當前窗口小部件添加到小部件的父字段的集合。這裏是代碼:

List<PDAnnotationWidget> widgets = [] 
// All the widgets that are associated with the fields 
acroForm.fields.each {PDField field -> 
     PDAnnotationWidget widget = field.widgets.get(0) 

     // Adding the following widget to the page and to the field's list of annotation widgets 
     widgets.add(widget) 
     fields.find {it.getFullyQualifiedName() == field.getFullyQualifiedName()}?.widgets.add(widget) 
      } 

page.annotations.addAll(widgets) 
+0

這裏已經很晚了,所以只是一個簡單的提示:跨頁面具有相同的值,您的字段需要有多個註釋小部件,即每個頁面上有一個小部件。該領域必須將這些小部件作爲孩子。每個頁面都必須在其註釋列表中包含此小部件。獲取一些現有的表單文檔(或者創建一個帶有Adobe Acrobat的文檔,如果有的話),並使用PDFDebugger進行查看。 –

+0

我試圖做你所說的,即在頁面中添加一個acro表單的小部件,但問題依然存在。我在帖子中添加了我寫的代碼 –

+0

不需要每個頁面都有一個獨立的小部件。即該字段必須有兩個孩子小部件。我爲自己做了一個備忘錄來擴大acroform的例子。 –

回答

0

你想要的是有相同領域的sereval視覺表示。這是通過爲這樣的字段提供幾個註釋小部件來完成的。

在PDF中,當字段只有一個註釋小部件時,它們共享一個通用字典。當它有幾個時,註釋小部件位於該字段的子列表中。

當您需要一個字段的多個註釋小部件時,您需要使用new PDAnnotationWidget()創建註釋小部件,而不是調用field.getWidgets().get(0)並使用該註釋小部件。這些小部件必須添加到列表中,並且必須將該列表分配給setWidgets()的字段。對於每個小部件,您必須致電setRectangle()setPage()setParent()

一個例子是新的CreateMultiWidgetsForm.java example。在2.0.3中尚未提供setParent()方法(但將在2.0.4中)。在這個答案中,它被一個不那麼優雅地完成同樣事情的調用所取代。

public final class CreateMultiWidgetsForm 
{ 
    private CreateMultiWidgetsForm() 
    { 
    } 

    public static void main(String[] args) throws IOException 
    { 
     // Create a new document with 2 empty pages. 
     PDDocument document = new PDDocument(); 
     PDPage page1 = new PDPage(PDRectangle.A4); 
     document.addPage(page1); 
     PDPage page2 = new PDPage(PDRectangle.A4); 
     document.addPage(page2); 

     // Adobe Acrobat uses Helvetica as a default font and 
     // stores that under the name '/Helv' in the resources dictionary 
     PDFont font = PDType1Font.HELVETICA; 
     PDResources resources = new PDResources(); 
     resources.put(COSName.getPDFName("Helv"), font); 

     // Add a new AcroForm and add that to the document 
     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. 
     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 1st annotation associated with the field 
     PDAnnotationWidget widget1 = new PDAnnotationWidget(); 
     PDRectangle rect = new PDRectangle(50, 750, 250, 50); 
     widget1.setRectangle(rect); 
     widget1.setPage(page1); 
     widget1.getCOSObject().setItem(COSName.PARENT, textBox); 

     // Specify 2nd annotation associated with the field 
     PDAnnotationWidget widget2 = new PDAnnotationWidget(); 
     PDRectangle rect2 = new PDRectangle(200, 650, 100, 50); 
     widget2.setRectangle(rect2); 
     widget2.setPage(page2); 
     widget2.getCOSObject().setItem(COSName.PARENT, textBox); 

     // set green border and yellow background for 1st widget 
     // if you prefer defaults, just delete this code block 
     PDAppearanceCharacteristicsDictionary fieldAppearance1 
       = new PDAppearanceCharacteristicsDictionary(new COSDictionary()); 
     fieldAppearance1.setBorderColour(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE)); 
     fieldAppearance1.setBackground(new PDColor(new float[]{1,1,0}, PDDeviceRGB.INSTANCE)); 
     widget1.setAppearanceCharacteristics(fieldAppearance1); 

     // set red border and green background for 2nd widget 
     // if you prefer defaults, just delete this code block 
     PDAppearanceCharacteristicsDictionary fieldAppearance2 
       = new PDAppearanceCharacteristicsDictionary(new COSDictionary()); 
     fieldAppearance2.setBorderColour(new PDColor(new float[]{1,0,0}, PDDeviceRGB.INSTANCE)); 
     fieldAppearance2.setBackground(new PDColor(new float[]{0,1,0}, PDDeviceRGB.INSTANCE)); 
     widget2.setAppearanceCharacteristics(fieldAppearance2); 

     List <PDAnnotationWidget> widgets = new ArrayList<PDAnnotationWidget>(); 
     widgets.add(widget1); 
     widgets.add(widget2); 
     textBox.setWidgets(widgets); 

     // make sure the annotations are visible on screen and paper 
     widget1.setPrinted(true); 
     widget2.setPrinted(true); 

     // Add the annotations to the pages 
     page1.getAnnotations().add(widget1); 
     page2.getAnnotations().add(widget2); 

     // set the field value 
     textBox.setValue("Sample field"); 

     document.save("MultiWidgetsForm.pdf"); 
     document.close(); 
    } 
}