]> git.llucax.com Git - mecon/meconlib.git/blob - lib/MECON/PDF/external/phppdflib.class.php
Se ponen bien los ALT.
[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.5 2003/07/05 21:33:07 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) != "\137PNG\x0d\x0a\x1a\x0d") {
659             $this->_push_std_error(6011);
660             return false;
661         }
662         
663     }
664
665     function jfif_embed($data)
666     {
667         /* Sanity check: Check magic numbers to see if
668          * this is really a JFIF stream
669          */
670         if ( substr($data, 0, 4) != "\xff\xd8\xff\xe0" ||
671              substr($data, 6, 4) != "JFIF" ) {
672             // This is not in JFIF format
673             $this->_push_std_error(6008);
674             return false;
675         }
676
677         /* Now we'll scan through all the markers in the
678          * JFIF and extract whatever data we need from them
679          * We're not being terribly anal about validating
680          * the structure of the JFIF, so a corrupt stream
681          * could have very unpredictable results
682          */
683         // Default values
684         $pos = 0;
685         $size = strlen($data);
686
687         while ( $pos < $size ) {
688             $marker = substr($data, $pos + 1, 1);
689             // Just skip these markers
690             if ($marker == "\xd8" || $marker == "\xd9" || $marker == "\x01") {
691                 $pos += 2;
692                 continue;
693             }
694             if ($marker == "\xff") {
695                 $pos ++;
696                 continue;
697             }
698
699             switch ($marker) {
700             // Start of frame
701             // Baseline
702             case "\xc0":
703             // Extended sequential
704             case "\xc1":
705             // Differential sequential
706             case "\xc5":
707             // Progressive
708             case "\xc2":
709             // differential progressive
710             case "\xc6":
711             // Lossless
712             case "\xc3":
713             // differential lossless
714             case "\xc7":
715             // Arithmetic encoded
716             case "\xc9":
717             case "\xca":
718             case "\xcb":
719             case "\xcd":
720             case "\xce":
721             case "\xcf":
722                 $precision = $this->_int_val(substr($data, $pos + 4, 1));
723                 $height = $this->_int_val(substr($data, $pos + 5, 2));
724                 $width = $this->_int_val(substr($data, $pos + 7, 2));
725                 $numcomp = $this->_int_val(substr($data, $pos + 9, 1));
726                 if ( $numcomp != 3 && $numcomp != 1 ) {
727                     // Abort if we aren't encoded as B&W or YCbCr
728                     $this->_push_std_error(6008);
729                     return false;
730                 }
731                 $pos += 2 + $this->_int_val(substr($data, $pos + 2, 2));
732                 break 2;
733             }
734
735             /* All marker identifications continue the
736              * loop, thus if we got here, we need to skip
737              * this marker as we don't understand it.
738              */
739             $pos += 2 + $this->_int_val(substr($data, $pos + 2, 2));
740         }
741         $cspace = $numcomp == 1 ? "/DeviceGray" : "/DeviceRGB";
742         return $this->image_raw_embed($data,
743                                       $cspace,
744                                       $precision,
745                                       $height,
746                                       $width,
747                                       "/DCTDecode");
748     }
749
750     function image_raw_embed($data, $cspace, $bpc, $height, $width, $filter = "")
751     {
752         $o = $this->_addnewoid();
753         $t["data"] = $data;
754         $t["colorspace"] = $cspace;
755         $t["bpc"] = $bpc;
756         $t["type"] = "image";
757         $t["height"] = $height;
758         $t["width"] = $width;
759         $t["filter"] = $filter;
760         $this->objects[$o] = $t;
761         return $o;
762     }
763
764     function get_image_size($id)
765     {
766         if ($this->objects[$id]['type'] != 'image') {
767             $this->_push_std_error(6009);
768             return false;
769         }
770         $r['width'] = $this->objects[$id]['width'];
771         $r['height'] = $this->objects[$id]['height'];
772         return $r;
773     }
774
775     function image_place($oid, $bottom, $left, $parent, $param = array())
776     {
777         if ($this->objects[$oid]["type"] != "image") {
778             $this->_push_std_error(6009);
779             return false;
780         }
781         if ($this->objects[$parent]["type"] != "page") {
782             $this->_push_std_error(6001);
783             return false;
784         }
785
786         $o = $this->_addnewoid();
787         $param = $this->_resolve_param($param, false);
788         $t["type"] = "iplace";
789         $this->_adjust_margin($left, $bottom, $parent);
790         $t["bottom"] = $bottom;
791         $t["left"] = $left;
792         $t["parent"] = $parent;
793         // find out what the image size should be
794         $width = $this->objects[$oid]["width"];
795         $height = $this->objects[$oid]["height"];
796         $scale = $param['scale'];
797         if (is_array($scale)) {
798             $t["xscale"] = $scale["x"] * $width;
799             $t["yscale"] = $scale["y"] * $height;
800         } else {
801             $t["xscale"] = $scale * $width;
802             $t["yscale"] = $scale * $height;
803         }
804         $t["rotation"] = $param['rotation'];
805         $t["image"] = $oid;
806         $this->objects[$o] = $t;
807         return $o;
808     }
809
810     function strlen($string , $params = false, $tabwidth = 4)
811     {
812         if ($this->needsset) {
813             require_once(dirname(__FILE__) . '/strlen.inc.php');
814         }
815         if (empty($params["font"])) {
816             $font = $this->default['font'];
817         } else {
818             $font = $params["font"];
819             switch ($font) {
820                 case "Times-Roman" :
821                     $font = "Times";
822                     break;
823                 case "Helvetica-Oblique" :
824                     $font = "Helvetica";
825                     break;
826                 case "Helvetica-BoldOblique" :
827                     $font = "Helvetica-Bold";
828                     break;
829                 case "ZapfDingbats" :
830                     $font = "Dingbats";
831                     break;
832             }
833         }
834         if ($params["height"] == 0) {
835             $size = $this->default['height'];
836         } else {
837             $size = $params["height"];
838         }
839         $tab = '';
840         for ($i = 0; $i < $tabwidth; $i++) {
841                 $tab .= ' ';
842         }
843         $string = str_replace(chr(9), $tab, $string);
844         if (substr($font, 0, 7) == "Courier") {
845             // Courier is a fixed-width font
846             $width = strlen($string) * 600;
847         } else {
848             $width = 0;
849             $len = strlen($string);
850             for ($i = 0; $i < $len; $i++) {
851                 $width += $this->widths[$font][ord($string{$i})];
852             }
853         }
854         // We now have the string width in font units
855         return $width * $size / 1000;
856     }
857
858     function wrap_line(&$text, $width, $param = array())
859     {
860         $maxchars = (int)(1.1 * $width / $this->strlen("i", $param));
861         $words = explode(" ", substr($text, 0, $maxchars));
862         if ($this->strlen($words[0]) >= $width) {
863             $this->_push_error(3001, "Single token too long for allowed space");
864             $final = $words[0];
865         } else {
866             $space = $this->strlen(" ", $param);
867             $len = 0;
868             $word = 0;
869             $final = "";
870             while ($len < $width) {
871                 if ($word >= count($words)) {
872                     break;
873                 }
874                 $temp = $this->strlen($words[$word], $param);
875                 if ( ($len + $temp) <= $width) {
876                     $final .= $words[$word] . " ";
877                     $word ++;
878                 }
879                 $len += $space + $temp;
880             }
881         }
882         $text = substr($text, strlen($final));
883         return $final;
884     }
885
886     function word_wrap($words, $width, $param = array())
887     {
888         // break $words into an array separated by manual paragraph break character
889         $paragraph = explode("\n", $words);
890         // find the width of 1 space in this font
891         $swidth = $this->strlen( " " , $param );
892         // uses each element of $paragraph array and splits it at spaces
893         for ($lc = 0; $lc < count($paragraph); $lc++){
894             while (strlen($paragraph[$lc]) > 0) {
895                 $returnarray[] = $this->wrap_line($paragraph[$lc], $width, $param);
896             }
897         }
898         return $returnarray;
899     }
900
901     function draw_one_paragraph($top, $left, $bottom, $right, $text, $page, $param = array())
902     {
903         $param = $this->_resolve_param($param);
904         $height = 1.1 * $param['height'];
905         $width = $right - $left;
906         while ($top > $bottom) {
907             if (strlen($text) < 1) {
908                 break;
909             }
910             $top -= $height;
911             if ($top >= $bottom) {
912                 $line = $this->wrap_line($text, $width, $param);
913                 switch ($param['align']) {
914                 case 'right' :
915                     $line = trim($line);
916                     $l = $right - $this->strlen($line, $param);
917                     break;
918
919                 case 'center' :
920                     $line = trim($line);
921                     $l = $left + (($width - $this->strlen($line, $param)) / 2);
922                     break;
923                 
924                 default :
925                     $l = $left;
926                 }
927                 $this->draw_text($l, $top, $line, $page, $param);
928             } else {
929                 $top += $height;
930                 break;
931             }
932         }
933         if (strlen($text) > 0) {
934             return $text;
935         } else {
936             return $top;
937         }
938     }
939
940     function draw_paragraph($top, $left, $bottom, $right, $text, $page, $param = array())
941     {
942         $paras = split("\n", $text);
943         for ($i = 0; $i < count($paras); $i++) {
944             $over = $this->draw_one_paragraph($top,
945                                               $left,
946                                               $bottom,
947                                               $right,
948                                               $paras[$i],
949                                               $page,
950                                               $param);
951             if (is_string($over)) {
952                 break;
953             }
954             $top = $over;
955         }
956         $rv = $over;
957         if ($i < count($paras)) {
958             for ($x = $i + 1; $x < count($paras); $x++) {
959                 $rv .= "\n" . $paras[$x];
960             }
961         }
962         return $rv;
963     }
964
965     function error_array()
966     {
967         $rv = array();
968         while (count($this->ermsg) > 0) {
969             $this->pop_error($num, $msg);
970             $rv[] = "Error $num: $msg";
971         }
972         return $rv;
973     }
974
975     function pop_error(&$num, &$msg)
976     {
977         $num = array_pop($this->erno);
978         $msg = array_pop($this->ermsg);
979         if (is_null($num)) {
980                 return false;
981         } else {
982                 return $num;
983         }
984     }
985
986     function enable($name)
987     {
988         $name = strtolower($name);
989         @include_once(dirname(__FILE__) . "/${name}.class.php");
990         $this->x[$name] = new $name;
991         $this->x[$name]->pdf = &$this;
992         switch ($name) {
993         case 'chart' :
994         case 'template' :
995                 $this->$name = &$this->x[$name];
996             break;
997         }
998     }
999
1000     function get_color($desc)
1001     {
1002
1003         $r = array();
1004         switch (strtolower($desc)) {
1005         case 'black' :
1006             $r['red'] = $r['blue'] = $r['green'] = 0;
1007             break;
1008
1009         case 'white' :
1010             $r['red'] = $r['blue'] = $r['green'] = 1;
1011             break;
1012             
1013         case 'red' :
1014             $r['red'] = 1;
1015             $r['blue'] = $r['green'] = 0;
1016             break;
1017             
1018         case 'blue' :
1019             $r['blue'] = 1;
1020             $r['red'] = $r['green'] = 0;
1021             break;
1022
1023         case 'green' :
1024             $r['green'] = 1;
1025             $r['blue'] = $r['red'] = 0;
1026             break;
1027
1028         default :
1029                 if (substr($desc, 0, 1) == '#') {
1030                 // Parse out a hex triplet
1031                 $v = substr($desc, 1, 2);
1032                 $r['red'] = eval("return ord(\"\\x$v\");") / 255;
1033                 $v = substr($desc, 3, 2);
1034                 $r['green'] = eval("return ord(\"\\x$v\");") / 255;
1035                 $v = substr($desc, 5, 2);
1036                 $r['blue'] = eval("return ord(\"\\x$v\");") / 255;
1037             } else {
1038                 // Error condition?
1039                 $this->_push_error(6012, "Unparsable color identifier: $desc");
1040                 $r = false;
1041             }
1042         }
1043         return $r;
1044     }
1045
1046 /******************************************************
1047  * These functions are internally used by the library *
1048  * and shouldn't really be called by a user of        *
1049  * phppdflib                                          *
1050  ******************************************************/
1051
1052     function _resolve_mode($attrib, $mode)
1053     {
1054         $rmode = $attrib[$mode];
1055         if ($rmode != 0) {
1056             $r = $rmode;
1057         } else {
1058             switch ($rmode) {
1059             case "fill":
1060                 $r = 0;
1061                 break;
1062
1063             case "stroke":
1064                 $r = 1;
1065                 break;
1066
1067             case "fill+stroke":
1068                 $r = 2;
1069                 break;
1070             }
1071         }
1072         return $r;
1073     }
1074
1075     function _adjust_margin(&$x, &$y, $page)
1076     {
1077         $x += $this->objects[$page]['margin-left'];
1078         $y += $this->objects[$page]['margin-bottom'];
1079     }
1080
1081     function _resolve_param($param, $text = true)
1082     {
1083         $rv = $this->default;
1084         if (is_array($param)) {
1085             if (isset($param['mode'])) {
1086                 $param['tmode'] = $param['smode'] = $param['mode'];
1087             }
1088             foreach ($param as $key => $value) {
1089                 $rv[$key] = $value;
1090             }
1091         }
1092         return $rv;
1093     }
1094     
1095     function _push_error($num, $msg)
1096     {
1097         array_push($this->erno, $num);
1098         array_push($this->ermsg, $msg);
1099     }
1100
1101     function _push_std_error($num)
1102     {
1103         switch ($num) {
1104             case 6001 : $m = "Object must be of type 'page'"; break;
1105             case 6008 : $m = "Data stream not recognized as JFIF"; break;
1106             case 6009 : $m = "Object must be of type 'image'"; break;
1107             case 6011 : $m = "Data stream not recognized as PNG"; break;
1108             default : $m = "_push_std_error() called with invalid error number: $num"; break;
1109         }
1110         $this->_push_error($num, $m);
1111     }
1112
1113     function _resolve_colors(&$n, $attrib)
1114     {
1115         $temp = array('red','green','blue');
1116         foreach ($temp as $colcomp) {
1117             if (isset($attrib['fillcolor'][$colcomp])) {
1118                 $n['fillcolor'][$colcomp] = $attrib['fillcolor'][$colcomp];
1119             }
1120             if (isset($attrib['strokecolor'][$colcomp])) {
1121                 $n['strokecolor'][$colcomp] = $attrib['strokecolor'][$colcomp];
1122             }
1123         }
1124     }
1125
1126     /* Check to see if a requested font is already in the
1127      * list, if not add it.  Either way, return the libid
1128      * of the font
1129      */
1130     function _use_font($id)
1131     {
1132         if (!isset($id['font'])) {
1133             $id['font'] = $this->default['font'];
1134         }
1135         if ( isset($this->builddata["fonts"]) && count($this->builddata["fonts"]) > 0 ) {
1136             foreach ($this->builddata["fonts"] as $libid => $name) {
1137                 if ($name == $id['font']) {
1138                     return $libid;
1139                 }
1140             }
1141         }
1142         /* The font isn't in the table, so we add it
1143          * and return it's ID
1144          */
1145         return $this->new_font($id['font']);
1146     }
1147
1148     /* Convert a big-endian byte stream into an integer */
1149     function _int_val($string)
1150     {
1151         $r = 0;
1152         for ($i = 0; $i < strlen($string); $i ++ ) {
1153             $r += ord($string{$i}) * pow(256, strlen($string) - $i -1);
1154         }
1155         return $r;
1156     }
1157
1158     function _make_raw_image($liboid)
1159     {
1160         $s["Type"] = "/XObject";
1161         $s["Subtype"] = "/Image";
1162         $s["Width"] = $this->objects[$liboid]["width"];
1163         $s["Height"] = $this->objects[$liboid]["height"];
1164         $s["ColorSpace"] = $this->objects[$liboid]["colorspace"];
1165         $s["BitsPerComponent"] = $this->objects[$liboid]["bpc"];
1166         if (strlen($this->objects[$liboid]["filter"]) > 0) {
1167             $s["Filter"] = $this->objects[$liboid]["filter"];
1168         }
1169         return $this->_streamify($this->objects[$liboid]["data"], $s);
1170     }
1171
1172     function _place_raw_image($liboid)
1173     {
1174         $xscale = $this->objects[$liboid]["xscale"];
1175         $yscale = $this->objects[$liboid]["yscale"];
1176         $angle = $this->objects[$liboid]["rotation"];
1177         $temp = "q 1 0 0 1 " .
1178                 $this->objects[$liboid]["left"] . " " .
1179                 $this->objects[$liboid]["bottom"] . " cm ";
1180         if ($angle != 0) {
1181             $temp .= $this->_rotate($angle) . " cm ";
1182         }
1183         if ($xscale != 1 || $yscale != 1) {
1184             $temp .= "$xscale 0 0 $yscale 0 0 cm ";
1185         }
1186         $temp .= "/Img" . $this->objects[$liboid]["image"] .
1187                  " Do Q\x0a";
1188         return $temp;
1189     }
1190
1191     function _rotate($angle)
1192     {
1193         $a = deg2rad($angle);
1194         $cos = cos($a);
1195         $sin = sin($a);
1196         $r = sprintf("%1\$01.6f %2\$01.6f %3\$01.6f %1\$01.6f 0 0", $cos, $sin, -$sin);
1197         return $r;
1198     }
1199
1200     function _get_operator($liboid)
1201     {
1202         switch ($this->objects[$liboid]['mode']) {
1203         case 0 : return "f"; break;
1204         case 1 : return "S"; break;
1205         case 2 : return "b"; break;
1206         }
1207     }
1208
1209     function _make_line($liboid)
1210     {
1211         $gstate = "";
1212         if ( $colortest = $this->_colorset($liboid) ) {
1213             $gstate .= $colortest . " ";
1214         }
1215         if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
1216             $gstate .= $this->objects[$liboid]["width"] . " w ";
1217         }
1218         $firstpoint = true;
1219         $temp = "";
1220         foreach ($this->objects[$liboid]["x"] as $pointid => $x) {
1221             $y = $this->objects[$liboid]["y"][$pointid];
1222             $temp .= $x . " " . $y . " ";
1223             if ($firstpoint) {
1224                 $temp .= "m ";
1225                 $firstpoint = false;
1226             } else {
1227                 $temp .= "l ";
1228             }
1229         }
1230         $temp .= $this->_get_operator($liboid);
1231         if ( strlen($gstate) > 0 ) {
1232             $temp = "q " . $gstate . $temp . " Q";
1233         }
1234         return $temp . "\x0a";
1235     }
1236
1237     function _make_rect($liboid)
1238     {
1239         $gstate = "";
1240         if ( $colortest = $this->_colorset($liboid) ) {
1241             $gstate .= $colortest . " ";
1242         }
1243         if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
1244             $gstate .= $this->objects[$liboid]["width"] . " w ";
1245         }
1246         $temp = $this->objects[$liboid]["left"] . " ";
1247         $temp .= $this->objects[$liboid]["bottom"];
1248         $temp .= " " . ( $this->objects[$liboid]["right"]
1249                  - $this->objects[$liboid]["left"] );
1250         $temp .= " " . ( $this->objects[$liboid]["top"]
1251                  - $this->objects[$liboid]["bottom"] );
1252         $temp .= ' re ';
1253         $temp .= $this->_get_operator($liboid);
1254         if ( strlen($gstate) > 0 ) {
1255             $temp = "q " . $gstate . $temp . " Q";
1256         }
1257         return $temp . "\x0a";
1258     }
1259
1260     function _make_circle($liboid)
1261     {
1262         $gstate = "";
1263         if ( $colortest = $this->_colorset($liboid) ) {
1264             $gstate .= $colortest . " ";
1265         }
1266         if ( isset($this->objects[$liboid]["width"]) && $this->objects[$liboid]["width"] != 1 ) {
1267             $gstate .= $this->objects[$liboid]["width"] . " w ";
1268         }
1269         $r = $this->objects[$liboid]['radius'];
1270         $x = $this->objects[$liboid]['x'];
1271         $y = $this->objects[$liboid]['y'];
1272         $ql = $x - $r;
1273         $pt = $y + $r * 1.33333;
1274         $qr = $x + $r;
1275         $pb = $y - $r * 1.33333;
1276         $temp = "$ql $y m ";
1277         $temp .= "$ql $pt $qr $pt $qr $y c ";
1278         $temp .= "$qr $pb $ql $pb $ql $y c ";
1279         $temp .= $this->_get_operator($liboid);
1280         if ( strlen($gstate) > 0 ) {
1281             $temp = "q " . $gstate . $temp . " Q";
1282         }
1283         return $temp . "\x0a";
1284     }
1285
1286     function _make_text($liboid)
1287     {
1288         $statechange = ""; $locateinbt = true;
1289         $statechange = $this->_colorset($liboid);
1290         if (isset($this->objects[$liboid]["rotation"]) && $this->objects[$liboid]["rotation"] != 0) {
1291             $statechange .= "1 0 0 1 " .
1292                             $this->objects[$liboid]["left"] . " " .
1293                             $this->objects[$liboid]["bottom"] . " cm " .
1294                             $this->_rotate($this->objects[$liboid]["rotation"]) .
1295                             " cm ";
1296             $locateinbt = false;
1297         }
1298         $temp = "BT ";
1299         if ($this->objects[$liboid]["mode"] != 0) {
1300             $temp .= $this->objects[$liboid]["mode"] .
1301                             " Tr ";
1302             // Adjust stroke width
1303             $statechange .= $this->objects[$liboid]["height"] / 35 . " w ";
1304         }
1305         $temp .= "/F" . $this->objects[$liboid]["font"] . " ";
1306         $temp .= $this->objects[$liboid]["height"];
1307         $temp .= " Tf ";
1308         if ($locateinbt) {
1309             $temp .= $this->objects[$liboid]["left"] . " " .
1310                      $this->objects[$liboid]["bottom"];
1311         } else {
1312             $temp .= "0 0";
1313         }
1314         $temp .= " Td ";
1315         $temp .= $this->_stringify($this->objects[$liboid]["text"]);
1316         $temp .= " Tj ";
1317         $temp .= "ET";
1318         if (strlen($statechange) > 0) {
1319             $temp = "q " . $statechange . $temp . " Q";
1320         }
1321         return $temp . "\x0a";
1322     }
1323
1324     function _colorset($libid)
1325     {
1326         $red = isset($this->objects[$libid]['fillcolor']["red"]) ? (float)$this->objects[$libid]['fillcolor']["red"] : 0;
1327         $green = isset($this->objects[$libid]['fillcolor']["green"]) ? (float)$this->objects[$libid]['fillcolor']["green"] : 0;
1328         $blue = isset($this->objects[$libid]['fillcolor']["blue"]) ? (float)$this->objects[$libid]['fillcolor']["blue"] : 0;
1329         if (($red > 0) || ($green > 0) || ($blue > 0)) {
1330             $r = $red . " " . $green . " " . $blue;
1331             $r .= " rg ";
1332         } else {
1333             $r = "";
1334         }
1335         $red = isset($this->objects[$libid]['strokecolor']["red"]) ? (float)$this->objects[$libid]['strokecolor']["red"] : 0;
1336         $green = isset($this->objects[$libid]['strokecolor']["green"]) ? (float)$this->objects[$libid]['strokecolor']["green"] : 0;
1337         $blue = isset($this->objects[$libid]['strokecolor']["blue"]) ? (float)$this->objects[$libid]['strokecolor']["blue"] : 0;
1338         if (($red > 0) || ($green > 0) || ($blue > 0) ) {
1339             $r .= $red . " " . $green . " " . $blue;
1340             $r .= " RG ";
1341         }
1342         return $r;
1343     }
1344
1345     /* Used to determine what pdflib objects need converted
1346      * to actual PDF objects.
1347      */
1348     function _becomes_object($object)
1349     {
1350         if ($object == "null") {
1351             return false;
1352         }
1353         return true;
1354     }
1355
1356     /* builds an array of child objects */
1357     function _get_kids($pdfid)
1358     {
1359         $libid = $this->pdftolib[$pdfid];
1360         foreach( $this->objects as $obid => $object ) {
1361             if (isset($object["parent"]) && $object["parent"] == $libid) {
1362                 $kids[] = $this->libtopdf[$obid] . " 0 R";
1363             }
1364         }
1365         return $kids;
1366     }
1367
1368     /* builds an array of pages, in order */
1369     function _order_pages($pdfid)
1370     {
1371         $libid = $this->pdftolib[$pdfid];
1372         foreach( $this->objects as $obid => $object ) {
1373             if (isset($object["parent"]) && $object["parent"] == $libid) {
1374                 $kids[$object["number"]] = $this->libtopdf[$obid] . " 0 R";
1375             }
1376         }
1377         ksort($kids);
1378         return $kids;
1379     }
1380
1381     /* simple helper function to return the current oid
1382      * and increment it by one
1383      */
1384     function _addnewoid()
1385     {
1386         $o = $this->nextoid;
1387         $this->nextoid++;
1388         return $o;
1389     }
1390
1391     /* The xreftable will contain a list of all the
1392      * objects in the pdf file and the number of bytes
1393      * from the beginning of the file that the object
1394      * occurs. Each time we add an object, we call this
1395      * to record it's location, then call ->_genxreftable()
1396      * to generate the table from array
1397      */
1398     function _addtoxreftable($offset, $gennum)
1399     {
1400         $this->nextobj ++;
1401         $this->xreftable[$this->nextobj]["offset"] = $offset;
1402         $this->xreftable[$this->nextobj]["gennum"] = $gennum;
1403         $this->xreftable[$this->nextobj]["free"] = "n";
1404         return $this->nextobj;
1405     }
1406
1407     /* Returns a properly formatted pdf dictionary
1408      * containing entries specified by
1409      * the array $entries
1410      */
1411     function _makedictionary($entries)
1412     {
1413         $rs = "<<\x0a";
1414         if (isset($entries) && count($entries) > 0) {
1415             foreach ($entries as $key => $value) {
1416                 $rs .= "/" . $key . " " . $value . "\x0a";
1417             }
1418         }
1419         $rs .= ">>";
1420         return $rs;
1421     }
1422
1423     /* returns a properly formatted pdf array */
1424     function _makearray($entries)
1425     {
1426         $rs = "[";
1427         if ( is_array($entries) ) {
1428             foreach ($entries as $entry) {
1429                 $rs .= $entry . " ";
1430             }
1431         } else {
1432             $rs .= $entries;
1433         }
1434         $rs = rtrim($rs) . "]";
1435         return $rs;
1436     }
1437
1438     /* Returns a properly formatted string, with any
1439      * special characters escaped
1440      */
1441     function _stringify($string)
1442     {
1443         // Escape potentially problematic characters
1444         $string = preg_replace("-\\\\-","\\\\\\\\",$string);
1445         $bad = array ("-\(-", "-\)-" );
1446         $good = array ("\\(", "\\)" );
1447         $string = preg_replace($bad,$good,$string);
1448         return "(" . rtrim($string) . ")";
1449     }
1450
1451     function _streamify($data, $sarray = array())
1452     {
1453         /* zlib compression is a compile time option
1454          * for php, thus we need to make sure it's
1455          * available before using it.
1456          */
1457         if ( function_exists('gzcompress') && $this->builddata["compress"] ) {
1458             // For now, we don't compress if already using a filter
1459             if ( !isset($sarray["Filter"]) || strlen($sarray["Filter"]) == 0 ) {
1460                 $sarray["Filter"] = "/FlateDecode";
1461             } else {
1462                 $sarray['Filter'] = "[/FlateDecode " . $sarray['Filter'] . "]";
1463             }
1464             $data = gzcompress($data, $this->builddata["compress"]);
1465         }
1466         $sarray["Length"] = strlen($data);
1467         $os = $this->_makedictionary($sarray);
1468         $os .= "stream\x0a" . $data . "\x0aendstream";
1469         return $os;
1470     }
1471
1472     /* Returns a properly formatted page node
1473      * page nodes with 0 kids are not created
1474      */
1475     function _makepagenode($kids, $addtlopts = false)
1476     {
1477         $parray["Type"] = "/Pages";
1478         if ( isset($kids) AND count($kids) > 0 ) {
1479             // Array of child objects
1480             $parray["Kids"] = $this->_makearray($kids);
1481             // Number of pages
1482             $parray["Count"] = count($kids);
1483         } else {
1484             // No kids is an error condition
1485             $this->_push_error(600, "Pagenode has no children");
1486             return false;
1487         }
1488         if ( is_array($addtlopts) ) {
1489             foreach ( $addtlopts as $key => $value ) {
1490                 $parray[$key] = $value;
1491             }
1492         }
1493
1494         /* The resource dictionary is always object 3
1495          */
1496         $parray["Resources"] = "3 0 R";
1497
1498         $os = $this->_makedictionary($parray);
1499         $os = "obj" . $os . "endobj";
1500         return $os;
1501     }
1502
1503     function _makepage($parent, $contents, $liboid)
1504     {
1505         $parray["Type"] = "/Page";
1506         $parray["Parent"] = $this->libtopdf[$parent] . " 0 R";
1507         $parray["Contents"] = $this->_makearray($contents);
1508         $parray["MediaBox"] = "[0 0 "
1509                             . $this->objects[$liboid]["width"] . " "
1510                             . $this->objects[$liboid]["height"] . "]";
1511         $os = $this->_makedictionary($parray);
1512         $os = "obj" . $os . "endobj";
1513         return $os;
1514     }
1515
1516 }
1517 ?>