Yes, Full Text Search!
Posted 2009-08.
In Part 1 we retrieved a PDF template's form field coordinates. Let's put them to use and create simple multi-page billing statement.
Implement PdfPageEvent
First we're going to create a simple helper class by implementing PdfPageEvent. The purpose of the helper class is to:
- Read the templates so they are only used one time each. This reduces overhead and file size.
- Add the stored template page everytime a new page is created; in a real world scenario you'll probably be getting the customer's transactions from a database and won't know in advance how many pages the billing statement will require.
public class event_helper : PdfPageEventHelper {
public const string PDF_TEMPLATE = "/cs/itext/bill_template.pdf";
public const string PDF_TEMPLATE2 = "/cs/itext/bill_template2.pdf";
public PdfImportedPage template;
public int start_page_count = 1;
// -------------------------------------------------------------------------
// read/save PDF template page **one time**
public override void OnOpenDocument(PdfWriter writer, Document doc) {
PdfReader reader = get_reader(PDF_TEMPLATE);
template = writer.GetImportedPage(reader, 1);
reader.Close();
}
// ----------------------------------------------------------------------------
public override void OnEndPage(PdfWriter writer, Document document) {
// second page on uses different template
if (start_page_count == 2) {
PdfReader reader = get_reader(PDF_TEMPLATE2);
template = writer.GetImportedPage(reader, 1);
reader.Close();
}
writer.DirectContentUnder.AddTemplate(template, 0, 0);
++start_page_count;
}
// -------------------------------------------------------------------------
public PdfReader get_reader(string pdf_template) {
return new PdfReader(new RandomAccessFileOrArray(
HttpContext.Current.Server.MapPath(pdf_template)
), null
);
}
}
Send Results to Client
iText makes this extremely easy for us; like the
book says it's a five-step process:
- Create a
Document object.
- Get a
PdfWriter so we can use the templates.
- Open the
Document.
- Add content; the code for
_do_form_fields(), _get_transaction_details(), and _transaction_summary() are omitted, since they only return strings to add to ColumnText. ColumnText is smart; each call to Go() renders as much text that will fit on the current page and returns a status code that tells you: (1) how much text (to write) is remaining, and/or (2) how much space is still available on the page. On each iteration you add text to the current page, call ColumnText.HasMoreText() to inspect the status, and then Document.NewPage() if necessary.
- Close the
Document.
// [1]
var document = new Document(PageSize.LETTER);
try {
// [2] second parameter specific to ASP.NET
var writer = PdfWriter.GetInstance(document, resp.OutputStream);
writer.PageEvent = new event_helper();
// [3]
document.Open();
// [4]
var column_text = new ColumnText(writer.DirectContent);
_do_form_fields(column_text, fill_font);
_init_column_text(column_text, account_activity1);
column_text.AddText(new Phrase(
_get_transaction_details().ToString(),
fill_font
));
column_text.AddText(_transaction_summary());
do {
column_text.Go();
_init_column_text(column_text, account_activity2);
document.NewPage();
} while ( ColumnText.HasMoreText(column_text.Go()) );
}
catch { throw; }
// [5]
finally { if (document != null) document.Close(); }
_init_column_text() does need more explanation.
ColumnText needs some way to calculate how much space it has to work with. Do this
before you start adding text - that's where the coordinates for the templates in
Part 1 come in:
// [1] combined transaction details
public readonly float[] account_activity1 = {62.9f, 48.7f, 549.3f, 550.3f};
public readonly float[] account_activity2 = {62.9f, 50f, 549.3f, 708f};
// [2] stand-alone values
public readonly float[] address_to = {149.3f, 620.1f, 398.7f, 696.1f};
public readonly float[] date_due = {311.4f, 725.5f, 425.5f, 738.9f};
public readonly float[] due_balance = {443.6f, 724.8f, 554.9f, 738.9f};
they're passed to SetSimpleColumn() to specify the exact available rectangular area:
/* lower left x-coordinate
* lower left y-coordinate
* upper right x-coordinate
* upper right y-coordinate
*/
private void _init_column_text(ColumnText ct, float[] coord) {
ct.SetSimpleColumn(
coord[0], coord[1], coord[2], coord[3],
12.0F, Element.ALIGN_LEFT
);
}
We use an overloaded signature of
SetSimpleColumn(), passing a
Phrase as the first parameter, to set the "stand-alone" ([2] above) values. See the
column_text.AddText() call above if you don't know how to create a
Phrase.
On to the demo.