]> git.llucax.com Git - mecon/meconlib.git/blob - lib/MECON/PDF/external/phppdflib.class.php
Se cambian los die(expr) por trigger_error(expr, E_USER_ERROR) para poder interceptar...
[mecon/meconlib.git] / lib / MECON / PDF / external / phppdflib.class.php
1 <?php
2 /*
3    php pdf generation library
4    Copyright (C) Potential Technologies 2002 - 2003
5    http://www.potentialtech.com
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21    $Id: phppdflib.class.php,v 2.6 2003/08/29 21:15:21 wmoran Exp $
22 */
23
24 class pdffile
25 {
26     /* $objects is an array that stores the objects
27      * that will become the pdf when ->generate()
28      * is called.
29      * The layout of ->objects does not directly
30      * mimic the pdf format, although it is similar.
31      * nextoid always holds the next available oid ( oid is short for object id )
32      */
33     var $objects, $nextoid;
34
35     /* xreftable is an array containing data to
36      * create the xref section (PDF calls it a referance table)
37      */
38     var $xreftable, $nextobj;
39
40     /* These arrays allow quick translation between
41      * pdflib OIDs and the final PDF OIDs (OID stands for object ids)
42      */
43     var $libtopdf, $pdftolib;
44
45     // Errors
46     var $ermsg = array(), $erno = array();
47
48     var $builddata;         // Various data required during the pdf build
49     var $nextpage;          // Tracks the next page number
50     var $widths, $needsset; // Store the font width arrays here
51     var $default;           // Default values for objects
52     var $x, $chart, $template;  // extension class is instantiated here if requested
53     /* Constructor function: is automatically called when the
54      * object is created.  Used to set up the environment
55      */
56     function pdffile()
57     {
58         /* Per spec, obj 0 should always have a generation
59          * number of 65535 and is always free
60          */
61         $this->xreftable[0]["gennum"] = 65535;
62         $this->xreftable[0]["offset"] = 0;
63         $this->xreftable[0]["free"] = "f";
64
65         // Object #1 will always be the Document Catalog
66         $this->xreftable[1]["gennum"] = 0;
67         $this->xreftable[1]["free"] = "n";
68
69         // Object #2 will always be the root pagenode
70         $this->xreftable[2]["gennum"] = 0;
71         $this->xreftable[2]["free"] = "n";
72         $this->pdftolib[2] = 1;
73         $this->libtopdf[1] = 2;
74
75         // Object #3 is always the resource library
76         $this->xreftable[3]["gennum"] = 0;
77         $this->xreftable[3]["free"] = "n";
78
79         /* nextoid starts at 2 because all
80          * drawing functions return either the
81          * object ID or FALSE on error, so we can't
82          * return an OID of 0, because it equates
83          * to false and error checking would think
84          * the procedure failed
85          */
86         $this->nextoid = 2;
87         $this->nextobj = 3;
88
89         // Pages start at 0
90         $this->nextpage = 0;
91
92         // Font width tables are not set unless they are needed
93         $this->needsset = true;
94
95         // Set all the default values
96         $t['pagesize'] = 'letter';
97         $t['font'] = 'Helvetica';
98         $t['height'] = 12;
99         $t['align'] = 'left';
100         $t['width'] = 1;
101         $t['rotation'] = 0;
102         $t['scale'] = 1;
103         $t['strokecolor'] = $this->get_color('black');
104         $t['fillcolor'] = $this->get_color('black');
105         $t['margin-left'] = $t['margin-right'] = $t['margin-top'] = $t['margin-bottom'] =72;
106         $t['tmode'] = 0; // Text: fill
107         $t['smode'] = 1; // Shapes: stroke
108         $this->default = $t;
109     }
110
111 /******************************************************
112  * These functions are the public ones, they are the  *
113  * way that the user will actually enter the data     *
114  * that will become the pdf                           *
115  ******************************************************/
116
117     function set_default($setting, $value)
118     {
119         switch ($setting) {
120         case 'margin' :
121             $this->default['margin-left'] = $value;
122             $this->default['margin-right'] = $value;
123             $this->default['margin-top'] = $value;
124             $this->default['margin-bottom'] = $value;
125             break;
126
127         case 'mode' :
128             $this->default['tmode'] = $this->default['smode'] = $value;
129             break;
130
131         default :
132             $this->default[$setting] = $value;
133         }
134         return true;
135     }
136     
137     function draw_rectangle($top, $left, $bottom, $right, $parent, $attrib = array())
138     {
139         if ($this->objects[$parent]["type"] != "page") {
140             $this->_push_std_error(6001);
141             return false;
142         }
143         $o = $this->_addnewoid();
144         $attrib = $this->_resolve_param($attrib, false);
145         $this->_resolve_colors($n, $attrib);
146         $this->objects[$o] = $n;
147         $this->objects[$o]["width"] = $attrib["width"];
148         $this->objects[$o]["type"] = "rectangle";
149         $this->_adjust_margin($left, $top, $parent);
150         $this->_adjust_margin($right, $bottom, $parent);
151         $this->objects[$o]["top"] = $top;
152         $this->objects[$o]["left"] = $left;
153         $this->objects[$o]["bottom"] = $bottom;
154         $this->objects[$o]["right"] = $right;
155         $this->objects[$o]["parent"] = $parent;
156         $this->objects[$o]["mode"] = $this->_resolve_mode($attrib, 'smode');
157         return $o;
158     }
159
160     function draw_circle($x, $y, $r, $parent, $attrib = array())
161     {
162         if ($this->objects[$parent]["type"] != "page") {
163             $this->_push_std_error(6001);
164             return false;
165         }
166         $o = $this->_addnewoid();
167         $attrib = $this->_resolve_param($attrib, false);
168         $this->_resolve_colors($n, $attrib);
169         $n['width'] = $attrib['width'];
170         $this->_adjust_margin($x, $y, $parent);
171         $n['x'] = $x;
172         $n['y'] = $y;
173         $n['radius'] = $r;
174         $n['type'] = 'circle';
175         $n['parent'] = $parent;
176         $n['mode'] = $this->_resolve_mode($attrib, 'smode');
177         $this->objects[$o] = $n;
178         return $o;
179     }
180
181     function draw_line($x, $y, $parent, $attrib = array())
182     {
183         if ($this->objects[$parent]["type"] != "page") {
184             $this->_push_std_error(6001);
185             return false;
186         }
187         if (count($x) != count($y)) {
188             $this->_push_error(6002, "X & Y variables must have equal number of elements");
189             return false;
190         }
191         $o = $this->_addnewoid();
192         $attrib = $this->_resolve_param($attrib, false);
193         $this->_resolve_colors($n, $attrib);
194         $this->objects[$o] = $n;
195         @$this->objects[$o]["width"] = $attrib["width"];
196         $this->objects[$o]['mode'] = $this->_resolve_mode($attrib, 'smode');
197         $this->objects[$o]["type"] = "line";
198         foreach ($x as $key => $value) {
199             if (isset($x[$key]) && isset($y[$key])) {
200                 $this->_adjust_margin($x[$key], $y[$key], $parent);
201             }
202         }
203         $this->objects[$o]["x"] = $x;
204         $this->objects[$o]["y"] = $y;
205         $this->objects[$o]["parent"] = $parent;
206         return $o;
207     }
208
209     // draw text
210     function draw_text($left, $bottom, $text, $parent, $attrib = array())
211     {
212         if ($this->objects[$parent]["type"] != "page") {
213             $this->_push_std_error(6001);
214             return false;
215         }
216         $attrib = $this->_resolve_param($attrib);
217         // Validate the font
218         if (!($n["font"] = $this->_use_font($attrib))) {
219             // Couldn't find/add the font
220             $this->_push_error(6003, "Font was not found");
221             return false;
222         }
223         if (isset($attrib["rotation"])) {
224             $n["rotation"] = $attrib["rotation"];
225         }
226         $n['mode'] = $this->_resolve_mode($attrib, 'tmode');
227         if (isset($attrib["height"]) && $attrib["height"] > 0) {
228            $n["height"] = $attrib["height"];
229         }
230         $this->_resolve_colors($n, $attrib);
231         $n["type"] = "texts";
232         $this->_adjust_margin($left, $bottom, $parent);
233         $n["left"] = $left;
234         $n["bottom"] = $bottom;
235         $n["text"] = $text;
236         $n["parent"] = $parent;
237
238         $o = $this->_addnewoid();
239         $this->objects[$o] = $n;
240         return $o;
241     }
242
243     function new_page($size = null)
244     {
245         if (is_null($size)) {
246             $size = $this->default['pagesize'];
247         }
248         switch ($size) {
249         case "letter" :
250             $o = $this->_addnewoid();
251             $this->objects[$o]["height"] = 792;
252             $this->objects[$o]["width"] = 612;
253             break;
254
255         case "legal" :
256             $o = $this->_addnewoid();
257             $this->objects[$o]["height"] = 1008;
258             $this->objects[$o]["width"] = 612;
259             break;
260
261         case "executive" :
262             $o = $this->_addnewoid();
263             $this->objects[$o]["height"] = 720;
264             $this->objects[$o]["width"] = 540;
265             break;
266
267         case "tabloid" :
268             $o = $this->_addnewoid();
269             $this->objects[$o]["height"] = 1224;
270             $this->objects[$o]["width"] = 792;
271             break;
272
273         case "a3" :
274             $o = $this->_addnewoid();
275             $this->objects[$o]["height"] = 1188;
276             $this->objects[$o]["width"] = 842;
277             break;
278
279         case "a4" :
280             $o = $this->_addnewoid();
281             $this->objects[$o]["height"] = 842;
282             $this->objects[$o]["width"] = 595;
283             break;
284
285         case "a5" :
286             $o = $this->_addnewoid();
287             $this->objects[$o]["height"] = 598;
288             $this->objects[$o]["width"] = 418;
289             break;
290
291         default :
292             if (preg_match("/in/",$size)) {
293                 $o = $this->_addnewoid();
294                 $size = substr($size, 0, strlen($size) - 2);
295                 $dims = split("x",$size);
296                 $this->objects[$o]["height"] = ($dims[1] * 72);
297                 $this->objects[$o]["width"] = ($dims[0] * 72);
298             } else {
299                 if (preg_match("/cm/",$size)) {
300                     $o = $this->_addnewoid();
301                     $size = substr($size, 0, strlen($size) - 2);
302                     $dims = split("x",$size);
303                     $this->objects[$o]["height"] = ($dims[1] * 28.346);
304                     $this->objects[$o]["width"] = ($dims[0] * 28.346);
305                 } else {
306                     $this->_push_error(6004, "Could not deciper page size description: $size");
307                     return false;
308                 }
309
310             }
311         }
312         $this->objects[$o]['type'] = 'page';
313         $this->objects[$o]['parent'] = 1;
314         $this->objects[$o]['number'] = $this->nextpage;
315         $this->nextpage ++;
316         foreach (array('margin-left', 'margin-right', 'margin-top', 'margin-bottom') as $margin) {
317                 $this->objects[$o][$margin] = $this->default[$margin];
318         }
319         return $o;
320     }
321
322     function swap_pages($p1, $p2)
323     {
324         if ($this->objects[$p1]["type"] != "page" ||
325             $this->objects[$p2]["type"] != "page") {
326                 $this->_push_std_error(6001);
327                 return false;
328         }
329         $temp = $this->objects[$p1]["number"];
330         $this->objects[$p1]["number"] = $this->objects[$p2]["number"];
331         $this->objects[$p2]["number"] = $temp;
332         return true;
333     }
334
335     function move_page_before($page, $infrontof)
336     {
337         if ($this->objects[$page]["type"] != "page" ||
338             $this->objects[$infrontof]["type"] != "page") {
339                 $this->_push_std_error(6001);
340                 return false;
341         }
342         if ($page == $infrontof) {
343             $this->_push_error(6005, "You're trying to swap a page with itself");
344             return false;
345         }
346         $target = $this->objects[$infrontof]["number"];
347         $leaving = $this->objects[$page]["number"];
348         foreach ($this->objects as $id => $o) {
349             if ($o["type"] == "page") {
350                 if ($target < $leaving) {
351                     if ($o["number"] >= $target && $o["number"] < $leaving) {
352                         $this->objects[$id]["number"]++;
353                     }
354                 } else {
355                     if ($o["number"] < $target && $o["number"] > $leaving) {
356                         $this->objects[$id]["number"]--;
357                     }
358                 }
359             }
360         }
361         if ($target < $leaving) {
362             $this->objects[$page]["number"] = $target;
363         } else {
364             $this->objects[$page]["number"] = $target - 1;
365         }
366         return true;
367     }
368
369     function new_font($identifier)
370     {
371         $n["type"] = "font";
372
373         switch ($identifier) {
374         /* The "standard" Type 1 fonts
375          * These are "guaranteed" to be available
376          * to the viewer application and don't
377          * need embedded
378          */
379         case "Courier":
380         case "Courier-Bold":
381         case "Courier-Oblique":
382         case "Courier-BoldOblique":
383         case "Helvetica":
384         case "Helvetica-Bold":
385         case "Helvetica-Oblique":
386         case "Helvetica-BoldOblique":
387         case "Times-Roman":
388         case "Times-Bold":
389         case "Times-Italic":
390         case "Times-BoldItalic":
391         case "Symbol":
392         case "ZapfDingbats":
393             $o = $this->_addnewoid();
394             $this->builddata["fonts"][$o] = $identifier;
395             $n["subtype"] = "Type1";
396             $n["basefont"] = $identifier;
397             break;
398
399         default:
400             if ($this->objects[$identifier]["type"] != "fontembed") {
401                 $this->_push_error(6006, "Object must be of type 'fontembed'");
402                 return false;
403             } else {
404                 // Not ready yet
405                 $this->_push_error(6007, "Feature not implemented yet");
406                 return false;
407             }
408         }
409         $this->objects[$o] = $n;
410         return $o;
411     }
412
413     function generate($clevel = 9)
414     {
415         // Validate the compression level
416         if (!$clevel) {
417             $this->builddata["compress"] = false;
418         } else {
419             if ($clevel < 10) {
420                 $this->builddata["compress"] = $clevel;
421             } else {
422                 $this->builddata["compress"] = 9;
423             }
424         }
425         /* Preprocess objects to see if they can
426          * be combined into a single stream
427          * We scan through each page, and create
428          * a multistream object out of all viable
429          * child objects
430          */
431         $temparray = $this->objects;
432         foreach ($this->objects as $oid => $def) {
433             if ( $def["type"] == "page" ) {
434                 unset($temp);
435                 $temp['data'] = "";
436                 reset($temparray);
437                 while ( list ($liboid, $obj) = each($temparray) ) {
438                     if (isset($obj["parent"]) && $obj["parent"] == $oid) {
439                         switch ($obj["type"]) {
440                         case "texts" :
441                             $temp["data"] .= $this->_make_text($liboid);
442                             $this->objects[$liboid]["type"] = "null";
443                             $this->objects[$liboid]["parent"] = -1;
444                             break;
445
446                         case "rectangle" :
447                             $temp["data"] .= $this->_make_rect($liboid);
448                             $this->objects[$liboid]["type"] = "null";
449                             $this->objects[$liboid]["parent"] = -1;
450                             break;
451
452                         case "iplace" :
453                             $temp["data"] .= $this->_place_raw_image($liboid);
454                             $this->objects[$liboid]["type"] = "null";
455                             $this->objects[$liboid]["parent"] = -1;
456                             break;
457
458                         case "line" :
459                             $temp["data"] .= $this->_make_line($liboid);
460                             $this->objects[$liboid]["type"] = "null";
461                             $this->objects[$liboid]["parent"] = -1;
462                             break;
463
464                         case "circle" :
465                             $temp["data"] .= $this->_make_circle($liboid);
466                             $this->objects[$liboid]["type"] = "null";
467                             $this->objects[$liboid]["parent"] = -1;
468                             break;
469                         }
470                     }
471                 }
472                 if (strlen($temp["data"]) > 0) {
473                     // this line takes the next available oid
474                     $o = $this->_addnewoid();
475                     $temp["type"] = "mstream";
476                     $temp["parent"] = $oid;
477                     $this->objects[$o] = $temp;
478                 }
479             }
480         }
481         unset($temparray);
482
483         // Generate a list of PDF object IDs to
484         // use and map them to phppdflib IDs
485         foreach ( $this->objects as $oid => $properties ) {
486             if ( $this->_becomes_object( $properties["type"] ) ) {
487                 $o = $this->_addtoxreftable(0,0);
488                 $this->libtopdf[$oid] = $o;
489                 $this->pdftolib[$o] = $oid;
490             }
491         }
492
493         /* First characters represent the version
494          * of the PDF spec to conform to.
495          * The PDF spec recommends that the next
496          * four bytes be a comment containing four
497          * non-ASCII characters, to convince
498          * (for example) ftp programs that this is
499          * a binary file
500          */
501         $os = "%PDF-1.3%\xe2\xe3\xcf\xd3\x0a";
502
503         // Create the Document Catalog
504         $carray["Type"] = "/Catalog";
505         $carray["Pages"] = "2 0 R";
506         $temp = $this->_makedictionary($carray);
507         $temp = "1 0 obj" . $temp . "endobj\x0a";
508         $this->xreftable[1]["offset"] = strlen($os);
509         $os .= $temp;
510
511         // Create the root page node
512         unset($carray);
513         $kids = $this->_order_pages(2);
514         $this->xreftable[2]["offset"] = strlen($os);
515         $os .= "2 0 " . $this->_makepagenode($kids, "" ) . "\x0a";
516
517         /* Create a resource dictionary for the entire
518          * PDF file.  This may not be the most efficient
519          * way to store it, but it makes the code simple.
520          * At some point, we should analyze performance
521          * and see if it's worth splitting the resource
522          * dictionary up
523          */
524         unset($temp);
525         unset($carray);
526         if (isset($this->builddata["fonts"]) && count($this->builddata["fonts"]) > 0) {
527             foreach ($this->builddata["fonts"] as $id => $base) {
528                 $ta["F$id"] = $this->libtopdf[$id] . " 0 R";
529             }
530             $temp["Font"] = $this->_makedictionary($ta);
531         }
532         reset($this->objects);
533         while (list($id, $obj) = each($this->objects)) {
534             if ($obj["type"] == "image") {
535                 $xol["Img$id"] = $this->libtopdf[$id] . " 0 R";
536             }
537         }
538         if ( isset($xol) && count($xol) > 0 ) {
539             $temp["XObject"] = $this->_makedictionary($xol);
540         }
541         $this->xreftable[3]["offset"] = strlen($os);
542         $os .= "3 0 obj";
543         if (isset($temp)) {
544             $os .= $this->_makedictionary($temp);
545         } else {
546             $os .= '<<>>';
547         }
548         $os .= " endobj\x0a";
549
550         // Go through and add the rest of the objects
551         foreach ( $this->pdftolib as $pdfoid => $liboid ) {
552             if ($pdfoid < 4) {
553                 continue;
554             }
555             // Set the location of the start
556             $this->xreftable[$pdfoid]["offset"] = strlen($os);
557             switch ( $this->objects[$liboid]["type"] ) {
558             case "page":
559                 $kids = $this->_get_kids($pdfoid);
560                 $os .= $pdfoid . " 0 ";
561                 $os .= $this->_makepage($this->objects[$liboid]["parent"],
562                                         $kids, $liboid);
563                 break;
564
565             case "rectangle":
566                 $os .= $pdfoid . " 0 obj";
567                 $os .= $this->_streamify($this->_make_rect($liboid));
568                 $os .= " endobj";
569                 break;
570
571             case "line":
572                 $os .= $pdfoid . " 0 obj";
573                 $os .= $this->_streamify($this->_make_line($liboid));
574                 $os .= " endobj";
575                 break;
576
577             case "circle":
578                 $os .= $pdfoid . " 0 obj";
579                 $os .= $this->_streamify($this->_make_circle($liboid));
580                 $os .= " endobj";
581                 break;
582
583             case "texts":
584                 $os .= $pdfoid . " 0 obj";
585                 $temp = $this->_make_text($liboid);
586                 $os .= $this->_streamify($temp) . " endobj";
587                 break;
588
589             case "mstream":
590                 $os .= $pdfoid . " 0 obj" .
591                        $this->_streamify(trim($this->objects[$liboid]["data"])) .
592                        " endobj";
593                 break;
594
595             case "image":
596                 $os .= $pdfoid . " 0 obj";
597                 $os .= $this->_make_raw_image($liboid);
598                 $os .= " endobj";
599                 break;
600
601             case "iplace":
602                 $os .= $pdfoid . " 0 obj";
603                 $os .= $this->_streamify($this->_place_raw_image($liboid));
604                 $os .= " endobj";
605                 break;
606
607             case "font" :
608                 $os .= $pdfoid . " 0 obj";
609                 unset ( $temp );
610                 $temp["Type"] = "/Font";
611                 $temp["Subtype"] = "/" . $this->objects[$liboid]["subtype"];
612                 $temp["BaseFont"] = "/" . $this->objects[$liboid]["basefont"];
613                 $temp["Encoding"] = "/WinAnsiEncoding";
614                 $temp["Name"] = "/F$liboid";
615                 $os .= $this->_makedictionary($temp);
616                 $os .= " endobj";
617                 break;
618             }
619             $os .= "\x0a";
620         }
621
622         // Create an Info entry
623         $info = $this->_addtoxreftable(0,0);
624         $this->xreftable[$info]["offset"] = strlen($os);
625         unset($temp);
626         $temp["Producer"] =
627             $this->_stringify("phppdflib http://www.potentialtech.com/ppl.php");
628         $os .= $info . " 0 obj" . $this->_makedictionary($temp) . " endobj\x0a";
629
630         // Create the xref table
631         $this->builddata["startxref"] = strlen($os);
632         $os .= "xref\x0a0 " . (string)($this->nextobj + 1) . "\x0a";
633         for ( $i = 0; $i <= $this->nextobj; $i ++ ) {
634             $os .= sprintf("%010u %05u %s \x0a", $this->xreftable[$i]["offset"],
635                            $this->xreftable[$i]["gennum"],
636                            $this->xreftable[$i]["free"]);
637         }
638
639         // Create document trailer
640         $os .= "trailer\x0a";
641         unset($temp);
642         $temp["Size"] = $this->nextobj + 1;
643         $temp["Root"] = "1 0 R";
644         $temp["Info"] = $info . " 0 R";
645         $os .= $this->_makedictionary($temp);
646         $os .= "\x0astartxref\x0a";
647         $os .= $this->builddata["startxref"] . "\x0a";
648
649         // Required end of file marker
650         $os .= "%%EOF\x0a";
651
652         return $os;
653     }
654
655     function png_embed($data)
656     {
657         // Sanity, make sure this is a png
658         if (substr($data, 0, 8) != "\x89PNG\x0d\x0a\x1a\x0a") {
659             $this->_push_error(6011, 'brand not valid');
660             return false;
661         }
662         $data = substr($data, 12);
663         if (substr($data, 0, 4) != 'IHDR') {
664                 $this->_push_error(6011, 'IHDR chunk missing');
665             return false;
666         }
667         $data = substr($data, 4);
668         $width = $this->_int_val(substr($data, 0, 4));
669         $height = $this->_int_val(substr($data, 4, 4));
670         $data = substr($data, 8);
671         $bpc = ord(substr($data, 0, 1));
672         $ct  = ord(substr($data, 1, 1));
673         if ($bpc > 8) {
674             $this->_push_error(6014, '16 bit PNG unsupported');
675             return false;
676         }
677         switch ($ct) {
678         case 0 : $cspace = '/DeviceGray'; break;
679         case 2 : $cspace = '/DeviceRGB'; break;
680         case 3 : $cspace = '/Indexed'; break;
681         default:
682             $this->_push_error(6015, 'PNG with alpha not supported');
683             return false;
684         }
685         if (ord(substr($data, 2, 1)) != 0) {
686             $this->_push_error(6016, 'Unknown compression type');
687             return false;
688         }
689         if (ord(substr($data, 3, 1)) != 0) {
690             $this->_push_error(6017, 'Unknown PNG filter method');
691             return false;
692         }
693         if (ord(substr($data, 4, 1)) != 0) {
694             $this->_push_error(6018, 'PNG interlacing not supported');
695             return false;
696         }
697         $params['Predictor'] = '15';
698         $params['Colors'] = $ct == 2 ? 3 : 1;
699         $params['BitsPerComponent'] = $bpc;
700         $params['Columns'] = $width;
701         $additional['DecodeParms'] = $this->_makedictionary($params);
702         $data = substr($data, 9);
703         $pal = '';
704         $trns = '';
705         $rawdata = '';
706         do {
707             $n = $this->_int_val(substr($data, 0, 4));
708             $type = substr($data, 4, 4);
709             $data = substr($data, 8);
710             switch ($type) {
711             case 'PLTE' :
712                 $pal = substr($data, 0, $n);
713                 $data = substr($data, $n + 4);
714                 break;
715                 
716             case 'tRNS' :
717                 $t = substr($data, 0, $n);
718                 if ($ct == 0)
719                     $trns = array(ord(substr($t, 1, 1)));
720                 elseif ($ct == 2)
721                     $trns = array(ord(substr($t, 1, 1)),
722                                   ord(substr($t, 3, 1)),
723                                   ord(substr($t, 5, 1)));
724                 else {
725                     $pos = strpos($t, chr(0));
726                     if (is_int($pos))
727                         $trns = array($pos);
728                 }
729                 $data = substr($data, $n + 4);
730                 break;
731                 
732             case 'IDAT' :
733                 $rawdata .= substr($data, 0, $n);
734                 $data = substr($data, $n + 4);
735                 break;
736                 
737             case 'IEND' :
738                 break 2;
739             
740             default :
741                 $data = substr($data, $n + 4);
742             }
743         } while ($n);
744         if ($cspace == '/Indexed') {
745             $this->_push_error(6011, 'Indexed without palette');
746             return false;
747         }
748         return $this->image_raw_embed($rawdata,
749                                       $cspace,
750                                       $bpc,
751                                       $height,
752                                       $width,
753                                       '/FlateDecode',
754                                       $additional);
755     }
756
757     function jfif_embed($data)
758     {
759         /* Sanity check: Check magic numbers to see if
760          * this is really a JFIF stream
761          */
762         if ( substr($data, 0, 4) != "\xff\xd8\xff\xe0" ||
763              substr($data, 6, 4) != "JFIF" ) {
764             // This is not in JFIF format
765             $this->_push_std_error(6008);
766             return false;
767         }
768
769         /* Now we'll scan through all the markers in the
770          * JFIF and extract whatever data we need from them
771          * We're not being terribly anal about validating
772          * the structure of the JFIF, so a corrupt stream
773          * could have very unpredictable results
774          */
775         // Default values
776         $pos = 0;
777         $size = strlen($data);
778
779         while ( $pos < $size ) {
780             $marker = substr($data, $pos + 1, 1);
781             // Just skip these markers
782             if ($marker == "\xd8" || $marker == "\xd9" || $marker == "\x01") {
783                 $pos += 2;
784                 continue;
785             }
786             if ($marker == "\xff") {
787                 $pos ++;
788                 continue;
789             }
790
791             switch ($marker) {
792             // Start of frame
793             // Baseline
794             case "\xc0":
795             // Extended sequential
796             case "\xc1":
797             // Differential sequential
798             case "\xc5":
799             // Progressive
800             case "\xc2":
801             // differential progressive
802             case "\xc6":
803             // Lossless
804             case "\xc3":
805             // differential lossless
806             case "\xc7":
807             // Arithmetic encoded
808             case "\xc9":
809             case "\xca":
810             case "\xcb":
811             case "\xcd":
812             case "\xce":
813             case "\xcf":
814                 $precision = $this->_int_val(substr($data, $pos + 4, 1));
815                 $height = $this->_int_val(substr($data, $pos + 5, 2));
816                 $width = $this->_int_val(substr($data, $pos + 7, 2));
817                 $numcomp = $this->_int_val(substr($data, $pos + 9, 1));
818                 if ( $numcomp != 3 && $numcomp != 1 ) {
819                     // Abort if we aren't encoded as B&W or YCbCr
820                     $this->_push_std_error(6008);
821                     return false;
822                 }
823                 $pos += 2 + $this->_int_val(substr($data, $pos + 2, 2));
824                 break 2;
825             }
826
827             /* All marker identifications continue the
828              * loop, thus if we got here, we need to skip
829              * this marker as we don't understand it.
830              */
831             $pos += 2 + $this->_int_val(substr($data, $pos + 2, 2));
832         }
833         $cspace = $numcomp == 1 ? "/DeviceGray" : "/DeviceRGB";
834         return $this->image_raw_embed($data,
835                                       $cspace,
836                                       $precision,
837                                       $height,
838                                       $width,
839                                       "/DCTDecode");
840     }
841
842     function image_raw_embed($data,
843                              $cspace,
844                              $bpc,
845                              $height,
846                              $width,
847                              $filter = "",
848                              $addtl = array())
849     {
850         $o = $this->_addnewoid();
851         $t['additional'] = $addtl;
852         $t['data'] = $data;
853         $t['colorspace'] = $cspace;
854         $t['bpc'] = $bpc;
855         $t['type'] = "image";
856         $t['height'] = $height;
857         $t['width'] = $width;
858         $t['filter'] = $filter;
859         $this->objects[$o] = $t;
860         return $o;
861     }
862
863     function get_image_size($id)
864     {
865         if ($this->objects[$id]['type'] != 'image') {
866             $this->_push_std_error(6009);
867             return false;
868         }
869         $r['width'] = $this->objects[$id]['width'];
870         $r['height'] = $this->objects[$id]['height'];
871         return $r;
872     }
873
874     function image_place($oid, $bottom, $left, $parent, $param = array())
875     {
876         if ($this->objects[$oid]["type"] != "image") {
877             $this->_push_std_error(6009);
878             return false;
879         }
880         if ($this->objects[$parent]["type"] != "page") {
881             $this->_push_std_error(6001);
882             return false;
883         }
884
885         $o = $this->_addnewoid();
886         $param = $this->_resolve_param($param, false);
887         $t["type"] = "iplace";
888         $this->_adjust_margin($left, $bottom, $parent);
889         $t["bottom"] = $bottom;
890         $t["left"] = $left;
891         $t["parent"] = $parent;
892         // find out what the image size should be
893         $width = $this->objects[$oid]["width"];
894         $height = $this->objects[$oid]["height"];
895         $scale = $param['scale'];
896         if (is_array($scale)) {
897             $t["xscale"] = $scale["x"] * $width;
898             $t["yscale"] = $scale["y"] * $height;
899         } else {
900             $t["xscale"] = $scale * $width;
901             $t["yscale"] = $scale * $height;
902         }
903         $t["rotation"] = $param['rotation'];
904         $t["image"] = $oid;
905         $this->objects[$o] = $t;
906         return $o;
907     }
908
909     function strlen($string , $params = false, $tabwidth = 4)
910     {
911         if ($this->needsset) {
912             require_once(dirname(__FILE__) . '/strlen.inc.php');
913         }
914         if (empty($params["font"])) {
915             $font = $this->default['font'];
916         } else {
917             $font = $params["font"];
918             switch ($font) {
919                 case "Times-Roman" :
920                     $font = "Times";
921                     break;
922                 case "Helvetica-Oblique" :
923                     $font = "Helvetica";
924                     break;
925                 case "Helvetica-BoldOblique" :
926                     $font = "Helvetica-Bold";
927                     break;
928                 case "ZapfDingbats" :
929                     $font = "Dingbats";
930                     break;
931             }
932         }
933         if ($params["height"] == 0) {
934             $size = $this->default['height'];
935         } else {
936             $size = $params["height"];
937         }
938         $tab = '';
939         for ($i = 0; $i < $tabwidth; $i++) {
940                 $tab .= ' ';
941         }
942         $string = str_replace(chr(9), $tab, $string);
943         if (substr($font, 0, 7) == "Courier") {
944             // Courier is a fixed-width font
945             $width = strlen($string) * 600;
946         } else {
947             $width = 0;
948             $len = strlen($string);
949             for ($i = 0; $i < $len; $i++) {
950                 $width += $this->widths[$font][ord($string{$i})];
951             }
952         }
953         // We now have the string width in font units
954         return $width * $size / 1000;
955     }
956
957     function wrap_line(&$text, $width, $param = array())
958     {
959         $maxchars = (int)(1.1 * $width / $this->strlen("i", $param));
960         $words = explode(" ", substr($text, 0, $maxchars));
961         if ($this->strlen($words[0]) >= $width) {
962             $this->_push_error(3001, "Single token too long for allowed space");
963             $final = $words[0];
964         } else {
965             $space = $this->strlen(" ", $param);
966             $len = 0;
967             $word = 0;
968             $final = "";
969             while ($len < $width) {
970                 if ($word >= count($words)) {
971                     break;
972                 }
973                 $temp = $this->strlen($words[$word], $param);
974                 if ( ($len + $temp) <= $width) {
975                     $final .= $words[$word] . " ";
976                     $word ++;
977                 }
978                 $len += $space + $temp;
979             }
980         }
981         $text = substr($text, strlen($final));
982         return $final;
983     }
984
985     function word_wrap($words, $width, $param = array())
986     {
987         // break $words into an array separated by manual paragraph break character
988         $paragraph = explode("\n", $words);
989         // find the width of 1 space in this font
990         $swidth = $this->strlen( " " , $param );
991         // uses each element of $paragraph array and splits it at spaces
992         for ($lc = 0; $lc < count($paragraph); $lc++){
993             while (strlen($paragraph[$lc]) > 0) {
994                 $returnarray[] = $this->wrap_line($paragraph[$lc], $width, $param);
995             }
996         }
997         return $returnarray;
998     }
999
1000     function draw_one_paragraph($top, $left, $bottom, $right, $text, $page, $param = array())
1001     {
1002         $param = $this->_resolve_param($param);
1003         $height = 1.1 * $param['height'];
1004         $width = $right - $left;
1005         while ($top > $bottom) {
1006             if (strlen($text) < 1) {
1007                 break;
1008             }
1009             $top -= $height;
1010             if ($top >= $bottom) {
1011                 $line = $this->wrap_line($text, $width, $param);
1012                 switch ($param['align']) {
1013                 case 'right' :
1014                     $line = trim($line);
1015                     $l = $right - $this->strlen($line, $param);
1016                     break;
1017
1018                 case 'center' :
1019                     $line = trim($line);
1020                     $l = $left + (($width - $this->strlen($line, $param)) / 2);
1021                     break;
1022                 
1023                 default :
1024                     $l = $left;
1025                 }
1026                 $this->draw_text($l, $top, $line, $page, $param);
1027             } else {
1028                 $top += $height;
1029                 break;
1030             }
1031         }
1032         if (strlen($text) > 0) {
1033             return $text;
1034         } else {
1035             return $top;
1036         }
1037     }
1038
1039     function draw_paragraph($top, $left, $bottom, $right, $text, $page, $param = array())
1040     {
1041         $paras = split("\n", $text);
1042         for ($i = 0; $i < count($paras); $i++) {
1043             $over = $this->draw_one_paragraph($top,
1044                                               $left,
1045                                               $bottom,
1046                                               $right,
1047                                               $paras[$i],
1048                                               $page,
1049                                               $param);
1050             if (is_string($over)) {
1051                 break;
1052             }
1053             $top = $over;
1054         }
1055         $rv = $over;
1056         if ($i < count($paras)) {
1057             for ($x = $i + 1; $x < count($paras); $x++) {
1058                 $rv .= "\n" . $paras[$x];
1059             }
1060         }
1061         return $rv;
1062     }
1063
1064     function error_array()
1065     {
1066         $rv = array();
1067         while (count($this->ermsg) > 0) {
1068             $this->pop_error($num, $msg);
1069             $rv[] = "Error $num: $msg";
1070         }
1071         return $rv;
1072     }
1073
1074     function pop_error(&$num, &$msg)
1075     {
1076         $num = array_pop($this->erno);
1077         $msg = array_pop($this->ermsg);
1078         if (is_null($num)) {
1079                 return false;
1080         } else {
1081                 return $num;
1082         }
1083     }
1084
1085     function enable($name)
1086     {
1087         $name = strtolower($name);
1088         @include_once(dirname(__FILE__) . "/${name}.class.php");
1089         $this->x[$name] = new $name;
1090         $this->x[$name]->pdf = &$this;
1091         switch ($name) {
1092         case 'chart' :
1093         case 'template' :
1094                 $this->$name = &$this->x[$name];
1095             break;
1096         }
1097     }
1098
1099     function get_color($desc)
1100     {
1101
1102         $r = array();
1103         switch (strtolower($desc)) {
1104         case 'black' :
1105             $r['red'] = $r['blue'] = $r['green'] = 0;
1106             break;
1107
1108         case 'white' :
1109             $r['red'] = $r['blue'] = $r['green'] = 1;
1110             break;
1111             
1112         case 'red' :
1113             $r['red'] = 1;
1114             $r['blue'] = $r['green'] = 0;
1115             break;
1116             
1117         case 'blue' :
1118             $r['blue'] = 1;
1119             $r['red'] = $r['green'] = 0;
1120             break;
1121
1122         case 'green' :
1123             $r['green'] = 1;
1124             $r['blue'] = $r['red'] = 0;
1125             break;
1126
1127         default :
1128                 if (substr($desc, 0, 1) == '#') {
1129                 // Parse out a hex triplet
1130                 $v = substr($desc, 1, 2);
1131                 $r['red'] = eval("return ord(\"\\x$v\");") / 255;
1132                 $v = substr($desc, 3, 2);
1133                 $r['green'] = eval("return ord(\"\\x$v\");") / 255;
1134                 $v = substr($desc, 5, 2);
1135                 $r['blue'] = eval("return ord(\"\\x$v\");") / 255;
1136             } else {
1137                 // Error condition?
1138                 $this->_push_error(6012, "Unparsable color identifier: $desc");
1139                 $r = false;
1140             }
1141         }
1142         return $r;
1143     }
1144
1145 /******************************************************
1146  * These functions are internally used by the library *
1147  * and shouldn't really be called by a user of        *
1148  * phppdflib                                          *
1149  ******************************************************/
1150
1151     function _resolve_mode($attrib, $mode)
1152     {
1153         $rmode = $attrib[$mode];
1154         if ($rmode != 0) {
1155             $r = $rmode;
1156         } else {
1157             switch ($rmode) {
1158             case "fill":
1159                 $r = 0;
1160                 break;
1161
1162             case "stroke":
1163                 $r = 1;
1164                 break;
1165
1166             case "fill+stroke":
1167                 $r = 2;
1168                 break;
1169             }
1170         }
1171         return $r;
1172     }
1173
1174     function _adjust_margin(&$x, &$y, $page)
1175     {
1176         $x += $this->objects[$page]['margin-left'];
1177         $y += $this->objects[$page]['margin-bottom'];
1178     }
1179
1180     function _resolve_param($param, $text = true)
1181     {
1182         $rv = $this->default;
1183         if (is_array($param)) {
1184             if (isset($param['mode'])) {
1185                 $param['tmode'] = $param['smode'] = $param['mode'];
1186             }
1187             foreach ($param as $key => $value) {
1188                 $rv[$key] = $value;
1189             }
1190         }
1191         return $rv;
1192     }
1193     
1194     function _push_error($num, $msg)
1195     {
1196         array_push($this->erno, $num);
1197         array_push($this->ermsg, $msg);
1198     }
1199
1200     function _push_std_error($num)
1201     {
1202         switch ($num) {
1203             case 6001 : $m = "Object must be of type 'page'"; break;
1204             case 6008 : $m = "Data stream not recognized as JFIF"; break;
1205             case 6009 : $m = "Object must be of type 'image'"; break;
1206             case 6011 : $m = "Data stream not recognized as PNG"; break;
1207             default : $m = "_push_std_error() called with invalid error number: $num"; break;
1208         }
1209         $this->_push_error($num, $m);
1210     }
1211
1212     function _resolve_colors(&$n, $attrib)
1213     {
1214         $temp = array('red','green','blue');
1215         foreach ($temp as $colcomp) {
1216             if (isset($attrib['fillcolor'][$colcomp])) {
1217                 $n['fillcolor'][$colcomp] = $attrib['fillcolor'][$colcomp];
1218             }
1219             if (isset($attrib['strokecolor'][$colcomp])) {
1220                 $n['strokecolor'][$colcomp] = $attrib['strokecolor'][$colcomp];
1221             }
1222         }
1223     }
1224
1225     /* Check to see if a requested font is already in the
1226      * list, if not add it.  Either way, return the libid
1227      * of the font
1228      */
1229     function _use_font($id)
1230     {
1231         if (!isset($id['font'])) {
1232             $id['font'] = $this->default['font'];
1233         }
1234         if ( isset($this->builddata["fonts"]) && count($this->builddata["fonts"]) > 0 ) {
1235             foreach ($this->builddata["fonts"] as $libid => $name) {
1236                 if ($name == $id['font']) {
1237                     return $libid;
1238                 }
1239             }
1240         }
1241         /* The font isn't in the table, so we add it
1242          * and return it's ID
1243          */
1244         return $this->new_font($id['font']);
1245     }
1246
1247     /* Convert a big-endian byte stream into an integer */
1248     function _int_val($string)
1249     {
1250         $r = 0;
1251         for ($i = 0; $i < strlen($string); $i ++ ) {
1252             $r += ord($string{$i}) * pow(256, strlen($string) - $i -1);
1253         }
1254         return $r;
1255     }
1256
1257     function _make_raw_image($liboid)
1258     {
1259         if (is_array($this->objects[$liboid]['additional']))
1260             $s = $this->objects[$liboid]['additional'];
1261         $s["Type"] = "/XObject";
1262         $s["Subtype"] = "/Image";
1263         $s["Width"] = $this->objects[$liboid]["width"];
1264         $s["Height"] = $this->objects[$liboid]["height"];
1265         $s["ColorSpace"] = $this->objects[$liboid]["colorspace"];
1266         $s["BitsPerComponent"] = $this->objects[$liboid]["bpc"];
1267         if (strlen($this->objects[$liboid]["filter"]) > 0) {
1268             $s["Filter"] = $this->objects[$liboid]["filter"];
1269         }
1270         return $this->_streamify($this->objects[$liboid]["data"], $s);
1271     }
1272
1273     function _place_raw_image($liboid)
1274     {
1275         $xscale = $this->objects[$liboid]["xscale"];
1276         $yscale = $this->objects[$liboid]["yscale"];
1277         $angle = $this->objects[$liboid]["rotation"];
1278         $temp = "q 1 0 0 1 " .
1279                 $this->objects[$liboid]["left"] . " " .
1280                 $this->objects[$liboid]["bottom"] . " cm ";
1281         if ($angle != 0) {
1282             $temp .= $this->_rotate($angle) . " cm ";
1283         }
1284         if ($xscale != 1 || $yscale != 1) {
1285             $temp .= "$xscale 0 0 $yscale 0 0 cm ";
1286         }
1287         $temp .= "/Img" . $this->objects[$liboid]["image"] .
1288                  " Do Q\x0a";
1289         return $temp;
1290     }
1291
1292     function _rotate($angle)
1293     {
1294         $a = deg2rad($angle);
1295         $cos = cos($a);
1296         $sin = sin($a);
1297         $r = sprintf("%1\$1.6f %2\$1.6f %3\$1.6f %1\$1.6f 0 0", $cos, $sin, -$sin);
1298         return $r;
1299     }
1300
1301     function _get_operator($liboid)
1302     {
1303         switch ($this->objects[$liboid]['mode']) {
1304         case 0 : return "f"; break;
1305         case 1 : return "S"; break;
1306         case 2 : return "b"; break;
1307         }
1308     }
1309
1310     function _make_line($liboid)
1311     {
1312         $gstate = "";
1313         if ( $colortest = $this->_colorset($liboid) ) {
1314             $gstate .= $colortest . " ";
1315         }
1316         if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
1317             $gstate .= $this->objects[$liboid]["width"] . " w ";
1318         }
1319         $firstpoint = true;
1320         $temp = "";
1321         foreach ($this->objects[$liboid]["x"] as $pointid => $x) {
1322             $y = $this->objects[$liboid]["y"][$pointid];
1323             $temp .= $x . " " . $y . " ";
1324             if ($firstpoint) {
1325                 $temp .= "m ";
1326                 $firstpoint = false;
1327             } else {
1328                 $temp .= "l ";
1329             }
1330         }
1331         $temp .= $this->_get_operator($liboid);
1332         if ( strlen($gstate) > 0 ) {
1333             $temp = "q " . $gstate . $temp . " Q";
1334         }
1335         return $temp . "\x0a";
1336     }
1337
1338     function _make_rect($liboid)
1339     {
1340         $gstate = "";
1341         if ( $colortest = $this->_colorset($liboid) ) {
1342             $gstate .= $colortest . " ";
1343         }
1344         if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
1345             $gstate .= $this->objects[$liboid]["width"] . " w ";
1346         }
1347         $temp = $this->objects[$liboid]["left"] . " ";
1348         $temp .= $this->objects[$liboid]["bottom"];
1349         $temp .= " " . ( $this->objects[$liboid]["right"]
1350                  - $this->objects[$liboid]["left"] );
1351         $temp .= " " . ( $this->objects[$liboid]["top"]
1352                  - $this->objects[$liboid]["bottom"] );
1353         $temp .= ' re ';
1354         $temp .= $this->_get_operator($liboid);
1355         if ( strlen($gstate) > 0 ) {
1356             $temp = "q " . $gstate . $temp . " Q";
1357         }
1358         return $temp . "\x0a";
1359     }
1360
1361     function _make_circle($liboid)
1362     {
1363         $gstate = "";
1364         if ( $colortest = $this->_colorset($liboid) ) {
1365             $gstate .= $colortest . " ";
1366         }
1367         if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
1368             $gstate .= $this->objects[$liboid]["width"] . " w ";
1369         }
1370         $r = $this->objects[$liboid]['radius'];
1371         $x = $this->objects[$liboid]['x'];
1372         $y = $this->objects[$liboid]['y'];
1373         $ql = $x - $r;
1374         $pt = $y + $r * 1.33333;
1375         $qr = $x + $r;
1376         $pb = $y - $r * 1.33333;
1377         $temp = "$ql $y m ";
1378         $temp .= "$ql $pt $qr $pt $qr $y c ";
1379         $temp .= "$qr $pb $ql $pb $ql $y c ";
1380         $temp .= $this->_get_operator($liboid);
1381         if ( strlen($gstate) > 0 ) {
1382             $temp = "q " . $gstate . $temp . " Q";
1383         }
1384         return $temp . "\x0a";
1385     }
1386
1387     function _make_text($liboid)
1388     {
1389         $statechange = ""; $locateinbt = true;
1390         $statechange = $this->_colorset($liboid);
1391         if (isset($this->objects[$liboid]["rotation"]) && $this->objects[$liboid]["rotation"] != 0) {
1392             $statechange .= "1 0 0 1 " .
1393                             $this->objects[$liboid]["left"] . " " .
1394                             $this->objects[$liboid]["bottom"] . " cm " .
1395                             $this->_rotate($this->objects[$liboid]["rotation"]) .
1396                             " cm ";
1397             $locateinbt = false;
1398         }
1399         $temp = "BT ";
1400         if ($this->objects[$liboid]["mode"] != 0) {
1401             $temp .= $this->objects[$liboid]["mode"] .
1402                             " Tr ";
1403             // Adjust stroke width
1404             $statechange .= $this->objects[$liboid]["height"] / 35 . " w ";
1405         }
1406         $temp .= "/F" . $this->objects[$liboid]["font"] . " ";
1407         $temp .= $this->objects[$liboid]["height"];
1408         $temp .= " Tf ";
1409         if ($locateinbt) {
1410             $temp .= $this->objects[$liboid]["left"] . " " .
1411                      $this->objects[$liboid]["bottom"];
1412         } else {
1413             $temp .= "0 0";
1414         }
1415         $temp .= " Td ";
1416         $temp .= $this->_stringify($this->objects[$liboid]["text"]);
1417         $temp .= " Tj ";
1418         $temp .= "ET";
1419         if (strlen($statechange) > 0) {
1420             $temp = "q " . $statechange . $temp . " Q";
1421         }
1422         return $temp . "\x0a";
1423     }
1424
1425     function _colorset($libid)
1426     {
1427         $red = isset($this->objects[$libid]['fillcolor']["red"]) ? (float)$this->objects[$libid]['fillcolor']["red"] : 0;
1428         $green = isset($this->objects[$libid]['fillcolor']["green"]) ? (float)$this->objects[$libid]['fillcolor']["green"] : 0;
1429         $blue = isset($this->objects[$libid]['fillcolor']["blue"]) ? (float)$this->objects[$libid]['fillcolor']["blue"] : 0;
1430         if (($red > 0) || ($green > 0) || ($blue > 0)) {
1431             $r = $red . " " . $green . " " . $blue;
1432             $r .= " rg ";
1433         } else {
1434             $r = "";
1435         }
1436         $red = isset($this->objects[$libid]['strokecolor']["red"]) ? (float)$this->objects[$libid]['strokecolor']["red"] : 0;
1437         $green = isset($this->objects[$libid]['strokecolor']["green"]) ? (float)$this->objects[$libid]['strokecolor']["green"] : 0;
1438         $blue = isset($this->objects[$libid]['strokecolor']["blue"]) ? (float)$this->objects[$libid]['strokecolor']["blue"] : 0;
1439         if (($red > 0) || ($green > 0) || ($blue > 0) ) {
1440             $r .= $red . " " . $green . " " . $blue;
1441             $r .= " RG ";
1442         }
1443         return $r;
1444     }
1445
1446     /* Used to determine what pdflib objects need converted
1447      * to actual PDF objects.
1448      */
1449     function _becomes_object($object)
1450     {
1451         if ($object == "null") {
1452             return false;
1453         }
1454         return true;
1455     }
1456
1457     /* builds an array of child objects */
1458     function _get_kids($pdfid)
1459     {
1460         $libid = $this->pdftolib[$pdfid];
1461         foreach( $this->objects as $obid => $object ) {
1462             if (isset($object["parent"]) && $object["parent"] == $libid) {
1463                 $kids[] = $this->libtopdf[$obid] . " 0 R";
1464             }
1465         }
1466         return $kids;
1467     }
1468
1469     /* builds an array of pages, in order */
1470     function _order_pages($pdfid)
1471     {
1472         $libid = $this->pdftolib[$pdfid];
1473         foreach( $this->objects as $obid => $object ) {
1474             if (isset($object["parent"]) && $object["parent"] == $libid) {
1475                 $kids[$object["number"]] = $this->libtopdf[$obid] . " 0 R";
1476             }
1477         }
1478         ksort($kids);
1479         return $kids;
1480     }
1481
1482     /* simple helper function to return the current oid
1483      * and increment it by one
1484      */
1485     function _addnewoid()
1486     {
1487         $o = $this->nextoid;
1488         $this->nextoid++;
1489         return $o;
1490     }
1491
1492     /* The xreftable will contain a list of all the
1493      * objects in the pdf file and the number of bytes
1494      * from the beginning of the file that the object
1495      * occurs. Each time we add an object, we call this
1496      * to record it's location, then call ->_genxreftable()
1497      * to generate the table from array
1498      */
1499     function _addtoxreftable($offset, $gennum)
1500     {
1501         $this->nextobj ++;
1502         $this->xreftable[$this->nextobj]["offset"] = $offset;
1503         $this->xreftable[$this->nextobj]["gennum"] = $gennum;
1504         $this->xreftable[$this->nextobj]["free"] = "n";
1505         return $this->nextobj;
1506     }
1507
1508     /* Returns a properly formatted pdf dictionary
1509      * containing entries specified by
1510      * the array $entries
1511      */
1512     function _makedictionary($entries)
1513     {
1514         $rs = "<<\x0a";
1515         if (isset($entries) && count($entries) > 0) {
1516             foreach ($entries as $key => $value) {
1517                 $rs .= "/" . $key . " " . $value . "\x0a";
1518             }
1519         }
1520         $rs .= ">>";
1521         return $rs;
1522     }
1523
1524     /* returns a properly formatted pdf array */
1525     function _makearray($entries)
1526     {
1527         $rs = "[";
1528         if ( is_array($entries) ) {
1529             foreach ($entries as $entry) {
1530                 $rs .= $entry . " ";
1531             }
1532         } else {
1533             $rs .= $entries;
1534         }
1535         $rs = rtrim($rs) . "]";
1536         return $rs;
1537     }
1538
1539     /* Returns a properly formatted string, with any
1540      * special characters escaped
1541      */
1542     function _stringify($string)
1543     {
1544         // Escape potentially problematic characters
1545         $string = preg_replace("-\\\\-","\\\\\\\\",$string);
1546         $bad = array ("-\(-", "-\)-" );
1547         $good = array ("\\(", "\\)" );
1548         $string = preg_replace($bad,$good,$string);
1549         return "(" . rtrim($string) . ")";
1550     }
1551
1552     function _streamify($data, $sarray = array())
1553     {
1554         /* zlib compression is a compile time option
1555          * for php, thus we need to make sure it's
1556          * available before using it.
1557          */
1558         $go = true;
1559         if (function_exists('gzcompress') && $this->builddata['compress']) {
1560             if ( !isset($sarray['Filter']) || strlen($sarray['Filter']) == 0 ) {
1561                 $sarray['Filter'] = '/FlateDecode';
1562             } else {
1563                 if ($sarray['Filter'] != '/FlateDecode')
1564                     $sarray['Filter'] = '[/FlateDecode ' . $sarray['Filter'] . ']';
1565                 else
1566                     $go = false;
1567             }
1568             if ($go) $data = gzcompress($data, $this->builddata['compress']);
1569         }
1570         $sarray['Length'] = strlen($data);
1571         $os = $this->_makedictionary($sarray);
1572         $os .= "stream\x0a" . $data . "\x0aendstream";
1573         return $os;
1574     }
1575
1576     /* Returns a properly formatted page node
1577      * page nodes with 0 kids are not created
1578      */
1579     function _makepagenode($kids, $addtlopts = false)
1580     {
1581         $parray["Type"] = "/Pages";
1582         if ( isset($kids) AND count($kids) > 0 ) {
1583             // Array of child objects
1584             $parray["Kids"] = $this->_makearray($kids);
1585             // Number of pages
1586             $parray["Count"] = count($kids);
1587         } else {
1588             // No kids is an error condition
1589             $this->_push_error(600, "Pagenode has no children");
1590             return false;
1591         }
1592         if ( is_array($addtlopts) ) {
1593             foreach ( $addtlopts as $key => $value ) {
1594                 $parray[$key] = $value;
1595             }
1596         }
1597
1598         /* The resource dictionary is always object 3
1599          */
1600         $parray["Resources"] = "3 0 R";
1601
1602         $os = $this->_makedictionary($parray);
1603         $os = "obj" . $os . "endobj";
1604         return $os;
1605     }
1606
1607     function _makepage($parent, $contents, $liboid)
1608     {
1609         $parray["Type"] = "/Page";
1610         $parray["Parent"] = $this->libtopdf[$parent] . " 0 R";
1611         $parray["Contents"] = $this->_makearray($contents);
1612         $parray["MediaBox"] = "[0 0 "
1613                             . $this->objects[$liboid]["width"] . " "
1614                             . $this->objects[$liboid]["height"] . "]";
1615         $os = $this->_makedictionary($parray);
1616         $os = "obj" . $os . "endobj";
1617         return $os;
1618     }
1619
1620 }
1621 ?>