pdfua/clone_pdfua updated
Clone PDF/A, PDF/UA and PDF/X standard documents.
Download PHP Code Switch to Java Code Show Output
<?php
/*
*
* Clone PDF/A, PDF/UA and PDF/X standard documents
* This is useful as basis for additional processing,
* such as stamping, adding XMP metadata, adding page content, etc.
*
* The following aspects of the input document are cloned:
* - PDF/A, PDF/UA and PDF/X version
* - PDF/A or PDF/X output intent (if present)
* - document language (if present)
* - all pages including page geometry, i.e. page boxes and Rotate key
* - the structure elements (tags); if required, an additional
* tag is inserted on top of the imported page elements
* - XMP document metadata
* This will generally also clone document info fields since these are
* synchronized with XMP in the majority of modern PDF documents.
*
* To demonstrate coordinate transformations which may be required
* to add new page content this topic adds a stamp across all pages.
*
* Input documents may conform to any combination of PDF/A, PDF/UA
* and PDF/X simultaneously.
*
* Note: Except for the names of the input and output documents the three
* Cookbook topics clone_pdfa, clone_pdfua and clone_pdfx are exact copies.
* They are included multiply so that they can easily be found in the
* PDF/A, PDF/UA and PDF/X categories.
*
* required software: PDFlib+PDI/PPS 10
* required data: PDF/A, PDF/UA or PDF/X input document
*/
/* This is where the data files are. 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";
$pdfinputfile = "PDFUA-invoice.pdf";
$title = "clone_pdfua";
$optlist = "";
/* The following standard flavors can be cloned: */
$supportedflavors = array(
"PDF/A-1a:2005", "PDF/A-1b:2005",
"PDF/A-2a", "PDF/A-2b", "PDF/A-2u",
"PDF/A-3a", "PDF/A-3b", "PDF/A-3u",
"PDF/X-3:2003",
"PDF/X-4", "PDF/X-4p",
"PDF/X-5g", "PDF/X-5pg", "PDF/X-5n",
"PDF/UA-1",
"none",
);
try {
$p = new pdflib();
$p->set_option("searchpath={" . $searchpath . "}");
$p->set_option("searchpath={" . $fontpath . "}");
/* This means we must check return values of load_font() etc. */
$p->set_option("errorpolicy=return");
/*
* Open the input PDF, preserve tags if present (for cloning
* PDF/A-1/2/3a and PDF/UA)
*/
$indoc = $p->open_pdi_document($pdfinputfile, "usetags=true");
if ($indoc == 0) {
throw new Exception("Error: " . $p->get_apiname() . ": "
. $p->get_errmsg());
}
/*
* Read PDF/A, PDF/UA and PDF/X version of the input document
*/
$pdfaversion = $p->pcos_get_string($indoc, "pdfa");
$pdfuaversion = $p->pcos_get_string($indoc, "pdfua");
$pdfxversion = $p->pcos_get_string($indoc, "pdfx");
for ($i = 0; $i < count($supportedflavors); $i++) {
if ($pdfaversion == $supportedflavors[$i]) {
$optlist .= " pdfa=" . $pdfaversion;
break;
}
}
if ($i == count($supportedflavors))
throw new Exception("Error: Cannot clone " . $pdfaversion
. " documents");
for ($i = 0; $i < count($supportedflavors); $i++) {
if ($pdfuaversion == $supportedflavors[$i]) {
$optlist .= " pdfua=" . $pdfuaversion;
break;
}
}
if ($i == count($supportedflavors))
throw new Exception("Error: Cannot clone " . $pdfuaversion
. " documents");
for ($i = 0; $i < count($supportedflavors); $i++) {
if ($pdfxversion == $supportedflavors[$i]) {
$optlist .= " pdfx=" . $pdfxversion;
break;
}
}
if ($i == count($supportedflavors))
throw new Exception("Error: Cannot clone " . $pdfxversion
. " documents");
/*
* Read language entry of the input document if present
*/
if ($p->pcos_get_string($indoc, "type:/Root/Lang") == "string") {
$inputlang = $p->pcos_get_string($indoc, "/Root/Lang");
$optlist .= " lang=" . $inputlang;
}
else if ($pdfuaversion == "PDF/UA-1")
{
/* PDF/UA documents don't necessarily need the /Lang entry
* in the Catalog, but PDFlib requires the "lang" option.
* We supply a default language (which may be wrong) to
* ensure that such documents can be cloned nevertheless.
*/
define("DEFAULT_LANGUAGE", "en");
$optlist .= " lang=" . DEFAULT_LANGUAGE;
}
else
$inputlang = "";
/*
* Clone XMP metadata of input document if present
*/
if ($p->pcos_get_string($indoc, "type:/Root/Metadata") == "stream") {
$xmp = $p->pcos_get_stream($indoc, "", "/Root/Metadata");
$p->create_pvf("/xmp/document.xmp", $xmp, "");
$optlist .= " metadata={filename=/xmp/document.xmp}";
}
/*
* Read Tagged status of input document
*/
$taggedinput = $p->pcos_get_string($indoc, "tagged") == "true";
if ($taggedinput)
$optlist .= " tag={tagname=Document}";
/*
* Create a new document and clone PDF/A, PDF/UA and PDF/X status
*/
if ($p->begin_document("", $optlist) == 0)
throw new Exception("Error: " . $p->get_apiname() . ": "
. $p->get_errmsg());
$p->set_info("Creator", "PDFlib Cookbook");
$p->set_info("Title", $title);
/*
* Clone PDF/A or PDF/X output intent
*/
if ($p->process_pdi($indoc, -1, "action=copyoutputintent")== 0){
throw new Exception("Error: " . $p->get_apiname() . ": "
. $p->get_errmsg());
}
/* Create a bookmark with the name of the input document */
$p->create_bookmark($pdfinputfile, "");
$endpage = (int) $p->pcos_get_number($indoc, "length:pages");
/* Copy all pages of the input document */
for ($pageno = 1; $pageno <= $endpage; $pageno++) {
$lowerleftcorner = array(
array( "x1", "y1" ), /* 0 degrees */
array( "x2", "y2" ), /* 90 degrees */
array( "x3", "y3" ), /* 180 degrees */
array( "x4", "y4" ), /* 270 degrees */
);
$page = $p->open_pdi_page($indoc, $pageno, "cloneboxes");
if ($page == 0) {
throw new Exception("Error opening page: " . $p->get_errmsg());
}
/*
* Query the geometry of the cloned page. This is required to
* account for translated or rotated pages if we want to add
* more contents to the page.
*/
$phi = $p->info_pdi_page($page, "rotate", "");
/*
* Select the lower left corner depending on the rotation angle
*/
$x = $p->info_pdi_page($page, $lowerleftcorner[intval($phi / 90)][0],
"");
$y = $p->info_pdi_page($page, $lowerleftcorner[intval($phi / 90)][1],
"");
$fittingpossible = true;
$additionaltag = "";
if ($taggedinput) {
$topleveltagcount = (int) $p->info_pdi_page($page,
"topleveltagcount", "");
if ($topleveltagcount == 0) {
/*
* The page doesn't contain any structure elements, i.e.
* it is empty or contains only Artifacts. Some
* applications may decide to skip such pages.
*
* We add an "Artifact" tag to work around an Acrobat
* bug.
*/
$additionaltag = "tag={tagname=Artifact} ";
}
else if ($p->info_pdi_page($page, "fittingpossible", "") == 0) {
/*
* Try to place the page without any additional tag; if
* this doesn't work we insert another tag.
*/
$additionaltag = "tag={tagname=P} ";
if ($p->info_pdi_page($page, "fittingpossible",
$additionaltag) == 0) {
$fittingpossible = false;
}
}
}
if ($fittingpossible) {
/* Page size may be adjusted by "cloneboxes" option */
$p->begin_page_ext(0, 0, "width=a4.width height=a4.height");
$optlist = "cloneboxes ";
if ($taggedinput)
$optlist .= $additionaltag;
$p->fit_pdi_page($page, 0, 0, $optlist);
/*
* Adjust the coordinate system to facilitate adding new
* page content on top of the cloned page.
*/
$p->translate($x, $y);
$p->rotate($phi);
$width = $p->info_pdi_page($page, "pagewidth", "");
$height = $p->info_pdi_page($page, "pageheight", "");
/*
* Add some text on each page and tag it as Artifact.
*/
$optlist = "fontname=NotoSerif-Regular "
. "textrendering=1 stamp=ll2ur "
. "boxsize={" . $width . " " . $height . "}";
if ($taggedinput)
$optlist .= " tag={tagname=Artifact}";
$p->fit_textline("Cloned page", 0, 0, $optlist);
$p->end_page_ext("");
}
else {
throw new Exception("Cannot fit page " . $pageno . " of '"
. $pdfinputfile . "': " . $p->get_errmsg());
}
$p->close_pdi_page($page);
}
$p->end_document("");
$p->delete_pvf("/xmp/document.xmp");
$p->close_pdi_document($indoc);
$buf = $p->get_buffer();
$len = strlen($buf);
header("Content-type: application/pdf");
header("Content-Length: $len");
header("Content-Disposition: inline; filename=" . $title . ".pdf");
print $buf;
} catch (PDFlibException $e) {
echo("PDFlib exception occurred:\n".
"[" . $e->get_errnum() . "] " . $e->get_apiname() .
": " . $e->get_errmsg() . "\n");
exit(1);
} catch (Throwable $e) {
echo("PHP exception occurred: " . $e->getMessage() . "\n");
exit(1);
}
$p = 0;
?>