blocks/pdfvt1_with_blocks
Create a large number of invoices in a single PDF and make use of PDF/VT-1 features.
Download PHP Code Switch to Java Code Show Output Show Input (stationery_pdfx4p.pdf)
<?php
/**
* Create PDF/VT-1 with Blocks
*
* Create a large number of invoices in a single PDF and make use of
* the following PDF/VT-1 features:
* - create a document part (DPart) hierarchy
* - assign PDF/VT scope attributes to images and imported PDF pages
* - add document part metadata (DPM) to the DPart root node and all page nodes
*
* This topic is similar to starter_pdfvt1, but it uses PDFlib Blocks to
* place various text and image items on the page.
*
* Required software: PPS 10
* Required data: PDF background, fonts, several raster images
*
* @version
*/
class articledata_s {
function __construct($name, $price) {
$this->name = $name;
$this->price = $price;
}
};
class addressdata_s {
function __construct($firstname, $lastname, $flat, $street, $city) {
$this->firstname = $firstname;
$this->lastname = $lastname;
$this->flat = $flat;
$this->street = $street;
$this->city = $city;
}
};
$seed = 0x1234;
define('MAXRECORD', 100);
$stationeryfilename = "stationery_pdfx4p.pdf";
$salesrepfilename = "sales_rep%d.jpg";
$fontname = "NotoSerif-Regular";
// This is where font/image/PDF input files live. Adjust as necessary.
$searchpath = dirname(__FILE__,3)."/input";
/* By default annotations are also imported. In some cases this
* requires the Noto fonts for creating annotation appearance streams.
* We therefore set the searchpath to also point to the font directory.
*/
$fontpath = dirname(__FILE__,3)."/resource/font";
$title = "Create PDF/VT-1 with Blocks";
$outfile = "pdfvt1_with_blocks.pdf";
$left = 55;
$right = 530;
$bottom = 822;
$fontsize = 11;
$leading = $fontsize + 2;
$closingtext =
"Terms of payment: <save fillcolor={cmyk 0 1 1 0}>30 days net<restore>. " .
"90 days warranty starting at the day of sale. " .
"This warranty covers defects in workmanship only. " .
"Kraxi Systems, Inc. will, at its option, repair or replace the " .
"product under the warranty. This warranty is not transferable. " .
"No returns or exchanges will be accepted for wet products.";
$articledata = array(
new articledata_s("Super Kite", 20),
new articledata_s("Turbo Flyer", 40),
new articledata_s("Giga Trash", 180),
new articledata_s("Bare Bone Kit", 50),
new articledata_s("Nitty Gritty", 20),
new articledata_s("Pretty Dark Flyer", 75),
new articledata_s("Large Hadron Glider", 85),
new articledata_s("Flying Bat", 25),
new articledata_s("Simple Dimple", 40),
new articledata_s("Mega Sail", 95),
new articledata_s("Tiny Tin", 25),
new articledata_s("Monster Duck", 275),
new articledata_s("Free Gift", 0)
);
$addressdata = array(
new addressdata_s("Edith", "Poulard", "Suite C", "Main Street",
"New York"),
new addressdata_s("Max", "Huber", "", "Lipton Avenue",
"Albuquerque"),
new addressdata_s("Herbert", "Pakard", "App. 29", "Easel",
"Duckberg"),
new addressdata_s("Charles", "Fever", "Office 3", "Scenic Drive",
"Los Angeles"),
new addressdata_s("D.", "Milliband", "", "Old Harbour", "Westland"),
new addressdata_s("Lizzy", "Tin", "Workshop", "Ford", "Detroit"),
new addressdata_s("Patrick", "Black", "Backside",
"Woolworth Street", "Clover")
);
$salesrepnames = array(
"Charles Ragner",
"Hugo Baldwin",
"Katie Blomock",
"Ernie Bastel",
"Lucy Irwin",
"Bob Montagnier",
"Chuck Hope",
"Pierre Richard"
);
$headers = array(
"ITEM", "DESCRIPTION", "QUANTITY", "PRICE", "AMOUNT"
);
$alignments = array(
"right", "left", "right", "right", "right"
);
/**
* Simulate a datamatrix barcode
*/
define('MATRIXROWS', 32);
$MATRIXDATASIZE = (4*MATRIXROWS);
function create_datamatrix($record)
{
$datastring = "";
for ($i=0; $i<MATRIXROWS; $i++)
{
$data[$i][0] = ((0xA3 + 1*$record + 17*$i) % 0xFF);
$data[$i][1] = ((0xA2 + 3*$record + 11*$i) % 0xFF);
$data[$i][2] = ((0xA0 + 5*$record + 7*$i) % 0xFF);
$data[$i][3] = ((0x71 + 7*$record + 9*$i) % 0xFF);
}
for ($i=0; $i<MATRIXROWS; $i++)
{
$data[$i][0] |= 0x80;
$data[$i][2] |= 0x80;
if ($i%2) {
$data[$i][3] |= 0x01;
} else {
$data[$i][3] &= 0xFE;
}
}
for ($i=0; $i<4; $i++)
{
$data[MATRIXROWS/2-1][$i] = 0xFF;
$data[MATRIXROWS-1][$i] = 0xFF;
}
// pack the datamatrix into a string
for ($i=0; $i<MATRIXROWS; $i++) {
foreach ($data[$i] as $d) {
$datastring = $datastring.pack("C", $d);
}
}
return $datastring;
}
try {
$p = new PDFlib();
// all strings are expected as utf8
if ($p->begin_document("",
"pdfx=PDF/X-4 pdfvt=PDF/VT-1 usestransparency=false " .
"nodenamelist={root recipient} recordlevel=1") == 0)
{
echo("Error: " . $p->get_errmsg());
exit(1);
}
/*
* Set errorpolicy to return, this means we must check return values
* of load_font() etc. Set the search path for fonts and images etc.
*/
$p->set_option("errorpolicy=return SearchPath={" . $searchpath . "}");
$p->set_option("SearchPath={" . $fontpath . "}");
$p->set_info("Creator", "PDFlib Cookbook");
$p->set_info("Title", $title);
$fontoptions = "fontname=" . $fontname . " fontsize=" . $fontsize;
// Define output intent profile */
if ($p->load_iccprofile("ISOcoated_v2_eci.icc", "usage=outputintent") == 0)
{
printf("Error: %s\n", $p->get_errmsg());
echo("See www.pdflib.com for ICC profiles.\n");
exit(1);
}
/*
* Load company stationery as background (used on first page for
* each recipient)
*/
$stationery = $p->open_pdi_document($stationeryfilename, "");
if ($stationery == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
$page = $p->open_pdi_page($stationery, 1,
"pdfvt={scope=global environment={Kraxi Systems}}");
if ($page == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
/*
* Preload images of all local sales reps (used on first page
* for each recipient). To get encapsulated image XObjects, the
* renderingintent option is used.
*/
for ($i = 0; $i < count($salesrepnames); $i++)
{
$buf = sprintf($salesrepfilename, $i);
$salesrepimage[$i] = $p->load_image("auto", $buf,
"pdfvt={scope=file} renderingintent=Perceptual");
if ($salesrepimage[$i] == 0) {
echo("Error: " . $p->get_errmsg());
exit(1);
}
}
/*
* Construct DPM metadata for the DPart root node
*/
$cip4_metadata = $p->poca_new("containertype=dict usage=dpm");
$p->poca_insert($cip4_metadata,
"type=string key=CIP4_Conformance value=base");
$p->poca_insert($cip4_metadata,
"type=string key=CIP4_Creator value=pdfvt1_with_blocks");
$p->poca_insert($cip4_metadata,
"type=string key=CIP4_JobID value={Kraxi Systems invoice}");
$optlist = sprintf("containertype=dict usage=dpm " .
"type=dict key=CIP4_Metadata value=%d", $cip4_metadata);
$cip4_root = $p->poca_new($optlist);
$optlist = sprintf("containertype=dict usage=dpm " .
"type=dict key=CIP4_Root value=%d", $cip4_root);
$dpm = $p->poca_new($optlist);
// Create root node in the DPart hierarchy and add DPM metadata */
$optlist = sprintf("dpm=%d", $dpm);
$p->begin_dpart($optlist);
$p->poca_delete($dpm, "recursive=true");
for ($record = 0; $record < MAXRECORD; $record++) {
$pagecount = 0;
$firstname = $addressdata[get_random(count($addressdata))]->firstname;
$lastname = $addressdata[get_random(count($addressdata))]->lastname;
// -----------------------------------
// Construct DPM metadata for the next DPart node (i.e. the page)
// -----------------------------------
$dpm = $p->poca_new("containertype=dict usage=dpm");
$cip4_root = $p->poca_new("containertype=dict usage=dpm");
$cip4_recipient = $p->poca_new("containertype=dict usage=dpm");
$cip4_contact = $p->poca_new("containertype=dict usage=dpm");
$cip4_person = $p->poca_new("containertype=dict usage=dpm");
$optlist = sprintf("type=dict key=CIP4_Root value=%d", $cip4_root);
$p->poca_insert($dpm, $optlist);
$optlist = sprintf("type=dict key=CIP4_Recipient value=%d",
$cip4_recipient);
$p->poca_insert($cip4_root, $optlist);
$optlist = sprintf("type=string key=CIP4_UniqueID value={ID_%d}",
$record);
$p->poca_insert($cip4_recipient, $optlist);
$optlist = sprintf("type=dict key=CIP4_Contact value=%d", $cip4_contact);
$p->poca_insert($cip4_recipient, $optlist);
$optlist = sprintf("type=dict key=CIP4_Person value=%d", $cip4_person);
$p->poca_insert($cip4_contact, $optlist);
$optlist = sprintf("type=string key=CIP4_Firstname value={%s}",
$firstname);
$p->poca_insert($cip4_person, $optlist);
$optlist = sprintf("type=string key=CIP4_Lastname value={%s}",
$lastname);
$p->poca_insert($cip4_person, $optlist);
/*
* Create a new node in the document part hierarchy and add DPM
* metadata
*/
$optlist = "dpm=" . $dpm;
$p->begin_dpart($optlist);
$p->poca_delete($dpm, "recursive=true");
/*
* Create and place table with article list
*/
// -----------------------------------
// Create and place table with article list
// -----------------------------------
//
// ---------- Header row
$row = 1;
$tbl = 0;
for ($col = 1; $col <= count($headers); $col++)
{
$optlist = sprintf(
"fittextline={position={%s center} %s} margin=2",
$alignments[$col-1], $fontoptions);
$tbl = $p->add_table_cell($tbl, $col, $row, $headers[$col-1],
$optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
}
$row++;
// ---------- Data rows: one for each article
$total = 0;
// -----------------------------------
// Print variable-length article list
// -----------------------------------
for ($i = 0, $item = 0; $i < count($articledata); $i++) {
$quantity = get_random(9) + 1;
if ($item > 0 && get_random(100) > 50) continue;
$col = 1;
$item++;
$sum = $articledata[$i]->price * $quantity;
// column 1: ITEM
$buf = sprintf("%d", $item);
$optlist = sprintf(
"fittextline={position={%s center} %s} colwidth=5%% margin=2",
$alignments[$col-1], $fontoptions);
$tbl = $p->add_table_cell($tbl, $col++, $row, $buf, $optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
// column 2: DESCRIPTION
$optlist = sprintf(
"fittextline={position={%s center} %s} colwidth=50%% " .
"margin=2",
$alignments[$col-1], $fontoptions);
$tbl = $p->add_table_cell($tbl, $col++, $row,
$articledata[$i]->name, $optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
// column 3: QUANTITY
$buf = sprintf("%d", $quantity);
$optlist = sprintf(
"fittextline={position={%s center} %s} margin=2",
$alignments[$col-1], $fontoptions);
$tbl = $p->add_table_cell($tbl, $col++, $row, $buf, $optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
// column 4: PRICE
$buf = number_format($articledata[$i]->price, 2, ".", ",");
$optlist = sprintf(
"fittextline={position={%s center} %s} margin=2",
$alignments[$col-1], $fontoptions);
$tbl = $p->add_table_cell($tbl, $col++, $row, $buf, $optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
// column 5: AMOUNT
$buf = number_format($sum, 2, ".", ",");
$optlist = sprintf(
"fittextline={position={%s center} %s} margin=2",
$alignments[$col-1], $fontoptions);
$tbl = $p->add_table_cell($tbl, $col++, $row, $buf, $optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
$total += $sum;
$row++;
}
// ---------- Print total in the rightmost column
$buf = number_format($total, 2, ".", ",");
$optlist = sprintf(
"fittextline={position={%s center} %s} margin=2",
$alignments[count($alignments) - 1], $fontoptions);
$tbl = $p->add_table_cell($tbl, count($headers), $row++, $buf,
$optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
// ---------- Footer row with terms of payment
$optlist = sprintf("%s alignment=justify leading=120%%", $fontoptions);
$tf = $p->create_textflow($closingtext, $optlist);
if ($tf == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
$optlist = sprintf(
"rowheight=1 margin=2 margintop=%f textflow=%d colspan=%d",
2*$fontsize, $tf, count($headers));
$tbl = $p->add_table_cell($tbl, 1, $row++, "", $optlist);
if ($tbl == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
/* Place the table instance(s), creating pages as required */
do {
$p->begin_page_ext(0, 0,
"topdown=true width=a4.width height=a4.height");
if (++$pagecount == 1) {
/*
* Place company stationery as background on first page
* for each recipient
*/
$p->fit_pdi_page($page, 0, 842, "");
/*
* Fill Blocks with name and image of local sales rep on
* first page for each recipient
*/
$optlist = "";
$buf = "Local sales rep:\n"
. $salesrepnames[$record % count($salesrepnames)];
if ($p->fill_textblock($page, "salesrepname", $buf,
$optlist) == 0)
print("Warning: " . $p->get_errmsg());
if ($p->fill_imageblock($page, "salesrepimage",
$salesrepimage[$record % count($salesrepnames)], "") == 0)
print("Warning: " . $p->get_errmsg());
/*
* Fill Text Blocks with recipient's address
*/
$optlist = "";
$buf = $firstname . " " . $lastname;
if ($p->fill_textblock($page, "name", $buf, $optlist) == 0)
print("Warning: " . $p->get_errmsg());
$buf = $addressdata[get_random(count($addressdata))]->flat;
if ($p->fill_textblock($page, "flat", $buf, $optlist) == 0)
print("Warning: " . $p->get_errmsg());
$buf = sprintf("%d %s",
get_random(999),
$addressdata[get_random(count($addressdata))]->street);
if ($p->fill_textblock($page, "street", $buf,
$optlist) == 0)
print("Warning: " . $p->get_errmsg());
$buf = sprintf("%05d %s",
get_random(99999),
$addressdata[get_random(count($addressdata))]->city);
if ($p->fill_textblock($page, "city", $buf, $optlist) == 0)
print("Warning: " . $p->get_errmsg());
/*
* Fill image Block with barcode image for each
* recipient. To get encapsulated image XObjects the
* renderingintent option is used.
*/
$datamatrix = create_datamatrix($record);
$p->create_pvf("barcode", $datamatrix, "");
$barcodeimage = $p->load_image("raw", "barcode",
"bpc=1 components=1 width=32 height=32 invert "
. "pdfvt={scope=singleuse} "
. "renderingintent=Saturation");
if ($barcodeimage == 0) {
throw new Exception("Error: " . $p->get_errmsg());
}
if ($p->fill_imageblock($page, "barcode", $barcodeimage,
"") == 0)
print("Warning: " . $p->get_errmsg());
$p->close_image($barcodeimage);
$p->delete_pvf("barcode");
/*
* Fill Text Blocks with header and date
*/
date_default_timezone_set('Europe/Berlin');
$optlist = "";
$buf = sprintf("INVOICE %d-%d", date("Y"), $record+1);
if ($p->fill_textblock($page, "invoice_number", $buf,
$optlist) == 0)
print("Warning: " . $p->get_errmsg());
$buf = date("F j, Y");
if ($p->fill_textblock($page, "date", $buf, $optlist) == 0)
print("Warning: " . $p->get_errmsg());
/*
* Retrieve y coordinate of the lowest Block
* "invoice_number" so that we know where subsequent
* data can be placed.
*/
$top = $p->pcos_get_number($stationery,
"pages[0]/blocks/invoice_number/Rect[1]");
// adjust for topdown coordinate system
$top = 842 - $top + 2 * $leading;
}
else {
$top = 50;
}
/*
* Place the table on the page. Shade every other row,
* except the footer row.
*/
$result = $p->fit_table($tbl, $left, $bottom, $right, $top,
"header=1 "
. "fill={{area=rowodd fillcolor={gray 0.9}} "
. "{area=rowlast fillcolor={gray 1}}} "
. "rowheightdefault=auto colwidthdefault=auto");
if ($result == "_error") {
throw new Exception(
"Couldn't place table: " . $p->get_errmsg());
}
$p->end_page_ext("");
}
while ($result == "_boxfull");
$p->delete_table($tbl, "");
/* Close node in the document part hierarchy */
$p->end_dpart("");
}
$p->close_pdi_page($page);
$p->close_pdi_document($stationery);
for ($i = 0; $i < count($salesrepimage); $i++) {
$p->close_image($salesrepimage[$i]);
}
/* Close root node in the document part hierarchy */
$p->end_dpart("");
$p->end_document("");
$buf = $p->get_buffer();
$len = strlen($buf);
header("Content-type: application/pdf");
header("Content-Length: $len");
header("Content-Disposition: inline; filename=" . $outfile);
print $buf;
}
catch (PDFlibException $e) {
echo("PDFlib exception occurred in pdfvt1_with_blocks sample:\n" .
"[" . $e->get_errnum() . "] " . $e->get_apiname() . ": " .
$e->get_errmsg() . "\n");
exit(1);
}
catch (Throwable $e) {
echo($e);
exit(1);
}
$p = 0;
/**
* Get a pseudo random number between 0 and n-1
*/
function get_random($n) {
global $seed;
$seed = ($seed *0xDEECE66D + 11) & 0x7FFFFFFF;
return ($seed % $n);
}
?>