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/