Thanks for the review and the helpful tips, Bruno!

I built them in, and inspired by them, cleaned up a couple other things,
including handling a template where the long text field is not on the last page,
but an earlier one.

With the hope that it may help someone else, I include a slightly modified
version of the code below.  I have tried to consolidate the comments, and hope I
have not clouded anything by so doing.

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.util.Iterator;

import com.lowagie.text.pdf.AcroFields;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfImportedPage;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.XfdfReader;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;

/*

  This example handles the following situation:

  PDF documents are to be produced by "merging" dynamic data from the
  database with static pages of a "template" pdf, containing form fields.

  The values to be used in filling in the form are in an xfdf file.

  This is normally simple to do using PdfStamper.

  In this case, however, one page of the template pdf includes, among other
  fields, a large text field, named "long_sow" ("Long Statement of Work");
  the runtime value (in the xfdf file) for this field may be too long to fit
  on the single page of the template.  If this happens, additional pages
  should should be inserted to accomodate the "overflow".

  In addition, page numbering should be added, e.g. Page x of y.

  The solution involves 2 passes:

  Pass 1 reads the template, and uses a PdfStamper to stamp and flatten all
  the fields except the long one "long_sow", (that will be handled separately
  in pass 2).  The result is a pdf with as many pages as the template; all
  fields have been flattened, and all but the long field have been stamped
  with the runtime value.  During this pass, information about the long field
  is retrieved and saved, prior to the flattening.

  Pass 2 reads the output from pass 1, and uses a ColumnText object to
  progressively add the long text.  The ColumnText is originally positioned
  on the page where the long field was defined.  As long as there is more
  text remaining, an additional page is inserted, and the ColumnText object
  is repositioned on the inserted page.  The go() method of the ColumnText
  object keeps track of the text remaining.

  Pass 2 also adds the page numbering.

  Output from each pass is kept in a ByteArrayOutputStream, so that it may be
  easily piped to the subsequent pass.

  Once the processing is complete, the results are written to the output file.

 */


public class FillFormWithLongSow {

    public static void main(String[] args) {
        final String templateFileName = "mp_template-a.pdf";            // 5
pages; page 4 has field named "long_sow" which may overflow at runtime
        final String dataFileName     = "sample-mp-with-long-sow.xfdf"; //
Includes long value for field "long_sow"
        final String outputFileName   = "sample-mp-with-long-sow.pdf";

        String sow = null;

                try {
            // Pass 1: read the template, fill in all fields other than the
            // long text field ("long_sow"), with flattening.

            PdfReader reader1 = new PdfReader(templateFileName);
            ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
            PdfStamper stamper1 = new PdfStamper(reader1, baos1);
            stamper1.setFormFlattening(true);

            XfdfReader dataReader = new XfdfReader(dataFileName);
            AcroFields form = stamper1.getAcroFields();
            String fieldName;
            for (Iterator i = form.getFields().keySet().iterator(); 
i.hasNext(); ) {
                fieldName = (String)i.next();
                if ( fieldName.equals("long_sow") ) {
                    sow = dataReader.getField(fieldName);
                } else {
                    form.setField(fieldName,dataReader.getField(fieldName));
                }
            }

            // Collect information about the long text field for use in pass 2
            // Need: number of page on which long text field occurs, and
            // bounding rectangle
            float[] sowFieldPositions = form.getFieldPositions("long_sow");
            if ( sowFieldPositions == null ) {
                System.err.println("no field positions found for long_sow");
                System.exit(1);
            } else if (sowFieldPositions.length != 5) {
                System.err.println("unexpected number of values for long_sow
field positions: " + sowFieldPositions.length);
                System.exit(1);
            }
            int sowPageNumber = (int) sowFieldPositions[0];
            float sowLlx = sowFieldPositions[1];
            float sowLly = sowFieldPositions[2];
            float sowUrx = sowFieldPositions[3];
            float sowUry = sowFieldPositions[4];

            stamper1.close(); // end of pass 1

            // Pass 2: starting with the output from pass 1, use a ColumnText
            // object to progressively add the value for the long text field.
            // Start on the page where the field is defined.  If more space
            // is needed, insert additional pages, initializing them with the
            // content (from pass 1) of the page where the long text field is
            // defined, and adding as much of the remaining text as possible

            PdfReader reader2 = new PdfReader(baos1.toByteArray());
            ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
            PdfStamper stamper2 = new PdfStamper(reader2, baos2);

            // Get the initial contents of the page with the long text field.
            // It will be used to initialize new pages inserted to accomodate
            // overflow from the long text field.
            PdfImportedPage sowPage =
stamper2.getImportedPage(reader2,sowPageNumber);

            int pageCt = reader2.getNumberOfPages();

            // Using ColumnText, add as much of the long text as fits on the 
page
            // where the field is defined in the template
            PdfContentByte cb = stamper2.getOverContent(sowPageNumber);
            Rectangle sowPageSize = 
reader2.getPageSizeWithRotation(sowPageNumber);
            ColumnText ct = new ColumnText(cb);
            ct.setSimpleColumn(sowLlx,sowLly,sowUrx,sowUry);
            ct.setText(new Phrase(sow));
            int status = ct.go();

            // As long as there is is more text from the long text field to add,
            // insert a new page, initialize it with the contents of the first
            // page (the page where the field is defined), and add as much text
            // as possible.  Keep track of total number of pages.
            while ( ColumnText.hasMoreText(status) ) {
                stamper2.insertPage(++sowPageNumber, sowPageSize);
                pageCt++;
                // initialize the new page with the content other than the long 
text
                cb = stamper2.getOverContent(sowPageNumber);
                cb.addTemplate(sowPage,0,0);
                // reposition the ColumnText to the new page, reset the Y line,
and go
                ct.setCanvas(cb);
                ct.setYLine(sowUry);
                status = ct.go();
            }

            // add page numbering
            BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA,
BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
            for (int pageNo = 1; pageNo <= pageCt; pageNo++) {
                cb = stamper2.getOverContent(pageNo);
                cb.beginText();
                cb.setFontAndSize(bf, 12);
                cb.setTextMatrix(522, 10);
                cb.showText("Page " + pageNo + " of " + pageCt);
                cb.endText();
            }

            stamper2.close(); // end of pass 2

            // write output to file
            FileOutputStream fos = new FileOutputStream(outputFileName);
            baos2.writeTo(fos);
            fos.close();
            System.out.println("done");

                } catch (Exception e) {
                        e.printStackTrace();
                }

    }
}







-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
iText-questions mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/itext-questions
Buy the iText book: http://itext.ugent.be/itext-in-action/

Reply via email to