dataset-gnunux/seed/galette/manual/image/postinstall/galette/includes/phppdflib/phppdflib.class.php
2023-02-14 14:48:25 +01:00

1687 lines
56 KiB
PHP

<?php
/*
php pdf generation library
Copyright (C) Potential Technologies 2002 - 2003
http://www.potentialtech.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
$Id: phppdflib.class.php 7 2004-02-11 20:44:53Z gruiick $
*/
class pdffile
{
/* $objects is an array that stores the objects
* that will become the pdf when ->generate()
* is called.
* The layout of ->objects does not directly
* mimic the pdf format, although it is similar.
* nextoid always holds the next available oid ( oid is short for object id )
*/
var $objects, $nextoid;
/* xreftable is an array containing data to
* create the xref section (PDF calls it a referance table)
*/
var $xreftable, $nextobj;
/* These arrays allow quick translation between
* pdflib OIDs and the final PDF OIDs (OID stands for object ids)
*/
var $libtopdf, $pdftolib;
// Errors
var $ermsg = array(), $erno = array();
var $builddata; // Various data required during the pdf build
var $nextpage; // Tracks the next page number
var $widths, $needsset; // Store the font width arrays here
var $default; // Default values for objects
var $x, $chart, $template,
$packer, $import; // extension class is instantiated here
var $debug = 0, $dbs = ''; // Default debug level, higher = more data
/* Constructor function: is automatically called when the
* object is created. Used to set up the environment
*/
function pdffile()
{
/* Per spec, obj 0 should always have a generation
* number of 65535 and is always free
*/
$this->xreftable[0]["gennum"] = 65535;
$this->xreftable[0]["offset"] = 0;
$this->xreftable[0]["free"] = "f";
// Object #1 will always be the Document Catalog
$this->xreftable[1]["gennum"] = 0;
$this->xreftable[1]["free"] = "n";
// Object #2 will always be the root pagenode
$this->xreftable[2]["gennum"] = 0;
$this->xreftable[2]["free"] = "n";
$this->pdftolib[2] = 1;
$this->libtopdf[1] = 2;
// Object #3 is always the resource library
$this->xreftable[3]["gennum"] = 0;
$this->xreftable[3]["free"] = "n";
/* nextoid starts at 2 because all
* drawing functions return either the
* object ID or FALSE on error, so we can't
* return an OID of 0, because it equates
* to false and error checking would think
* the procedure failed
*/
$this->nextoid = 2;
$this->nextobj = 3;
// Pages start at 0
$this->nextpage = 0;
// Font width tables are not set unless they are needed
$this->needsset = true;
// Set all the default values
$t['pagesize'] = 'letter';
$t['font'] = 'Helvetica';
$t['height'] = 12;
$t['align'] = 'left';
$t['width'] = 1;
$t['rotation'] = 0;
$t['scale'] = 1;
$t['strokecolor'] = $this->get_color('black');
$t['fillcolor'] = $this->get_color('black');
$t['margin-left'] = $t['margin-right'] = $t['margin-top'] = $t['margin-bottom'] =72;
$t['tmode'] = 0; // Text: fill
$t['smode'] = 1; // Shapes: stroke
$this->default = $t;
}
/******************************************************
* These functions are the public ones, they are the *
* way that the user will actually enter the data *
* that will become the pdf *
******************************************************/
function set_default($setting, $value)
{
switch ($setting) {
case 'margin' :
$this->default['margin-left'] = $value;
$this->default['margin-right'] = $value;
$this->default['margin-top'] = $value;
$this->default['margin-bottom'] = $value;
break;
case 'mode' :
$this->default['tmode'] = $this->default['smode'] = $value;
break;
default :
$this->default[$setting] = $value;
}
return true;
}
function draw_rectangle($top, $left, $bottom, $right, $parent, $attrib = array())
{
if ($this->objects[$parent]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
$o = $this->_addnewoid();
$attrib = $this->_resolve_param($attrib, false);
$this->_resolve_colors($n, $attrib);
$this->objects[$o] = $n;
$this->objects[$o]["width"] = $attrib["width"];
$this->objects[$o]["type"] = "rectangle";
$this->_adjust_margin($left, $top, $parent);
$this->_adjust_margin($right, $bottom, $parent);
$this->objects[$o]["top"] = $top;
$this->objects[$o]["left"] = $left;
$this->objects[$o]["bottom"] = $bottom;
$this->objects[$o]["right"] = $right;
$this->objects[$o]["parent"] = $parent;
$this->objects[$o]["mode"] = $this->_resolve_mode($attrib, 'smode');
$this->_debug_var(10, "Placed rectangle: ", $this->objects[$o]);
return $o;
}
function draw_circle($x, $y, $r, $parent, $attrib = array())
{
if ($this->objects[$parent]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
$o = $this->_addnewoid();
$attrib = $this->_resolve_param($attrib, false);
$this->_resolve_colors($n, $attrib);
$n['width'] = $attrib['width'];
$this->_adjust_margin($x, $y, $parent);
$n['x'] = $x;
$n['y'] = $y;
$n['radius'] = $r;
$n['type'] = 'circle';
$n['parent'] = $parent;
$n['mode'] = $this->_resolve_mode($attrib, 'smode');
$this->objects[$o] = $n;
$this->_debug_var(10, "Placed circle ", $o);
return $o;
}
function draw_line($x, $y, $parent, $attrib = array())
{
if ($this->objects[$parent]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
if (count($x) != count($y)) {
$this->_push_error(6002, "X & Y variables must have equal number of elements");
return false;
}
$o = $this->_addnewoid();
$attrib = $this->_resolve_param($attrib, false);
$this->_resolve_colors($n, $attrib);
$this->objects[$o] = $n;
@$this->objects[$o]["width"] = $attrib["width"];
$this->objects[$o]['mode'] = $this->_resolve_mode($attrib, 'smode');
$this->objects[$o]["type"] = "line";
foreach ($x as $key => $value) {
if (isset($x[$key]) && isset($y[$key])) {
$this->_adjust_margin($x[$key], $y[$key], $parent);
}
}
$this->objects[$o]["x"] = $x;
$this->objects[$o]["y"] = $y;
$this->objects[$o]["parent"] = $parent;
$this->_debug_var(10, 'Placed line', $this->objects[$o]);
return $o;
}
// draw text
function draw_text($left, $bottom, $text, $parent, $attrib = array())
{
if ($this->objects[$parent]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
$attrib = $this->_resolve_param($attrib);
// Validate the font
if (!($n["font"] = $this->_use_font($attrib))) {
// Couldn't find/add the font
$this->_push_error(6003, "Font was not found");
return false;
}
if (isset($attrib["rotation"])) {
$n["rotation"] = $attrib["rotation"];
}
$n['mode'] = $this->_resolve_mode($attrib, 'tmode');
if (isset($attrib["height"]) && $attrib["height"] > 0) {
$n["height"] = $attrib["height"];
}
$this->_resolve_colors($n, $attrib);
$n["type"] = "texts";
$this->_adjust_margin($left, $bottom, $parent);
$n["left"] = $left;
$n["bottom"] = $bottom;
$n["text"] = $text;
$n["parent"] = $parent;
$o = $this->_addnewoid();
$this->objects[$o] = $n;
$this->_debug_var(10, 'Placed text', $n);
return $o;
}
function new_page($size = null)
{
if (is_null($size)) {
$size = $this->default['pagesize'];
}
switch ($size) {
case "letter" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 792;
$this->objects[$o]["width"] = 612;
break;
case "legal" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 1008;
$this->objects[$o]["width"] = 612;
break;
case "executive" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 720;
$this->objects[$o]["width"] = 540;
break;
case "tabloid" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 1224;
$this->objects[$o]["width"] = 792;
break;
case "a3" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 1188;
$this->objects[$o]["width"] = 842;
break;
case "a4" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 842;
$this->objects[$o]["width"] = 595;
break;
case "a4-landscape" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 595;
$this->objects[$o]["width"] = 842;
break;
case "a5" :
$o = $this->_addnewoid();
$this->objects[$o]["height"] = 598;
$this->objects[$o]["width"] = 418;
break;
default :
if (preg_match("/in/",$size)) {
$o = $this->_addnewoid();
$size = substr($size, 0, strlen($size) - 2);
$dims = split("x",$size);
$this->objects[$o]["height"] = ($dims[1] * 72);
$this->objects[$o]["width"] = ($dims[0] * 72);
} else {
if (preg_match("/cm/",$size)) {
$o = $this->_addnewoid();
$size = substr($size, 0, strlen($size) - 2);
$dims = split("x",$size);
$this->objects[$o]["height"] = ($dims[1] * 28.346);
$this->objects[$o]["width"] = ($dims[0] * 28.346);
} else {
$this->_push_error(6004, "Could not deciper page size description: $size");
return false;
}
}
}
$this->objects[$o]['type'] = 'page';
$this->objects[$o]['parent'] = 1;
$this->objects[$o]['number'] = $this->nextpage;
$this->nextpage ++;
foreach (array('margin-left', 'margin-right', 'margin-top', 'margin-bottom') as $margin) {
$this->objects[$o][$margin] = $this->default[$margin];
}
$this->_debug_var(10, 'New page', $this->objects[$o]);
return $o;
}
function swap_pages($p1, $p2)
{
if ($this->objects[$p1]["type"] != "page" ||
$this->objects[$p2]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
$temp = $this->objects[$p1]["number"];
$this->objects[$p1]["number"] = $this->objects[$p2]["number"];
$this->objects[$p2]["number"] = $temp;
return true;
}
function move_page_before($page, $infrontof)
{
if ($this->objects[$page]["type"] != "page" ||
$this->objects[$infrontof]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
if ($page == $infrontof) {
$this->_push_error(6005, "You're trying to swap a page with itself");
return false;
}
$target = $this->objects[$infrontof]["number"];
$leaving = $this->objects[$page]["number"];
foreach ($this->objects as $id => $o) {
if ($o["type"] == "page") {
if ($target < $leaving) {
if ($o["number"] >= $target && $o["number"] < $leaving) {
$this->objects[$id]["number"]++;
}
} else {
if ($o["number"] < $target && $o["number"] > $leaving) {
$this->objects[$id]["number"]--;
}
}
}
}
if ($target < $leaving) {
$this->objects[$page]["number"] = $target;
} else {
$this->objects[$page]["number"] = $target - 1;
}
return true;
}
function new_font($identifier)
{
$n["type"] = "font";
switch ($identifier) {
/* The "standard" Type 1 fonts
* These are "guaranteed" to be available
* to the viewer application and don't
* need embedded
*/
case "Courier":
case "Courier-Bold":
case "Courier-Oblique":
case "Courier-BoldOblique":
case "Helvetica":
case "Helvetica-Bold":
case "Helvetica-Oblique":
case "Helvetica-BoldOblique":
case "Times-Roman":
case "Times-Bold":
case "Times-Italic":
case "Times-BoldItalic":
case "Symbol":
case "ZapfDingbats":
$o = $this->_addnewoid();
$this->builddata["fonts"][$o] = $identifier;
$n["subtype"] = "Type1";
$n["basefont"] = $identifier;
break;
default:
if ($this->objects[$identifier]["type"] != "fontembed") {
$this->_push_error(6006, "Object must be of type 'fontembed'");
return false;
} else {
// Not ready yet
$this->_push_error(6007, "Feature not implemented yet");
return false;
}
}
$this->objects[$o] = $n;
$this->_debug_var(10, 'Added font', $n);
return $o;
}
function generate($clevel = 9)
{
$this->_debug(1, 'Starting ->generate()');
// Validate the compression level
if (!$clevel) {
$this->builddata["compress"] = false;
} else {
if ($clevel < 10) {
$this->builddata["compress"] = $clevel;
} else {
$this->builddata["compress"] = 9;
}
}
/* Preprocess objects to see if they can
* be combined into a single stream
* We scan through each page, and create
* a multistream object out of all viable
* child objects
*/
$temparray = $this->objects;
foreach ($this->objects as $oid => $def) {
if ( $def["type"] == "page" ) {
unset($temp);
$temp['data'] = "";
reset($temparray);
while ( list ($liboid, $obj) = each($temparray) ) {
if (isset($obj["parent"]) && $obj["parent"] == $oid) {
$this->_debug(10, "Adding # $liboid to mstream");
switch ($obj["type"]) {
case "texts" :
$temp["data"] .= $this->_make_text($liboid);
$this->objects[$liboid]["type"] = "null";
$this->objects[$liboid]["parent"] = -1;
break;
case "rectangle" :
$temp["data"] .= $this->_make_rect($liboid);
$this->objects[$liboid]["type"] = "null";
$this->objects[$liboid]["parent"] = -1;
break;
case "iplace" :
$temp["data"] .= $this->_place_raw_image($liboid);
$this->objects[$liboid]["type"] = "null";
$this->objects[$liboid]["parent"] = -1;
break;
case "line" :
$temp["data"] .= $this->_make_line($liboid);
$this->objects[$liboid]["type"] = "null";
$this->objects[$liboid]["parent"] = -1;
break;
case "circle" :
$temp["data"] .= $this->_make_circle($liboid);
$this->objects[$liboid]["type"] = "null";
$this->objects[$liboid]["parent"] = -1;
break;
}
}
}
if (strlen($temp["data"]) > 0) {
// this line takes the next available oid
$o = $this->_addnewoid();
$this->_debug(10, "Saving mstream to # $o");
$temp["type"] = "mstream";
$temp["parent"] = $oid;
$this->objects[$o] = $temp;
}
}
}
unset($temparray);
// Generate a list of PDF object IDs to
// use and map them to phppdflib IDs
foreach ( $this->objects as $oid => $properties ) {
if ( $this->_becomes_object( $properties["type"] ) ) {
$o = $this->_addtoxreftable(0,0);
$this->libtopdf[$oid] = $o;
$this->pdftolib[$o] = $oid;
}
}
/* First characters represent the version
* of the PDF spec to conform to.
* The PDF spec recommends that the next
* four bytes be a comment containing four
* non-ASCII characters, to convince
* (for example) ftp programs that this is
* a binary file
*/
$os = "%PDF-1.3%\xe2\xe3\xcf\xd3\x0a";
// Create the Document Catalog
$carray["Type"] = "/Catalog";
$carray["Pages"] = "2 0 R";
$temp = $this->_makedictionary($carray);
$temp = "1 0 obj" . $temp . "endobj\x0a";
$this->xreftable[1]["offset"] = strlen($os);
$os .= $temp;
// Create the root page node
unset($carray);
$kids = $this->_order_pages(2);
$this->xreftable[2]["offset"] = strlen($os);
$os .= "2 0 " . $this->_makepagenode($kids, "" ) . "\x0a";
/* Create a resource dictionary for the entire
* PDF file. This may not be the most efficient
* way to store it, but it makes the code simple.
* At some point, we should analyze performance
* and see if it's worth splitting the resource
* dictionary up
*/
unset($temp);
unset($carray);
if (isset($this->builddata["fonts"]) && count($this->builddata["fonts"]) > 0) {
foreach ($this->builddata["fonts"] as $id => $base) {
$ta["F$id"] = $this->libtopdf[$id] . " 0 R";
}
$temp["Font"] = $this->_makedictionary($ta);
}
reset($this->objects);
while (list($id, $obj) = each($this->objects)) {
if ($obj["type"] == "image") {
$xol["Img$id"] = $this->libtopdf[$id] . " 0 R";
}
}
if ( isset($xol) && count($xol) > 0 ) {
$temp["XObject"] = $this->_makedictionary($xol);
}
$this->xreftable[3]["offset"] = strlen($os);
$os .= "3 0 obj";
if (isset($temp)) {
$os .= $this->_makedictionary($temp);
} else {
$os .= '<<>>';
}
$os .= " endobj\x0a";
// Go through and add the rest of the objects
foreach ( $this->pdftolib as $pdfoid => $liboid ) {
if ($pdfoid < 4) {
continue;
}
// Set the location of the start
$this->xreftable[$pdfoid]["offset"] = strlen($os);
switch ( $this->objects[$liboid]["type"] ) {
case "page":
$kids = $this->_get_kids($pdfoid);
$os .= $pdfoid . " 0 ";
$os .= $this->_makepage($this->objects[$liboid]["parent"],
$kids, $liboid);
break;
case "rectangle":
$os .= $pdfoid . " 0 obj";
$os .= $this->_streamify($this->_make_rect($liboid));
$os .= " endobj";
break;
case "line":
$os .= $pdfoid . " 0 obj";
$os .= $this->_streamify($this->_make_line($liboid));
$os .= " endobj";
break;
case "circle":
$os .= $pdfoid . " 0 obj";
$os .= $this->_streamify($this->_make_circle($liboid));
$os .= " endobj";
break;
case "texts":
$os .= $pdfoid . " 0 obj";
$temp = $this->_make_text($liboid);
$os .= $this->_streamify($temp) . " endobj";
break;
case "mstream":
$os .= $pdfoid . " 0 obj" .
$this->_streamify(trim($this->objects[$liboid]["data"])) .
" endobj";
break;
case "image":
$os .= $pdfoid . " 0 obj";
$os .= $this->_make_raw_image($liboid);
$os .= " endobj";
break;
case "iplace":
$os .= $pdfoid . " 0 obj";
$os .= $this->_streamify($this->_place_raw_image($liboid));
$os .= " endobj";
break;
case "font" :
$os .= $pdfoid . " 0 obj";
unset ( $temp );
$temp["Type"] = "/Font";
$temp["Subtype"] = "/" . $this->objects[$liboid]["subtype"];
$temp["BaseFont"] = "/" . $this->objects[$liboid]["basefont"];
$temp["Encoding"] = "/WinAnsiEncoding";
$temp["Name"] = "/F$liboid";
$os .= $this->_makedictionary($temp);
$os .= " endobj";
break;
}
$os .= "\x0a";
}
// Create an Info entry
$info = $this->_addtoxreftable(0,0);
$this->xreftable[$info]["offset"] = strlen($os);
unset($temp);
$temp["Producer"] =
$this->_stringify("phppdflib http://www.potentialtech.com/ppl.php");
$os .= $info . " 0 obj" . $this->_makedictionary($temp) . " endobj\x0a";
// Create the xref table
$this->builddata["startxref"] = strlen($os);
$os .= "xref\x0a0 " . (string)($this->nextobj + 1) . "\x0a";
for ( $i = 0; $i <= $this->nextobj; $i ++ ) {
$os .= sprintf("%010u %05u %s \x0a", $this->xreftable[$i]["offset"],
$this->xreftable[$i]["gennum"],
$this->xreftable[$i]["free"]);
}
// Create document trailer
$os .= "trailer\x0a";
unset($temp);
$temp["Size"] = $this->nextobj + 1;
$temp["Root"] = "1 0 R";
$temp["Info"] = $info . " 0 R";
$os .= $this->_makedictionary($temp);
$os .= "\x0astartxref\x0a";
$os .= $this->builddata["startxref"] . "\x0a";
// Required end of file marker
$os .= "%%EOF\x0a";
return $os;
}
function png_embed($data)
{
// Sanity, make sure this is a png
if (substr($data, 0, 8) != "\x89PNG\x0d\x0a\x1a\x0a") {
$this->_push_error(6011, 'brand not valid');
return false;
}
$data = substr($data, 12);
if (substr($data, 0, 4) != 'IHDR') {
$this->_push_error(6011, 'IHDR chunk missing');
return false;
}
$data = substr($data, 4);
$width = $this->_int_val(substr($data, 0, 4));
$height = $this->_int_val(substr($data, 4, 4));
$data = substr($data, 8);
$bpc = ord(substr($data, 0, 1));
$ct = ord(substr($data, 1, 1));
if ($bpc > 8) {
$this->_push_error(6014, '16 bit PNG unsupported');
return false;
}
switch ($ct) {
case 0 : $cspace = '/DeviceGray'; break;
case 2 : $cspace = '/DeviceRGB'; break;
case 3 : $cspace = '/Indexed'; break;
default:
$this->_push_error(6015, 'PNG with alpha not supported');
return false;
}
if (ord(substr($data, 2, 1)) != 0) {
$this->_push_error(6016, 'Unknown compression type');
return false;
}
if (ord(substr($data, 3, 1)) != 0) {
$this->_push_error(6017, 'Unknown PNG filter method');
return false;
}
if (ord(substr($data, 4, 1)) != 0) {
$this->_push_error(6018, 'PNG interlacing not supported');
return false;
}
$params['Predictor'] = '15';
$params['Colors'] = $ct == 2 ? 3 : 1;
$params['BitsPerComponent'] = $bpc;
$params['Columns'] = $width;
$additional['DecodeParms'] = $this->_makedictionary($params);
$data = substr($data, 9);
$pal = '';
$trns = '';
$rawdata = '';
do {
$n = $this->_int_val(substr($data, 0, 4));
$type = substr($data, 4, 4);
$data = substr($data, 8);
switch ($type) {
case 'PLTE' :
$pal = substr($data, 0, $n);
$data = substr($data, $n + 4);
break;
case 'tRNS' :
$t = substr($data, 0, $n);
if ($ct == 0)
$trns = array(ord(substr($t, 1, 1)));
elseif ($ct == 2)
$trns = array(ord(substr($t, 1, 1)),
ord(substr($t, 3, 1)),
ord(substr($t, 5, 1)));
else {
$pos = strpos($t, chr(0));
if (is_int($pos))
$trns = array($pos);
}
$data = substr($data, $n + 4);
break;
case 'IDAT' :
$rawdata .= substr($data, 0, $n);
$data = substr($data, $n + 4);
break;
case 'IEND' :
break 2;
default :
$data = substr($data, $n + 4);
}
} while ($n);
if ($cspace == '/Indexed') {
$this->_push_error(6011, 'Indexed without palette');
return false;
}
return $this->image_raw_embed($rawdata,
$cspace,
$bpc,
$height,
$width,
'/FlateDecode',
$additional);
}
function jfif_embed($data)
{
/* Sanity check: Check magic numbers to see if
* this is really a JFIF stream
*/
if ( substr($data, 0, 4) != "\xff\xd8\xff\xe0" ||
substr($data, 6, 4) != "JFIF" ) {
// This is not in JFIF format
$this->_push_std_error(6008);
return false;
}
/* Now we'll scan through all the markers in the
* JFIF and extract whatever data we need from them
* We're not being terribly anal about validating
* the structure of the JFIF, so a corrupt stream
* could have very unpredictable results
*/
// Default values
$pos = 0;
$size = strlen($data);
while ( $pos < $size ) {
$marker = substr($data, $pos + 1, 1);
// Just skip these markers
if ($marker == "\xd8" || $marker == "\xd9" || $marker == "\x01") {
$pos += 2;
continue;
}
if ($marker == "\xff") {
$pos ++;
continue;
}
switch ($marker) {
// Start of frame
// Baseline
case "\xc0":
// Extended sequential
case "\xc1":
// Differential sequential
case "\xc5":
// Progressive
case "\xc2":
// differential progressive
case "\xc6":
// Lossless
case "\xc3":
// differential lossless
case "\xc7":
// Arithmetic encoded
case "\xc9":
case "\xca":
case "\xcb":
case "\xcd":
case "\xce":
case "\xcf":
$precision = $this->_int_val(substr($data, $pos + 4, 1));
$height = $this->_int_val(substr($data, $pos + 5, 2));
$width = $this->_int_val(substr($data, $pos + 7, 2));
$numcomp = $this->_int_val(substr($data, $pos + 9, 1));
if ( $numcomp != 3 && $numcomp != 1 ) {
// Abort if we aren't encoded as B&W or YCbCr
$this->_push_std_error(6008);
return false;
}
$pos += 2 + $this->_int_val(substr($data, $pos + 2, 2));
break 2;
}
/* All marker identifications continue the
* loop, thus if we got here, we need to skip
* this marker as we don't understand it.
*/
$pos += 2 + $this->_int_val(substr($data, $pos + 2, 2));
}
$cspace = $numcomp == 1 ? "/DeviceGray" : "/DeviceRGB";
return $this->image_raw_embed($data,
$cspace,
$precision,
$height,
$width,
"/DCTDecode");
}
function image_raw_embed($data,
$cspace,
$bpc,
$height,
$width,
$filter = "",
$addtl = array())
{
$o = $this->_addnewoid();
$t['additional'] = $addtl;
$t['data'] = $data;
$t['colorspace'] = $cspace;
$t['bpc'] = $bpc;
$t['type'] = "image";
$t['height'] = $height;
$t['width'] = $width;
$t['filter'] = $filter;
$this->objects[$o] = $t;
$t['data'] = '[Data stripped]';
$this->_debug_var(10, 'Embedded image', $t);
return $o;
}
function get_image_size($id)
{
if ($this->objects[$id]['type'] != 'image') {
$this->_push_std_error(6009);
return false;
}
$r['width'] = $this->objects[$id]['width'];
$r['height'] = $this->objects[$id]['height'];
return $r;
}
function image_place($oid, $bottom, $left, $parent, $param = array())
{
if ($this->objects[$oid]["type"] != "image") {
$this->_push_std_error(6009);
return false;
}
if ($this->objects[$parent]["type"] != "page") {
$this->_push_std_error(6001);
return false;
}
$o = $this->_addnewoid();
$param = $this->_resolve_param($param, false);
$t["type"] = "iplace";
$this->_adjust_margin($left, $bottom, $parent);
$t["bottom"] = $bottom;
$t["left"] = $left;
$t["parent"] = $parent;
// find out what the image size should be
$width = $this->objects[$oid]["width"];
$height = $this->objects[$oid]["height"];
$scale = $param['scale'];
if (is_array($scale)) {
$t["xscale"] = $scale["x"] * $width;
$t["yscale"] = $scale["y"] * $height;
} else {
$t["xscale"] = $scale * $width;
$t["yscale"] = $scale * $height;
}
$t["rotation"] = $param['rotation'];
$t["image"] = $oid;
$this->objects[$o] = $t;
$this->_debug_var(10, 'Placed image', $t);
return $o;
}
function strlen($string , $params = false, $tabwidth = 4)
{
if ($this->needsset) {
require_once(dirname(__FILE__) . '/strlen.inc.php');
}
if (empty($params["font"])) {
$font = $this->default['font'];
} else {
$font = $params["font"];
switch ($font) {
case "Times-Roman" :
$font = "Times";
break;
case "Helvetica-Oblique" :
$font = "Helvetica";
break;
case "Helvetica-BoldOblique" :
$font = "Helvetica-Bold";
break;
case "ZapfDingbats" :
$font = "Dingbats";
break;
}
}
if ($params["height"] == 0) {
$size = $this->default['height'];
} else {
$size = $params["height"];
}
$tab = '';
for ($i = 0; $i < $tabwidth; $i++) {
$tab .= ' ';
}
$string = str_replace(chr(9), $tab, $string);
if (substr($font, 0, 7) == "Courier") {
// Courier is a fixed-width font
$width = strlen($string) * 600;
} else {
$width = 0;
$len = strlen($string);
for ($i = 0; $i < $len; $i++) {
$width += $this->widths[$font][ord($string{$i})];
}
}
// We now have the string width in font units
return $width * $size / 1000;
}
function wrap_line(&$text, $width, $param = array())
{
$maxchars = (int)(1.1 * $width / $this->strlen("i", $param));
$words = explode(" ", substr($text, 0, $maxchars));
if ($this->strlen($words[0]) >= $width) {
$this->_push_error(3001, "Single token too long for allowed space");
$final = $words[0];
} else {
$space = $this->strlen(" ", $param);
$len = 0;
$word = 0;
$final = "";
while ($len < $width) {
if ($word >= count($words)) {
break;
}
$temp = $this->strlen($words[$word], $param);
if ( ($len + $temp) <= $width) {
$final .= $words[$word] . " ";
$word ++;
}
$len += $space + $temp;
}
}
$text = substr($text, strlen($final));
return $final;
}
function word_wrap($words, $width, $param = array())
{
// break $words into an array separated by manual paragraph break character
$paragraph = explode("\n", $words);
// find the width of 1 space in this font
$swidth = $this->strlen( " " , $param );
// uses each element of $paragraph array and splits it at spaces
for ($lc = 0; $lc < count($paragraph); $lc++){
while (strlen($paragraph[$lc]) > 0) {
$returnarray[] = $this->wrap_line($paragraph[$lc], $width, $param);
}
}
return $returnarray;
}
function draw_one_paragraph($top, $left, $bottom, $right, $text, $page, $param = array())
{
$param = $this->_resolve_param($param);
$height = 1.1 * $param['height'];
$width = $right - $left;
while ($top > $bottom) {
if (strlen($text) < 1) {
break;
}
$top -= $height;
if ($top >= $bottom) {
$line = $this->wrap_line($text, $width, $param);
switch ($param['align']) {
case 'right' :
$line = trim($line);
$l = $right - $this->strlen($line, $param);
break;
case 'center' :
$line = trim($line);
$l = $left + (($width - $this->strlen($line, $param)) / 2);
break;
default :
$l = $left;
}
$this->draw_text($l, $top, $line, $page, $param);
} else {
$top += $height;
break;
}
}
if (strlen($text) > 0) {
return $text;
} else {
return $top;
}
}
function draw_paragraph($top, $left, $bottom, $right, $text, $page, $param = array())
{
$paras = split("\n", $text);
for ($i = 0; $i < count($paras); $i++) {
$over = $this->draw_one_paragraph($top,
$left,
$bottom,
$right,
$paras[$i],
$page,
$param);
if (is_string($over)) {
break;
}
$top = $over;
}
$rv = $over;
if ($i < count($paras)) {
for ($x = $i + 1; $x < count($paras); $x++) {
$rv .= "\n" . $paras[$x];
}
}
$this->_debug(10, "Drawing paragraph\n$text\nReturning\n$rv");
return $rv;
}
function error_array()
{
$rv = array();
while (count($this->ermsg) > 0) {
$this->pop_error($num, $msg);
$rv[] = "Error $num: $msg";
}
return $rv;
}
function pop_error(&$num, &$msg)
{
$num = array_pop($this->erno);
$msg = array_pop($this->ermsg);
if (is_null($num)) {
return false;
} else {
return $num;
}
}
function enable($name)
{
$name = strtolower($name);
include_once(dirname(__FILE__) . "/${name}.class.php");
$this->x[$name] = new $name;
$this->x[$name]->pdf = &$this;
switch ($name) {
case 'chart' :
case 'template' :
case 'packer' :
case 'import' :
$this->$name = &$this->x[$name];
break;
}
$this->_debug(10, "enabled extension: $name");
}
function get_color($desc)
{
$r = array();
switch (strtolower($desc)) {
case 'black' :
$r['red'] = $r['blue'] = $r['green'] = 0;
break;
case 'white' :
$r['red'] = $r['blue'] = $r['green'] = 1;
break;
case 'red' :
$r['red'] = 1;
$r['blue'] = $r['green'] = 0;
break;
case 'blue' :
$r['blue'] = 1;
$r['red'] = $r['green'] = 0;
break;
case 'green' :
$r['green'] = 1;
$r['blue'] = $r['red'] = 0;
break;
default :
if (substr($desc, 0, 1) == '#') {
// Parse out a hex triplet
$v = substr($desc, 1, 2);
$r['red'] = eval("return ord(\"\\x$v\");") / 255;
$v = substr($desc, 3, 2);
$r['green'] = eval("return ord(\"\\x$v\");") / 255;
$v = substr($desc, 5, 2);
$r['blue'] = eval("return ord(\"\\x$v\");") / 255;
} else {
// Error condition?
$this->_push_error(6012, "Unparsable color identifier: $desc");
$r = false;
}
}
$this->_debug_var(10, "get_color: $desc ", $r);
return $r;
}
/******************************************************
* These functions are internally used by the library *
* and shouldn't really be called by a user of *
* phppdflib *
******************************************************/
function _debug_var($level, $name, $value)
{
if ($level <= $this->debug) {
$this->dbs .= $name . ' -> ' . $this->_print_r($value) . "\n";
}
}
function _print_r($v)
{
if (version_compare('4.3.0', phpversion()) <= 0) {
return print_r($v, true);
} else {
if (is_array($v)) {
$r = "array {\n";
foreach ($v as $key => $value) {
$r .= "[$key] = " . $this->print_r($value) . "\n";
}
$r .= "}\n";
return $r;
} else {
return $v;
}
}
}
function _debug($level, $string)
{
if ($level <= $this->debug) {
$this->dbs .= $string . "\n";
}
}
function _resolve_mode($attrib, $mode)
{
$rmode = $attrib[$mode];
if ($rmode != 0) {
$r = $rmode;
} else {
switch ($rmode) {
case "fill":
$r = 0;
break;
case "stroke":
$r = 1;
break;
case "fill+stroke":
$r = 2;
break;
}
}
return $r;
}
function _adjust_margin(&$x, &$y, $page)
{
$x += $this->objects[$page]['margin-left'];
$y += $this->objects[$page]['margin-bottom'];
}
function _resolve_param($param, $text = true)
{
$rv = $this->default;
if (is_array($param)) {
if (isset($param['mode'])) {
$param['tmode'] = $param['smode'] = $param['mode'];
}
foreach ($param as $key => $value) {
$rv[$key] = $value;
}
}
return $rv;
}
function _push_error($num, $msg)
{
array_push($this->erno, $num);
array_push($this->ermsg, $msg);
$this->_debug(1, "ERROR: $num: $msg");
}
function _push_std_error($num)
{
switch ($num) {
case 6001 : $m = "Object must be of type 'page'"; break;
case 6008 : $m = "Data stream not recognized as JFIF"; break;
case 6009 : $m = "Object must be of type 'image'"; break;
case 6011 : $m = "Data stream not recognized as PNG"; break;
default : $m = "_push_std_error() called with invalid error number: $num"; break;
}
$this->_push_error($num, $m);
}
function _resolve_colors(&$n, $attrib)
{
$temp = array('red','green','blue');
foreach ($temp as $colcomp) {
if (isset($attrib['fillcolor'][$colcomp])) {
$n['fillcolor'][$colcomp] = $attrib['fillcolor'][$colcomp];
}
if (isset($attrib['strokecolor'][$colcomp])) {
$n['strokecolor'][$colcomp] = $attrib['strokecolor'][$colcomp];
}
}
}
/* Check to see if a requested font is already in the
* list, if not add it. Either way, return the libid
* of the font
*/
function _use_font($id)
{
if (!isset($id['font'])) {
$id['font'] = $this->default['font'];
}
if ( isset($this->builddata["fonts"]) && count($this->builddata["fonts"]) > 0 ) {
foreach ($this->builddata["fonts"] as $libid => $name) {
if ($name == $id['font']) {
return $libid;
}
}
}
/* The font isn't in the table, so we add it
* and return it's ID
*/
return $this->new_font($id['font']);
}
/* Convert a big-endian byte stream into an integer */
function _int_val($string)
{
$r = 0;
for ($i = 0; $i < strlen($string); $i ++ ) {
$r += ord($string{$i}) * pow(256, strlen($string) - $i -1);
}
return $r;
}
function _make_raw_image($liboid)
{
if (is_array($this->objects[$liboid]['additional']))
$s = $this->objects[$liboid]['additional'];
$s["Type"] = "/XObject";
$s["Subtype"] = "/Image";
$s["Width"] = $this->objects[$liboid]["width"];
$s["Height"] = $this->objects[$liboid]["height"];
$s["ColorSpace"] = $this->objects[$liboid]["colorspace"];
$s["BitsPerComponent"] = $this->objects[$liboid]["bpc"];
if (strlen($this->objects[$liboid]["filter"]) > 0) {
$s["Filter"] = $this->objects[$liboid]["filter"];
}
return $this->_streamify($this->objects[$liboid]["data"], $s);
}
function _place_raw_image($liboid)
{
$xscale = $this->objects[$liboid]["xscale"];
$yscale = $this->objects[$liboid]["yscale"];
$angle = $this->objects[$liboid]["rotation"];
$temp = "q 1 0 0 1 " .
$this->objects[$liboid]["left"] . " " .
$this->objects[$liboid]["bottom"] . " cm ";
if ($angle != 0) {
$temp .= $this->_rotate($angle) . " cm ";
}
if ($xscale != 1 || $yscale != 1) {
$temp .= "$xscale 0 0 $yscale 0 0 cm ";
}
$temp .= "/Img" . $this->objects[$liboid]["image"] .
" Do Q\x0a";
$this->_debug(20, "Made image: $temp");
return $temp;
}
function _rotate($angle)
{
$a = deg2rad($angle);
$cos = cos($a);
$sin = sin($a);
$r = sprintf("%1\$1.6f %2\$1.6f %3\$1.6f %1\$1.6f 0 0", $cos, $sin, -$sin);
return $r;
}
function _get_operator($liboid)
{
switch ($this->objects[$liboid]['mode']) {
case 0 : return "f"; break;
case 1 : return "S"; break;
case 2 : return "b"; break;
}
}
function _make_line($liboid)
{
$gstate = "";
if ( $colortest = $this->_colorset($liboid) ) {
$gstate .= $colortest . " ";
}
if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
$gstate .= $this->objects[$liboid]["width"] . " w ";
}
$firstpoint = true;
$temp = "";
foreach ($this->objects[$liboid]["x"] as $pointid => $x) {
$y = $this->objects[$liboid]["y"][$pointid];
$temp .= $x . " " . $y . " ";
if ($firstpoint) {
$temp .= "m ";
$firstpoint = false;
} else {
$temp .= "l ";
}
}
$temp .= $this->_get_operator($liboid);
if ( strlen($gstate) > 0 ) {
$temp = "q " . $gstate . $temp . " Q";
}
$this->_debug(20, "Made line: $temp");
return $temp . "\x0a";
}
function _make_rect($liboid)
{
$gstate = "";
if ( $colortest = $this->_colorset($liboid) ) {
$gstate .= $colortest . " ";
}
if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
$gstate .= $this->objects[$liboid]["width"] . " w ";
}
$temp = $this->objects[$liboid]["left"] . " ";
$temp .= $this->objects[$liboid]["bottom"];
$temp .= " " . ( $this->objects[$liboid]["right"]
- $this->objects[$liboid]["left"] );
$temp .= " " . ( $this->objects[$liboid]["top"]
- $this->objects[$liboid]["bottom"] );
$temp .= ' re ';
$temp .= $this->_get_operator($liboid);
if ( strlen($gstate) > 0 ) {
$temp = "q " . $gstate . $temp . " Q";
}
$this->_debug(20, "Made rectangle: $temp");
return $temp . "\x0a";
}
function _make_circle($liboid)
{
$gstate = "";
if ( $colortest = $this->_colorset($liboid) ) {
$gstate .= $colortest . " ";
}
if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
$gstate .= $this->objects[$liboid]["width"] . " w ";
}
$r = $this->objects[$liboid]['radius'];
$x = $this->objects[$liboid]['x'];
$y = $this->objects[$liboid]['y'];
$ql = $x - $r;
$pt = $y + $r * 1.33333;
$qr = $x + $r;
$pb = $y - $r * 1.33333;
$temp = "$ql $y m ";
$temp .= "$ql $pt $qr $pt $qr $y c ";
$temp .= "$qr $pb $ql $pb $ql $y c ";
$temp .= $this->_get_operator($liboid);
if ( strlen($gstate) > 0 ) {
$temp = "q " . $gstate . $temp . " Q";
}
$this->_debug(20, "Made circle: $temp");
return $temp . "\x0a";
}
function _make_text($liboid)
{
$statechange = ""; $locateinbt = true;
$statechange = $this->_colorset($liboid);
if (isset($this->objects[$liboid]["rotation"]) && $this->objects[$liboid]["rotation"] != 0) {
$statechange .= "1 0 0 1 " .
$this->objects[$liboid]["left"] . " " .
$this->objects[$liboid]["bottom"] . " cm " .
$this->_rotate($this->objects[$liboid]["rotation"]) .
" cm ";
$locateinbt = false;
}
$temp = "BT ";
if ($this->objects[$liboid]["mode"] != 0) {
$temp .= $this->objects[$liboid]["mode"] .
" Tr ";
// Adjust stroke width
$statechange .= $this->objects[$liboid]["height"] / 35 . " w ";
}
$temp .= "/F" . $this->objects[$liboid]["font"] . " ";
$temp .= $this->objects[$liboid]["height"];
$temp .= " Tf ";
if ($locateinbt) {
$temp .= $this->objects[$liboid]["left"] . " " .
$this->objects[$liboid]["bottom"];
} else {
$temp .= "0 0";
}
$temp .= " Td ";
$temp .= $this->_stringify($this->objects[$liboid]["text"]);
$temp .= " Tj ";
$temp .= "ET";
if (strlen($statechange) > 0) {
$temp = "q " . $statechange . $temp . " Q";
}
$this->_debug(20, "Made text: $temp");
return $temp . "\x0a";
}
function _colorset($libid)
{
$red = isset($this->objects[$libid]['fillcolor']["red"]) ? (float)$this->objects[$libid]['fillcolor']["red"] : 0;
$green = isset($this->objects[$libid]['fillcolor']["green"]) ? (float)$this->objects[$libid]['fillcolor']["green"] : 0;
$blue = isset($this->objects[$libid]['fillcolor']["blue"]) ? (float)$this->objects[$libid]['fillcolor']["blue"] : 0;
if (($red > 0) || ($green > 0) || ($blue > 0)) {
$r = $red . " " . $green . " " . $blue;
$r .= " rg ";
} else {
$r = "";
}
$red = isset($this->objects[$libid]['strokecolor']["red"]) ? (float)$this->objects[$libid]['strokecolor']["red"] : 0;
$green = isset($this->objects[$libid]['strokecolor']["green"]) ? (float)$this->objects[$libid]['strokecolor']["green"] : 0;
$blue = isset($this->objects[$libid]['strokecolor']["blue"]) ? (float)$this->objects[$libid]['strokecolor']["blue"] : 0;
if (($red > 0) || ($green > 0) || ($blue > 0) ) {
$r .= $red . " " . $green . " " . $blue;
$r .= " RG ";
}
$this->_debug(20, "Resolved color for $libid: $r");
return $r;
}
/* Used to determine what pdflib objects need converted
* to actual PDF objects.
*/
function _becomes_object($object)
{
if ($object == "null") {
return false;
}
return true;
}
/* builds an array of child objects */
function _get_kids($pdfid)
{
$libid = $this->pdftolib[$pdfid];
foreach( $this->objects as $obid => $object ) {
if (isset($object["parent"]) && $object["parent"] == $libid) {
$kids[] = $this->libtopdf[$obid] . " 0 R";
}
}
return $kids;
}
/* builds an array of pages, in order */
function _order_pages($pdfid)
{
$libid = $this->pdftolib[$pdfid];
foreach( $this->objects as $obid => $object ) {
if (isset($object["parent"]) && $object["parent"] == $libid) {
$kids[$object["number"]] = $this->libtopdf[$obid] . " 0 R";
}
}
ksort($kids);
return $kids;
}
/* simple helper function to return the current oid
* and increment it by one
*/
function _addnewoid()
{
$o = $this->nextoid;
$this->nextoid++;
return $o;
}
/* The xreftable will contain a list of all the
* objects in the pdf file and the number of bytes
* from the beginning of the file that the object
* occurs. Each time we add an object, we call this
* to record it's location, then call ->_genxreftable()
* to generate the table from array
*/
function _addtoxreftable($offset, $gennum)
{
$this->nextobj ++;
$this->xreftable[$this->nextobj]["offset"] = $offset;
$this->xreftable[$this->nextobj]["gennum"] = $gennum;
$this->xreftable[$this->nextobj]["free"] = "n";
return $this->nextobj;
}
/* Returns a properly formatted pdf dictionary
* containing entries specified by
* the array $entries
*/
function _makedictionary($entries)
{
$rs = "<<\x0a";
if (isset($entries) && count($entries) > 0) {
foreach ($entries as $key => $value) {
$rs .= "/" . $key . " " . $value . "\x0a";
}
}
$rs .= ">>";
return $rs;
}
/* returns a properly formatted pdf array */
function _makearray($entries)
{
$rs = "[";
if ( is_array($entries) ) {
foreach ($entries as $entry) {
$rs .= $entry . " ";
}
} else {
$rs .= $entries;
}
$rs = rtrim($rs) . "]";
return $rs;
}
/* Returns a properly formatted string, with any
* special characters escaped
*/
function _stringify($string)
{
// Escape potentially problematic characters
$string = preg_replace("-\\\\-","\\\\\\\\",$string);
$bad = array ("-\(-", "-\)-" );
$good = array ("\\(", "\\)" );
$string = preg_replace($bad,$good,$string);
return "(" . rtrim($string) . ")";
}
function _streamify($data, $sarray = array())
{
/* zlib compression is a compile time option
* for php, thus we need to make sure it's
* available before using it.
*/
$go = true;
if (function_exists('gzcompress') && $this->builddata['compress']) {
if ( !isset($sarray['Filter']) || strlen($sarray['Filter']) == 0 ) {
$sarray['Filter'] = '/FlateDecode';
} else {
if ($sarray['Filter'] != '/FlateDecode')
$sarray['Filter'] = '[/FlateDecode ' . $sarray['Filter'] . ']';
else
$go = false;
}
if ($go) $data = gzcompress($data, $this->builddata['compress']);
}
$sarray['Length'] = strlen($data);
$os = $this->_makedictionary($sarray);
$os .= "stream\x0a" . $data . "\x0aendstream";
return $os;
}
/* Returns a properly formatted page node
* page nodes with 0 kids are not created
*/
function _makepagenode($kids, $addtlopts = false)
{
$parray["Type"] = "/Pages";
if ( isset($kids) AND count($kids) > 0 ) {
// Array of child objects
$parray["Kids"] = $this->_makearray($kids);
// Number of pages
$parray["Count"] = count($kids);
} else {
// No kids is an error condition
$this->_push_error(600, "Pagenode has no children");
return false;
}
if ( is_array($addtlopts) ) {
foreach ( $addtlopts as $key => $value ) {
$parray[$key] = $value;
}
}
/* The resource dictionary is always object 3
*/
$parray["Resources"] = "3 0 R";
$os = $this->_makedictionary($parray);
$os = "obj" . $os . "endobj";
$this->_debug(20, "Pagenode: $os");
return $os;
}
function _makepage($parent, $contents, $liboid)
{
$parray["Type"] = "/Page";
$parray["Parent"] = $this->libtopdf[$parent] . " 0 R";
$parray["Contents"] = $this->_makearray($contents);
$parray["MediaBox"] = "[0 0 "
. $this->objects[$liboid]["width"] . " "
. $this->objects[$liboid]["height"] . "]";
$os = $this->_makedictionary($parray);
$os = "obj" . $os . "endobj";
$this->_debug(20, "Page from $liboid: $os");
return $os;
}
}
?>