]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/tcpdf/tcpdf.php
Release 6.2.2
[Github/sugarcrm.git] / include / tcpdf / tcpdf.php
1 <?php
2
3 /*
4
5 Modification information for LGPL compliance
6
7 Mon May 30 16:16:13 2011 +0300 - alex-vlasov - Bug 41216 - currency is displayed on 2 lines instead of one when printing a quote to PDF
8
9 r57813 - 2010-08-19 10:34:44 -0700 (Thu, 19 Aug 2010) - kjing - Author: John Mertic <jmertic@sugarcrm.com>
10     Bug 39085 - When loading the opposite search panel via ajax on the ListViews, call the index action instead of the ListView action to avoid touching pre-MVC code by accident.
11
12 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
13
14 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
15
16 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
17
18 r53409 - 2010-01-03 19:31:15 -0800 (Sun, 03 Jan 2010) - roger - merge -r50376:HEAD from fuji_newtag_tmp
19
20 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
21
22 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
23
24 r51443 - 2009-10-12 13:34:36 -0700 (Mon, 12 Oct 2009) - jmertic - Bug 33332 - Made application PHP 5.3 compliant with E_DEPRECATED warnings on by:
25 - Changing all ereg function to either preg or simple string based ones
26 - No more references to magic quotes.
27 - Change all the session_unregister() functions to just unset() the correct session variable instead.
28
29 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
30
31 r47904 - 2009-06-02 11:54:10 -0700 (Tue, 02 Jun 2009) - jenny - Adding changes that were made to the earlier version of this file to support jpg images.
32
33 r47900 - 2009-06-02 11:26:55 -0700 (Tue, 02 Jun 2009) - jenny - Updating with changes from bsoufflet.
34
35 r46662 - 2009-04-29 10:48:07 -0700 (Wed, 29 Apr 2009) - jenny - Invoking native jpg image support which isnt' automatically there.
36
37 r46617 - 2009-04-28 16:08:45 -0700 (Tue, 28 Apr 2009) - jenny - Fixing a bug in TCPDF that checks to see if the GD library is installed.
38
39 r46451 - 2009-04-23 16:57:40 -0700 (Thu, 23 Apr 2009) - jenny - tcpdf initial checkin.
40
41
42 */
43
44
45 //============================================================+
46 // File name   : tcpdf.php
47 // Begin       : 2002-08-03
48 // Last Update : 2009-05-28
49 // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
50 // Version     : 4.6.013
51 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
52 //      ----------------------------------------------------------------------------
53 //  Copyright (C) 2002-2009  Nicola Asuni - Tecnick.com S.r.l.
54 //      
55 //      This program is free software: you can redistribute it and/or modify
56 //      it under the terms of the GNU Lesser General Public License as published by
57 //      the Free Software Foundation, either version 2.1 of the License, or
58 //      (at your option) any later version.
59 //      
60 //      This program is distributed in the hope that it will be useful,
61 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
62 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
63 //      GNU Lesser General Public License for more details.
64 //      
65 //      You should have received a copy of the GNU Lesser General Public License
66 //      along with this program.  If not, see <http://www.gnu.org/licenses/>.
67 //      
68 //      See LICENSE.TXT file for more information.
69 //  ----------------------------------------------------------------------------
70 //
71 // Description : This is a PHP class for generating PDF documents without 
72 //               requiring external extensions.
73 //
74 // NOTE:
75 // This class was originally derived in 2002 from the Public 
76 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 
77 // but now is almost entirely rewritten.
78 //
79 // Main features:
80 //  * no external libraries are required for the basic functions;
81 //      * supports all ISO page formats;
82 //      * supports custom page formats, margins and units of measure;
83 //      * supports UTF-8 Unicode and Right-To-Left languages;
84 //      * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
85 //      * supports document encryption;
86 //      * includes methods to publish some XHTML code;
87 //      * includes graphic (geometric) and transformation methods;
88 //      * includes Javascript and forms support;
89 //      * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;
90 //      * includes methods to set Bookmarks and print a Table of Content;
91 //      * includes methods to move and delete pages;
92 //      * includes methods for automatic page header and footer management;
93 //      * supports automatic page break;
94 //      * supports automatic page numbering and page groups;
95 //      * supports automatic line break and text justification;
96 //      * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
97 //      * supports stroke and clipping mode for text;
98 //      * supports clipping masks;
99 //      * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
100 //      * supports several annotations, including links, text and file attachments;
101 //      * supports page compression (requires zlib extension);
102 //  * supports text hyphenation.
103 //  * supports transactions to UNDO commands.
104 //
105 // -----------------------------------------------------------
106 // THANKS TO:
107 // 
108 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
109 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
110 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
111 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
112 // dullus for text Justification.
113 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
114 // Patrick Benny for text stretch suggestion on Cell().
115 // Johannes G�ntert for JavaScript support.
116 // Denis Van Nuffelen for Dynamic Form.
117 // Jacek Czekaj for multibyte justification
118 // Anthony Ferrara for the reintroduction of legacy image methods.
119 // Sourceforge user 1707880 (hucste) for line-trough mode.
120 // Larry Stanbery for page groups.
121 // Martin Hall-May for transparency.
122 // Aaron C. Spike for Polycurve method.
123 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
124 // Moritz Wagner and Andreas Wurmser for graphic functions.
125 // Andrew Whitehead for core fonts support.
126 // Esteban Jo�l Mar�n for OpenType font conversion.
127 // Teus Hagen for several suggestions and fixes.
128 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
129 // Kosmas Papachristos for some CSS improvements.
130 // Marcel Partap for some fixes.
131 // Won Kyu Park for several suggestions, fixes and patches.
132 // Anyone that has reported a bug or sent a suggestion.
133 //============================================================+
134
135 /**
136  * This is a PHP class for generating PDF documents without requiring external extensions.<br>
137  * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
138  * <h3>TCPDF main features are:</h3>
139  * <ul>
140 * <li>no external libraries are required for the basic functions;</li>
141 * <li>supports all ISO page formats;</li>
142 * <li>supports custom page formats, margins and units of measure;</li>
143 * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
144 * <li>supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>
145 * <li>supports document encryption;</li>
146 * <li>includes methods to publish some XHTML code;</li>
147 * <li>includes graphic (geometric) and transformation methods;</li>
148 * <li>includes Javascript and forms support;</li>
149 * <li>includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;</li>
150 * <li>includes methods to set Bookmarks and print a Table of Content;</li>
151 * <li>includes methods to move and delete pages;</li>
152 * <li>includes methods for automatic page header and footer management;</li>
153 * <li>supports automatic page break;</li>
154 * <li>supports automatic page numbering and page groups;</li>
155 * <li>supports automatic line break and text justification;</li>
156 * <li>supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li>
157 * <li>supports stroke and clipping mode for text;</li>
158 * <li>supports clipping masks;</li>
159 * <li>supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
160 * <li>supports several annotations, including links, text and file attachments;</li>
161 * <li>supports page compression (requires zlib extension);</li>
162 * <li>supports text hyphenation.</li>
163 * <li>supports transactions to UNDO commands.</li>
164  * </ul>
165  * Tools to encode your unicode fonts are on fonts/utils directory.</p>
166  * @package com.tecnick.tcpdf
167  * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
168  * @author Nicola Asuni
169  * @copyright 2002-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
170  * @link http://www.tcpdf.org
171  * @license http://www.gnu.org/copyleft/lesser.html LGPL
172  * @version 4.6.013
173  */
174
175 /**
176  * main configuration file
177  */
178 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
179
180 // includes some support files
181
182 /**
183  * unicode data
184  */
185 require_once(dirname(__FILE__).'/unicode_data.php');
186
187 /**
188  * html colors table
189  */
190 require_once(dirname(__FILE__).'/htmlcolors.php');
191
192 if (!class_exists('TCPDF', false)) {
193         /**
194          * define default PDF document producer
195          */ 
196         define('PDF_PRODUCER', 'TCPDF 4.6.013 (http://www.tcpdf.org)');
197         
198         /**
199         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
200         * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
201         * @name TCPDF
202         * @package com.tecnick.tcpdf
203         * @version 4.6.013
204         * @author Nicola Asuni - info@tecnick.com
205         * @link http://www.tcpdf.org
206         * @license http://www.gnu.org/copyleft/lesser.html LGPL
207         */
208         class TCPDF {
209                 
210                 // protected or Protected properties
211
212                 /**
213                 * @var current page number
214                 * @access protected
215                 */
216                 protected $page;
217                 
218                 /**
219                 * @var current object number
220                 * @access protected
221                 */
222                 protected $n;
223
224                 /**
225                 * @var array of object offsets
226                 * @access protected
227                 */
228                 protected $offsets;
229
230                 /**
231                 * @var buffer holding in-memory PDF
232                 * @access protected
233                 */
234                 protected $buffer;
235
236                 /**
237                 * @var array containing pages
238                 * @access protected
239                 */
240                 protected $pages = array();
241
242                 /**
243                 * @var current document state
244                 * @access protected
245                 */
246                 protected $state;
247
248                 /**
249                 * @var compression flag
250                 * @access protected
251                 */
252                 protected $compress;
253                 
254                 /**
255                 * @var current page orientation (P = Portrait, L = Landscape)
256                 * @access protected
257                 */
258                 protected $CurOrientation;
259
260                 /**
261                 * @var array that stores page dimensions and graphic status.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>
262                 * @access protected
263                 */
264                 protected $pagedim = array();
265
266                 /**
267                 * @var scale factor (number of points in user unit)
268                 * @access protected
269                 */
270                 protected $k;
271
272                 /**
273                 * @var width of page format in points
274                 * @access protected
275                 */
276                 protected $fwPt;
277
278                 /**
279                 * @var height of page format in points
280                 * @access protected
281                 */
282                 protected $fhPt;
283
284                 /**
285                 * @var current width of page in points
286                 * @access protected
287                 */
288                 protected $wPt;
289
290                 /**
291                 * @var current height of page in points
292                 * @access protected
293                 */
294                 protected $hPt;
295
296                 /**
297                 * @var current width of page in user unit
298                 * @access protected
299                 */
300                 protected $w;
301
302                 /**
303                 * @var current height of page in user unit
304                 * @access protected
305                 */
306                 protected $h;
307
308                 /**
309                 * @var left margin
310                 * @access protected
311                 */
312                 protected $lMargin;
313
314                 /**
315                 * @var top margin
316                 * @access protected
317                 */
318                 protected $tMargin;
319
320                 /**
321                 * @var right margin
322                 * @access protected
323                 */
324                 protected $rMargin;
325
326                 /**
327                 * @var page break margin
328                 * @access protected
329                 */
330                 protected $bMargin;
331
332                 /**
333                 * @var cell internal padding
334                 * @access protected
335                 */
336                 //protected
337                 public $cMargin;
338                 
339                 /**
340                 * @var cell internal padding (previous value)
341                 * @access protected
342                 */
343                 protected $oldcMargin;
344
345                 /**
346                 * @var current horizontal position in user unit for cell positioning
347                 * @access protected
348                 */
349                 protected $x;
350
351                 /**
352                 * @var current vertical position in user unit for cell positioning
353                 * @access protected
354                 */
355                 protected $y;
356
357                 /**
358                 * @var height of last cell printed
359                 * @access protected
360                 */
361                 protected $lasth;
362
363                 /**
364                 * @var line width in user unit
365                 * @access protected
366                 */
367                 protected $LineWidth;
368
369                 /**
370                 * @var array of standard font names
371                 * @access protected
372                 */
373                 protected $CoreFonts;
374
375                 /**
376                 * @var array of used fonts
377                 * @access protected
378                 */
379                 protected $fonts = array();
380
381                 /**
382                 * @var array of font files
383                 * @access protected
384                 */
385                 protected $FontFiles = array();
386
387                 /**
388                 * @var array of encoding differences
389                 * @access protected
390                 */
391                 protected $diffs = array();
392
393                 /**
394                 * @var array of used images
395                 * @access protected
396                 */
397                 protected $images = array();
398
399                 /**
400                 * @var array of Annotations in pages
401                 * @access protected
402                 */
403                 protected $PageAnnots = array();
404
405                 /**
406                 * @var array of internal links
407                 * @access protected
408                 */
409                 protected $links = array();
410
411                 /**
412                 * @var current font family
413                 * @access protected
414                 */
415                 protected $FontFamily;
416
417                 /**
418                 * @var current font style
419                 * @access protected
420                 */
421                 protected $FontStyle;
422                 
423                 /**
424                 * @var current font ascent (distance between font top and baseline)
425                 * @access protected
426                 * @since 2.8.000 (2007-03-29)
427                 */
428                 protected $FontAscent;
429                 
430                 /**
431                 * @var current font descent (distance between font bottom and baseline)
432                 * @access protected
433                 * @since 2.8.000 (2007-03-29)
434                 */
435                 protected $FontDescent;
436
437                 /**
438                 * @var underlining flag
439                 * @access protected
440                 */
441                 protected $underline;
442
443                 /**
444                 * @var current font info
445                 * @access protected
446                 */
447                 protected $CurrentFont;
448
449                 /**
450                 * @var current font size in points
451                 * @access protected
452                 */
453                 protected $FontSizePt;
454
455                 /**
456                 * @var current font size in user unit
457                 * @access protected
458                 */
459                 protected $FontSize;
460
461                 /**
462                 * @var commands for drawing color
463                 * @access protected
464                 */
465                 protected $DrawColor;
466
467                 /**
468                 * @var commands for filling color
469                 * @access protected
470                 */
471                 protected $FillColor;
472
473                 /**
474                 * @var commands for text color
475                 * @access protected
476                 */
477                 protected $TextColor;
478
479                 /**
480                 * @var indicates whether fill and text colors are different
481                 * @access protected
482                 */
483                 protected $ColorFlag;
484
485                 /**
486                 * @var automatic page breaking
487                 * @access protected
488                 */
489                 protected $AutoPageBreak;
490
491                 /**
492                 * @var threshold used to trigger page breaks
493                 * @access protected
494                 */
495                 protected $PageBreakTrigger;
496
497                 /**
498                 * @var flag set when processing footer
499                 * @access protected
500                 */
501                 protected $InFooter = false;
502
503                 /**
504                 * @var zoom display mode
505                 * @access protected
506                 */
507                 protected $ZoomMode;
508
509                 /**
510                 * @var layout display mode
511                 * @access protected
512                 */
513                 protected $LayoutMode;
514
515                 /**
516                 * @var title
517                 * @access protected
518                 */
519                 protected $title = '';
520
521                 /**
522                 * @var subject
523                 * @access protected
524                 */
525                 protected $subject = '';
526
527                 /**
528                 * @var author
529                 * @access protected
530                 */
531                 protected $author = '';
532
533                 /**
534                 * @var keywords
535                 * @access protected
536                 */
537                 protected $keywords = '';
538
539                 /**
540                 * @var creator
541                 * @access protected
542                 */
543                 protected $creator = '';
544
545                 /**
546                 * @var alias for total number of pages
547                 * @access protected
548                 */
549                 protected $AliasNbPages = '{nb}';
550                 
551                 /**
552                 * @var alias for page number
553                 * @access protected
554                 */
555                 protected $AliasNumPage = '{pnb}';
556                 
557                 /**
558                 * @var right-bottom corner X coordinate of inserted image
559                 * @since 2002-07-31
560                 * @author Nicola Asuni
561                 * @access protected
562                 */
563                 protected $img_rb_x;
564
565                 /**
566                 * @var right-bottom corner Y coordinate of inserted image
567                 * @since 2002-07-31
568                 * @author Nicola Asuni
569                 * @access protected
570                 */
571                 protected $img_rb_y;
572
573                 /**
574                 * @var adjusting factor to convert pixels to user units.
575                 * @since 2004-06-14
576                 * @author Nicola Asuni
577                 * @access protected
578                 */
579                 protected $imgscale = 1;
580
581                 /**
582                 * @var boolean set to true when the input text is unicode (require unicode fonts)
583                 * @since 2005-01-02
584                 * @author Nicola Asuni
585                 * @access protected
586                 */
587                 protected $isunicode = false;
588
589                 /**
590                 * @var PDF version
591                 * @since 1.5.3
592                 * @access protected
593                 */
594                 protected $PDFVersion = '1.7';
595                 
596                 
597                 // ----------------------
598                 
599                 /**
600                  * @var Minimum distance between header and top page margin.
601                  * @access protected
602                  */
603                 protected $header_margin;
604                 
605                 /**
606                  * @var Minimum distance between footer and bottom page margin.
607                  * @access protected
608                  */
609                 protected $footer_margin;
610                 
611                 /**
612                  * @var original left margin value
613                  * @access protected
614                  * @since 1.53.0.TC013
615                  */
616                 protected $original_lMargin;
617                 
618                 /**
619                  * @var original right margin value
620                  * @access protected
621                  * @since 1.53.0.TC013
622                  */
623                 protected $original_rMargin;
624                         
625                 /**
626                  * @var Header font.
627                  * @access protected
628                  */
629                 protected $header_font;
630                 
631                 /**
632                  * @var Footer font.
633                  * @access protected
634                  */
635                 protected $footer_font;
636                 
637                 /**
638                  * @var Language templates.
639                  * @access protected
640                  */
641                 protected $l;
642                 
643                 /**
644                  * @var Barcode to print on page footer (only if set).
645                  * @access protected
646                  */
647                 protected $barcode = false;
648                 
649                 /**
650                  * @var If true prints header
651                  * @access protected
652                  */
653                 protected $print_header = true;
654                 
655                 /**
656                  * @var If true prints footer.
657                  * @access protected
658                  */
659                 protected $print_footer = true;
660                         
661                 /**
662                  * @var Header image logo.
663                  * @access protected
664                  */
665                 protected $header_logo = '';
666                 
667                 /**
668                  * @var Header image logo width in mm.
669                  * @access protected
670                  */
671                 protected $header_logo_width = 30;
672                 
673                 /**
674                  * @var String to print as title on document header.
675                  * @access protected
676                  */
677                 protected $header_title = '';
678                 
679                 /**
680                  * @var String to print on document header.
681                  * @access protected
682                  */
683                 protected $header_string = '';
684                 
685                 /**
686                  * @var Default number of columns for html table.
687                  * @access protected
688                  */
689                 protected $default_table_columns = 4;
690                 
691                 
692                 // variables for html parser
693                 
694                 /**
695                  * @var HTML PARSER: array to store current link and rendering styles.
696                  * @access protected
697                  */
698                 protected $HREF = array();
699                 
700                 /**
701                  * @var store a list of available fonts on filesystem.
702                  * @access protected
703                  */
704                 protected $fontlist = array();
705                 
706                 /**
707                  * @var current foreground color
708                  * @access protected
709                  */
710                 protected $fgcolor;
711                                                 
712                 /**
713                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
714                  * @access protected
715                  */
716                 protected $listordered = array();
717                 
718                 /**
719                  * @var HTML PARSER: array count list items on nested lists.
720                  * @access protected
721                  */
722                 protected $listcount = array();
723                 
724                 /**
725                  * @var HTML PARSER: current list nesting level.
726                  * @access protected
727                  */
728                 protected $listnum = 0;
729                 
730                 /**
731                  * @var HTML PARSER: indent amount for lists.
732                  * @access protected
733                  */
734                 protected $listindent;
735                 
736                 /**
737                  * @var current background color
738                  * @access protected
739                  */
740                 protected $bgcolor;
741                 
742                 /**
743                  * @var Store temporary font size in points.
744                  * @access protected
745                  */
746                 protected $tempfontsize = 10;
747                 
748                 /**
749                  * @var spacer for LI tags.
750                  * @access protected
751                  */
752                 protected $lispacer = '';
753                 
754                 /**
755                  * @var default encoding
756                  * @access protected
757                  * @since 1.53.0.TC010
758                  */
759                 protected $encoding = 'UTF-8';
760                 
761                 /**
762                  * @var PHP internal encoding
763                  * @access protected
764                  * @since 1.53.0.TC016
765                  */
766                 protected $internal_encoding;
767                 
768                 /**
769                  * @var indicates if the document language is Right-To-Left
770                  * @access protected
771                  * @since 2.0.000
772                  */
773                 protected $rtl = false;
774                 
775                 /**
776                  * @var used to force RTL or LTR string inversion
777                  * @access protected
778                  * @since 2.0.000
779                  */
780                 protected $tmprtl = false;
781                 
782                 // --- Variables used for document encryption:
783                 
784                 /**
785                  * Indicates whether document is protected
786                  * @access protected
787                  * @since 2.0.000 (2008-01-02)
788                  */
789                 protected $encrypted;
790                 
791                 /**
792                  * U entry in pdf document
793                  * @access protected
794                  * @since 2.0.000 (2008-01-02)
795                  */
796                 protected $Uvalue;
797                 
798                 /**
799                  * O entry in pdf document
800                  * @access protected
801                  * @since 2.0.000 (2008-01-02)
802                  */
803                 protected $Ovalue;
804                 
805                 /**
806                  * P entry in pdf document
807                  * @access protected
808                  * @since 2.0.000 (2008-01-02)
809                  */
810                 protected $Pvalue;
811                 
812                 /**
813                  * encryption object id
814                  * @access protected
815                  * @since 2.0.000 (2008-01-02)
816                  */
817                 protected $enc_obj_id;
818                 
819                 /**
820                  * last RC4 key encrypted (cached for optimisation)
821                  * @access protected
822                  * @since 2.0.000 (2008-01-02)
823                  */
824                 protected $last_rc4_key;
825                 
826                 /**
827                  * last RC4 computed key
828                  * @access protected
829                  * @since 2.0.000 (2008-01-02)
830                  */
831                 protected $last_rc4_key_c;
832                 
833                 /**
834                  * RC4 padding
835                  * @access protected
836                  */
837                 protected $padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
838                 
839                 /**
840                  * RC4 encryption key
841                  * @access protected
842                  */
843                 protected $encryption_key;
844                 
845                 // --- bookmark ---
846                 
847                 /**
848                  * Outlines for bookmark
849                  * @access protected
850                  * @since 2.1.002 (2008-02-12)
851                  */
852                 protected $outlines = array();
853                 
854                 /**
855                  * Outline root for bookmark
856                  * @access protected
857                  * @since 2.1.002 (2008-02-12)
858                  */
859                 protected $OutlineRoot;
860                 
861                 
862                 // --- javascript and form ---
863                 
864                 /**
865                  * javascript code
866                  * @access protected
867                  * @since 2.1.002 (2008-02-12)
868                  */
869                 protected $javascript = '';
870                 
871                 /**
872                  * javascript counter
873                  * @access protected
874                  * @since 2.1.002 (2008-02-12)
875                  */
876                 protected $n_js;
877
878                 /**
879                  * line trough state
880                  * @access protected
881                  * @since 2.8.000 (2008-03-19)
882                  */
883                 protected $linethrough;
884
885                 // --- Variables used for User's Rights ---
886                 // See PDF reference chapter 8.7 Digital Signatures
887
888                 /**
889                  * If true enables user's rights on PDF reader
890                  * @access protected
891                  * @since 2.9.000 (2008-03-26)
892                  */
893                 protected $ur;
894
895                 /**
896                  * Names specifying additional document-wide usage rights for the document.
897                  * @access protected
898                  * @since 2.9.000 (2008-03-26)
899                  */
900                 protected $ur_document;
901
902                 /**
903                  * Names specifying additional annotation-related usage rights for the document.
904                  * @access protected
905                  * @since 2.9.000 (2008-03-26)
906                  */
907                 protected $ur_annots;
908
909                 /**
910                  * Names specifying additional form-field-related usage rights for the document.
911                  * @access protected
912                  * @since 2.9.000 (2008-03-26)
913                  */
914                 protected $ur_form;
915
916                 /**
917                  * Names specifying additional signature-related usage rights for the document.
918                  * @access protected
919                  * @since 2.9.000 (2008-03-26)
920                  */
921                 protected $ur_signature;
922
923                 /**
924                  * Dot Per Inch Document Resolution (do not change)
925                  * @access protected
926                  * @since 3.0.000 (2008-03-27)
927                  */
928                 protected $dpi = 72;
929                 
930                 /**
931                  * Array of page numbers were a new page group was started
932                  * @access protected
933                  * @since 3.0.000 (2008-03-27)
934                  */
935                 protected $newpagegroup = array();
936                 
937                 /**
938                  * Contains the number of pages of the groups
939                  * @access protected
940                  * @since 3.0.000 (2008-03-27)
941                  */
942                 protected $pagegroups;
943                 
944                 /**
945                  * Contains the alias of the current page group
946                  * @access protected
947                  * @since 3.0.000 (2008-03-27)
948                  */
949                 protected $currpagegroup; 
950                 
951                 /**
952                  * Restrict the rendering of some elements to screen or printout.
953                  * @access protected
954                  * @since 3.0.000 (2008-03-27)
955                  */
956                 protected $visibility = 'all';
957                 
958                 /**
959                  * Print visibility.
960                  * @access protected
961                  * @since 3.0.000 (2008-03-27)
962                  */
963                 protected $n_ocg_print;
964                 
965                 /**
966                  * View visibility.
967                  * @access protected
968                  * @since 3.0.000 (2008-03-27)
969                  */
970                 protected $n_ocg_view;
971                 
972                 /**
973                  * Array of transparency objects and parameters.
974                  * @access protected
975                  * @since 3.0.000 (2008-03-27)
976                  */
977                 protected $extgstates;
978                 
979                 /**
980                  * Set the default JPEG compression quality (1-100)
981                  * @access protected
982                  * @since 3.0.000 (2008-03-27)
983                  */
984                 protected $jpeg_quality;
985                 
986                 /**
987                  * Default cell height ratio.
988                  * @access protected
989                  * @since 3.0.014 (2008-05-23)
990                  */
991                 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
992                 
993                 /**
994                  * PDF viewer preferences.
995                  * @access protected
996                  * @since 3.1.000 (2008-06-09)
997                  */
998                 protected $viewer_preferences;
999                 
1000                 /**
1001                  * A name object specifying how the document should be displayed when opened.
1002                  * @access protected
1003                  * @since 3.1.000 (2008-06-09)
1004                  */
1005                 protected $PageMode;
1006                 
1007                 /**
1008                  * Array for storing gradient information.
1009                  * @access protected
1010                  * @since 3.1.000 (2008-06-09)
1011                  */
1012                 protected $gradients = array();
1013                 
1014                 /**
1015                  * Array used to store positions inside the pages buffer.
1016                  * keys are the page numbers
1017                  * @access protected
1018                  * @since 3.2.000 (2008-06-26)
1019                  */
1020                 protected $intmrk = array();
1021                 
1022                 /**
1023                  * Array used to store footer positions of each page.
1024                  * @access protected
1025                  * @since 3.2.000 (2008-07-01)
1026                  */
1027                 protected $footerpos = array();
1028                 
1029                 
1030                 /**
1031                  * Array used to store footer lenght of each page.
1032                  * @access protected
1033                  * @since 4.0.014 (2008-07-29)
1034                  */
1035                 protected $footerlen = array();
1036                 
1037                 /**
1038                  * True if a newline is created.
1039                  * @access protected
1040                  * @since 3.2.000 (2008-07-01)
1041                  */
1042                 protected $newline = true;
1043                 
1044                 /**
1045                  * End position of the latest inserted line
1046                  * @access protected
1047                  * @since 3.2.000 (2008-07-01)
1048                  */
1049                 protected $endlinex = 0;
1050                 
1051                 /**
1052                  * PDF string for last line width
1053                  * @access protected
1054                  * @since 4.0.006 (2008-07-16)
1055                  */
1056                 protected $linestyleWidth = '';
1057                 
1058                 /**
1059                  * PDF string for last line width
1060                  * @access protected
1061                  * @since 4.0.006 (2008-07-16)
1062                  */
1063                 protected $linestyleCap = '0 J';
1064                 
1065                 /**
1066                  * PDF string for last line width
1067                  * @access protected
1068                  * @since 4.0.006 (2008-07-16)
1069                  */
1070                 protected $linestyleJoin = '0 j';
1071                 
1072                 /**
1073                  * PDF string for last line width
1074                  * @access protected
1075                  * @since 4.0.006 (2008-07-16)
1076                  */
1077                 protected $linestyleDash = '[] 0 d';
1078                 
1079                 /**
1080                  * True if marked-content sequence is open
1081                  * @access protected
1082                  * @since 4.0.013 (2008-07-28)
1083                  */
1084                 protected $openMarkedContent = false;
1085                 
1086                 /**
1087                  * Count the latest inserted vertical spaces on HTML
1088                  * @access protected
1089                  * @since 4.0.021 (2008-08-24)
1090                  */
1091                 protected $htmlvspace = 0;
1092                 
1093                 /**
1094                  * Array of Spot colors
1095                  * @access protected
1096                  * @since 4.0.024 (2008-09-12)
1097                  */
1098                 protected $spot_colors = array();
1099                 
1100                 /**
1101                  * Symbol used for HTML unordered list items
1102                  * @access protected
1103                  * @since 4.0.028 (2008-09-26)
1104                  */
1105                 protected $lisymbol = '';
1106                 
1107                 /**
1108                  * String used to mark the beginning and end of EPS image blocks
1109                  * @access protected
1110                  * @since 4.1.000 (2008-10-18)
1111                  */
1112                 protected $epsmarker = 'x#!#EPS#!#x';
1113                 
1114                 /**
1115                  * Array of transformation matrix
1116                  * @access protected
1117                  * @since 4.2.000 (2008-10-29)
1118                  */
1119                 protected $transfmatrix = array();
1120                 
1121                 /**
1122                  * Booklet mode for double-sided pages
1123                  * @access protected
1124                  * @since 4.2.000 (2008-10-29)
1125                  */
1126                 protected $booklet = false;
1127                 
1128                 /**
1129                  * Epsilon value used for float calculations
1130                  * @access protected
1131                  * @since 4.2.000 (2008-10-29)
1132                  */
1133                 protected $feps = 0.001;
1134                 
1135                 /**
1136                  * Array used for custom vertical spaces for HTML tags
1137                  * @access protected
1138                  * @since 4.2.001 (2008-10-30)
1139                  */
1140                 protected $tagvspaces = array();
1141                 
1142                 /**
1143                  * @var HTML PARSER: custom indent amount for lists.
1144                  * Negative value means disabled.
1145                  * @access protected
1146                  * @since 4.2.007 (2008-11-12)
1147                  */
1148                 protected $customlistindent = -1;
1149                 
1150                 /**
1151                  * @var if true keeps the border open for the cell sides that cross the page.
1152                  * @access protected
1153                  * @since 4.2.010 (2008-11-14)
1154                  */
1155                 protected $opencell = true;
1156
1157                 /**
1158                  * @var array of files to embedd
1159                  * @access protected
1160                  * @since 4.4.000 (2008-12-07)
1161                  */
1162                 protected $embeddedfiles = array();
1163
1164                 /**
1165                  * @var boolean true when inside html pre tag
1166                  * @access protected
1167                  * @since 4.4.001 (2008-12-08)
1168                  */
1169                 protected $premode = false;
1170
1171                 /**
1172                  * Array used to store positions of graphics transformation blocks inside the page buffer.
1173                  * keys are the page numbers
1174                  * @access protected
1175                  * @since 4.4.002 (2008-12-09)
1176                  */
1177                 protected $transfmrk = array();
1178
1179                 /**
1180                  * Default color for html links
1181                  * @access protected
1182                  * @since 4.4.003 (2008-12-09)
1183                  */
1184                 protected $htmlLinkColorArray = array(0, 0, 255);
1185
1186                 /**
1187                  * Default font style to add to html links
1188                  * @access protected
1189                  * @since 4.4.003 (2008-12-09)
1190                  */
1191                 protected $htmlLinkFontStyle = 'U';
1192
1193                 /**
1194                  * Counts the number of pages.
1195                  * @access protected
1196                  * @since 4.5.000 (2008-12-31)
1197                  */
1198                 protected $numpages = 0;
1199
1200                 /**
1201                  * Array containing page lenghts in bytes.
1202                  * @access protected
1203                  * @since 4.5.000 (2008-12-31)
1204                  */
1205                 protected $pagelen = array();
1206
1207                 /**
1208                  * Counts the number of pages.
1209                  * @access protected
1210                  * @since 4.5.000 (2008-12-31)
1211                  */
1212                 protected $numimages = 0;
1213
1214                 /**
1215                  * Store the image keys.
1216                  * @access protected
1217                  * @since 4.5.000 (2008-12-31)
1218                  */
1219                 protected $imagekeys = array();
1220
1221                 /**
1222                  * Lenght of the buffer in bytes.
1223                  * @access protected
1224                  * @since 4.5.000 (2008-12-31)
1225                  */
1226                 protected $bufferlen = 0;
1227
1228                 /**
1229                  * If true enables disk caching.
1230                  * @access protected
1231                  * @since 4.5.000 (2008-12-31)
1232                  */
1233                 protected $diskcache = false;
1234
1235                 /**
1236                  * Counts the number of fonts.
1237                  * @access protected
1238                  * @since 4.5.000 (2009-01-02)
1239                  */
1240                 protected $numfonts = 0;
1241
1242                 /**
1243                  * Store the font keys.
1244                  * @access protected
1245                  * @since 4.5.000 (2009-01-02)
1246                  */
1247                 protected $fontkeys = array();
1248
1249                 /**
1250                  * Store the fage status (true when opened, false when closed).
1251                  * @access protected
1252                  * @since 4.5.000 (2009-01-02)
1253                  */
1254                 protected $pageopen = array();
1255                 
1256                 /**
1257                  * Default monospaced font
1258                  * @access protected
1259                  * @since 4.5.025 (2009-03-10)
1260                  */
1261                 protected $default_monospaced_font = 'courier';
1262
1263                 /**
1264                  * Used to store a cloned copy of the current class object
1265                  * @access protected
1266                  * @since 4.5.029 (2009-03-19)
1267                  */
1268                 protected $objcopy;
1269
1270                 /**
1271                  * Array used to store the lenghts of cache files
1272                  * @access protected
1273                  * @since 4.5.029 (2009-03-19)
1274                  */
1275                 protected $cache_file_lenght = array();
1276
1277                 /**
1278                  * Table header content to be repeated on each new page
1279                  * @access protected
1280                  * @since 4.5.030 (2009-03-20)
1281                  */
1282                 protected $thead = '';
1283
1284                 /**
1285                  * Distance between the top of page and end of table headers on a new page.
1286                  * @access protected
1287                  * @since 4.5.030 (2009-03-20)
1288                  */
1289                 protected $theadMargin = '';
1290
1291                 /**
1292                  * Cache array for UTF8StringToArray() method.
1293                  * @access protected
1294                  * @since 4.5.037 (2009-04-07)
1295                  */
1296                 protected $cache_UTF8StringToArray = array();
1297
1298                 /**
1299                  * Maximum size of cache array used for UTF8StringToArray() method.
1300                  * @access protected
1301                  * @since 4.5.037 (2009-04-07)
1302                  */
1303                 protected $cache_maxsize_UTF8StringToArray = 8;
1304
1305                 /**
1306                  * Current size of cache array used for UTF8StringToArray() method.
1307                  * @access protected
1308                  * @since 4.5.037 (2009-04-07)
1309                  */
1310                 protected $cache_size_UTF8StringToArray = 0;
1311
1312                 /**
1313                  * If true enables document signing
1314                  * @access protected
1315                  * @since 4.6.005 (2009-04-24)
1316                  */
1317                 protected $sign = false;
1318
1319                 /**
1320                  * Signature data
1321                  * @access protected
1322                  * @since 4.6.005 (2009-04-24)
1323                  */
1324                 protected $signature_data = array();
1325
1326                 /**
1327                  * Signature max lenght
1328                  * @access protected
1329                  * @since 4.6.005 (2009-04-24)
1330                  */
1331                 protected $signature_max_lenght = 5120;
1332
1333                 /**
1334                  * Regular expression used to find blank characters used for word-wrapping.
1335                  * @access protected
1336                  * @since 4.6.006 (2009-04-28)
1337                  */
1338                 protected $re_spaces = '/[\s\p{Z}\p{Lo}]/';
1339
1340                 //------------------------------------------------------------
1341                 // METHODS
1342                 //------------------------------------------------------------
1343
1344                 /**
1345                  * This is the class constructor. 
1346                  * It allows to set up the page format, the orientation and 
1347                  * the measure unit used in all the methods (except for the font sizes).
1348                  * @since 1.0
1349                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1350                  * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1351                  * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1352                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1353                  * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1354                  * @param String $encoding charset encoding; default is UTF-8
1355                  * @access public
1356                  */
1357                 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1358                         /* Set internal character encoding to ASCII */
1359                         if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1360                                 $this->internal_encoding = mb_internal_encoding();
1361                                 mb_internal_encoding('ASCII');
1362                         }
1363                         // set disk caching
1364                         $this->diskcache = $diskcache ? true : false;
1365                         // set language direction
1366                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1367                         $this->tmprtl = false;
1368                         //Some checks
1369                         $this->_dochecks();
1370                         //Initialization of properties
1371                         $this->isunicode = $unicode;
1372                         $this->page = 0;
1373                         $this->transfmrk[0] = array();
1374                         $this->pagedim = array();
1375                         $this->n = 2;
1376                         $this->buffer = '';
1377                         $this->pages = array();
1378                         $this->state = 0;
1379                         $this->fonts = array();
1380                         $this->FontFiles = array();
1381                         $this->diffs = array();
1382                         $this->images = array();
1383                         $this->links = array();
1384                         $this->gradients = array();
1385                         $this->InFooter = false;
1386                         $this->lasth = 0;
1387                         $this->FontFamily = 'helvetica';
1388                         $this->FontStyle = '';
1389                         $this->FontSizePt = 12;
1390                         $this->underline = false;
1391                         $this->linethrough = false;
1392                         $this->DrawColor = '0 G';
1393                         $this->FillColor = '0 g';
1394                         $this->TextColor = '0 g';
1395                         $this->ColorFlag = false;
1396                         // encryption values
1397                         $this->encrypted = false;
1398                         $this->last_rc4_key = '';
1399                         $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
1400                         //Standard Unicode fonts
1401                         $this->CoreFonts = array(
1402                                 'courier'=>'Courier',
1403                                 'courierB'=>'Courier-Bold',
1404                                 'courierI'=>'Courier-Oblique',
1405                                 'courierBI'=>'Courier-BoldOblique',
1406                                 'helvetica'=>'Helvetica',
1407                                 'helveticaB'=>'Helvetica-Bold',
1408                                 'helveticaI'=>'Helvetica-Oblique',
1409                                 'helveticaBI'=>'Helvetica-BoldOblique',
1410                                 'times'=>'Times-Roman',
1411                                 'timesB'=>'Times-Bold',
1412                                 'timesI'=>'Times-Italic',
1413                                 'timesBI'=>'Times-BoldItalic',
1414                                 'symbol'=>'Symbol',
1415                                 'zapfdingbats'=>'ZapfDingbats'
1416                         );
1417                         //Set scale factor
1418                         $this->setPageUnit($unit);
1419                         // set page format and orientation
1420                         $this->setPageFormat($format, $orientation);
1421                         //Page margins (1 cm)
1422                         $margin = 28.35 / $this->k;
1423                         $this->SetMargins($margin, $margin);
1424                         //Interior cell margin
1425                         $this->cMargin = $margin / 10;
1426                         //Line width (0.2 mm)
1427                         $this->LineWidth = 0.57 / $this->k;
1428                         $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1429                         $this->linestyleCap = '0 J';
1430                         $this->linestyleJoin = '0 j';
1431                         $this->linestyleDash = '[] 0 d';
1432                         //Automatic page break
1433                         $this->SetAutoPageBreak(true, (2 * $margin));
1434                         //Full width display mode
1435                         $this->SetDisplayMode('fullwidth');
1436                         //Compression
1437                         $this->SetCompression(true);
1438                         //Set default PDF version number
1439                         $this->PDFVersion = '1.7';
1440                         $this->encoding = $encoding;
1441                         $this->HREF = array();
1442                         $this->getFontsList();
1443                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1444                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1445                         $this->extgstates = array();
1446                         // user's rights
1447                         $this->sign = false;
1448                         $this->ur = false;
1449                         $this->ur_document = '/FullSave';
1450                         $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
1451                         $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1452                         $this->ur_signature = '/Modify';                        
1453                         // set default JPEG quality
1454                         $this->jpeg_quality = 75;
1455                         // initialize some settings
1456                         $this->utf8Bidi(array(''), '');
1457                         // set default font
1458                         $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1459                         // check if PCRE Unicode support is enabled
1460                         if (@preg_match('/\pL/u', 'a') == 1) {
1461                                 // PCRE unicode support is turned ON
1462                                 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1463                                 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1464                                 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1465                                 $this->re_spaces = '/[\s\p{Z}\p{Lo}]/';
1466                         } else {
1467                                 // PCRE unicode support is turned OFF
1468                                 $this->re_spaces = '/[\s]/';
1469                         }
1470                 }
1471                 
1472                 /**
1473                  * Default destructor.
1474                  * @access public
1475                  * @since 1.53.0.TC016
1476                  */
1477                 public function __destruct() {
1478                         // restore internal encoding
1479                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1480                                 mb_internal_encoding($this->internal_encoding);
1481                         }
1482                         // unset all class variables
1483                         $this->_destroy(true);
1484                 }
1485                 
1486                 /**
1487                  * Set the units of measure for the document.
1488                  * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1489                  * @access public
1490                  * @since 3.0.015 (2008-06-06)
1491                  */
1492                 public function setPageUnit($unit) {
1493                 //Set scale factor
1494                         switch (strtolower($unit)) {
1495                                 // points
1496                                 case 'px':
1497                                 case 'pt': {
1498                                         $this->k = 1;
1499                                         break;
1500                                 }
1501                                 // millimeters
1502                                 case 'mm': {
1503                                         $this->k = $this->dpi / 25.4;
1504                                         break;
1505                                 }
1506                                 // centimeters
1507                                 case 'cm': {
1508                                         $this->k = $this->dpi / 2.54;
1509                                         break;
1510                                 }
1511                                 // inches
1512                                 case 'in': {
1513                                         $this->k = $this->dpi;
1514                                         break;
1515                                 }
1516                                 // unsupported unit
1517                                 default : {
1518                                         $this->Error('Incorrect unit: '.$unit);
1519                                         break;
1520                                 }
1521                         }
1522                         if (isset($this->CurOrientation)) {
1523                                         $this->setPageOrientation($this->CurOrientation);
1524                         }
1525                 }
1526                 
1527                 /**
1528                 * Set the page format
1529                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1530                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1531                 * @access public
1532                 * @since 3.0.015 (2008-06-06)
1533                 */
1534                 public function setPageFormat($format, $orientation='P') {
1535                         //Page format
1536                         if (is_string($format)) {
1537                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1538                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1539                                 switch (strtoupper($format)) {
1540                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1541                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1542                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1543                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1544                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1545                                         case 'A3': {$format = array(841.89,1190.55); break;}
1546                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1547                                         case 'A5': {$format = array(419.53,595.28); break;}
1548                                         case 'A6': {$format = array(297.64,419.53); break;}
1549                                         case 'A7': {$format = array(209.76,297.64); break;}
1550                                         case 'A8': {$format = array(147.40,209.76); break;}
1551                                         case 'A9': {$format = array(104.88,147.40); break;}
1552                                         case 'A10': {$format = array(73.70,104.88); break;}
1553                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1554                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1555                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1556                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1557                                         case 'B4': {$format = array(708.66,1000.63); break;}
1558                                         case 'B5': {$format = array(498.90,708.66); break;}
1559                                         case 'B6': {$format = array(354.33,498.90); break;}
1560                                         case 'B7': {$format = array(249.45,354.33); break;}
1561                                         case 'B8': {$format = array(175.75,249.45); break;}
1562                                         case 'B9': {$format = array(124.72,175.75); break;}
1563                                         case 'B10': {$format = array(87.87,124.72); break;}
1564                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1565                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1566                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1567                                         case 'C3': {$format = array(918.43,1298.27); break;}
1568                                         case 'C4': {$format = array(649.13,918.43); break;}
1569                                         case 'C5': {$format = array(459.21,649.13); break;}
1570                                         case 'C6': {$format = array(323.15,459.21); break;}
1571                                         case 'C7': {$format = array(229.61,323.15); break;}
1572                                         case 'C8': {$format = array(161.57,229.61); break;}
1573                                         case 'C9': {$format = array(113.39,161.57); break;}
1574                                         case 'C10': {$format = array(79.37,113.39); break;}
1575                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1576                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1577                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1578                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1579                                         case 'RA4': {$format = array(609.45,864.57); break;}
1580                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1581                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1582                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1583                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1584                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1585                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1586                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1587                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1588                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1589                                 }
1590                                 $this->fwPt = $format[0];
1591                                 $this->fhPt = $format[1];
1592                         } else {
1593                                 $this->fwPt = $format[0] * $this->k;
1594                                 $this->fhPt = $format[1] * $this->k;
1595                         }
1596                         $this->setPageOrientation($orientation);
1597                 }
1598                 
1599                 /**
1600                 * Set page orientation.
1601                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1602                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1603                 * @param float $bottommargin bottom margin of the page.
1604                 * @access public
1605                 * @since 3.0.015 (2008-06-06)
1606                 */
1607                 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1608                         $orientation = strtoupper($orientation);
1609                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1610                                 $this->CurOrientation = 'P';
1611                                 $this->wPt = $this->fwPt;
1612                                 $this->hPt = $this->fhPt;
1613                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1614                                 $this->CurOrientation = 'L';
1615                                 $this->wPt = $this->fhPt;
1616                                 $this->hPt = $this->fwPt;
1617                         } else {
1618                                 $this->Error('Incorrect orientation: '.$orientation);
1619                         }
1620                         $this->w = $this->wPt / $this->k;
1621                         $this->h = $this->hPt / $this->k;
1622                         if ($this->empty_string($autopagebreak)) {
1623                                 if (isset($this->AutoPageBreak)) {
1624                                         $autopagebreak = $this->AutoPageBreak;
1625                                 } else {
1626                                         $autopagebreak = true;
1627                                 }
1628                         }
1629                         if ($this->empty_string($bottommargin)) {
1630                                 if (isset($this->bMargin)) {
1631                                         $bottommargin = $this->bMargin;
1632                                 } else {
1633                                         // default value = 2 cm
1634                                         $bottommargin = 2 * 28.35 / $this->k;
1635                                 }
1636                         }
1637                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1638                         // store page dimensions
1639                         $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
1640                 }
1641                                 
1642                 /**
1643                  * Enable or disable Right-To-Left language mode
1644                  * @param Boolean $enable if true enable Right-To-Left language mode.
1645                  * @access public
1646                 * @since 2.0.000 (2008-01-03)
1647                  */
1648                 public function setRTL($enable) {
1649                         $this->rtl = $enable ? true : false;
1650                         $this->tmprtl = false;
1651                 }
1652                 
1653                 /**
1654                  * Return the RTL status
1655                  * @return boolean
1656                  * @access public
1657                  * @since 4.0.012 (2008-07-24)
1658                  */
1659                 public function getRTL() {
1660                         return $this->rtl;
1661                 }
1662                 
1663                 /**
1664                 * Force temporary RTL language direction
1665                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1666                 * @access public
1667                 * @since 2.1.000 (2008-01-09)
1668                 */
1669                 public function setTempRTL($mode) {
1670                         switch ($mode) {
1671                                 case false:
1672                                 case 'L':
1673                                 case 'R': {
1674                                         $this->tmprtl = $mode;
1675                                 }
1676                         }
1677                 }
1678                 
1679                 /**
1680                 * Set the last cell height.
1681                 * @param float $h cell height.
1682                 * @author Nicola Asuni
1683                 * @access public
1684                 * @since 1.53.0.TC034
1685                 */
1686                 public function setLastH($h) {
1687                         $this->lasth = $h;
1688                 }
1689                 
1690                 /**
1691                 * Get the last cell height.
1692                 * @return last cell height
1693                 * @access public
1694                 * @since 4.0.017 (2008-08-05)
1695                 */
1696                 public function getLastH() {
1697                         return $this->lasth;
1698                 }
1699                 
1700                 /**
1701                 * Set the adjusting factor to convert pixels to user units.
1702                 * @param float $scale adjusting factor to convert pixels to user units.
1703                 * @author Nicola Asuni
1704                 * @access public
1705                 * @since 1.5.2
1706                 */
1707                 public function setImageScale($scale) {
1708                         $this->imgscale = $scale;
1709                 }
1710
1711                 /**
1712                 * Returns the adjusting factor to convert pixels to user units.
1713                 * @return float adjusting factor to convert pixels to user units.
1714                 * @author Nicola Asuni
1715                 * @access public
1716                 * @since 1.5.2
1717                 */
1718                 public function getImageScale() {
1719                         return $this->imgscale;
1720                 }
1721                                 
1722                 /**
1723                 * Returns an array of page dimensions:
1724                 * <ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>
1725                 * @param int $pagenum page number (empty = current page)
1726                 * @return array of page dimensions.
1727                 * @author Nicola Asuni
1728                 * @access public
1729                 * @since 4.5.027 (2009-03-16)
1730                 */
1731                 public function getPageDimensions($pagenum='') {
1732                         if (empty($pagenum)) {
1733                                 $pagenum = $this->page;
1734                         }
1735                         return $this->pagedim[$pagenum];
1736                 }
1737                 
1738                 /**
1739                 * Returns the page width in units.
1740                 * @param int $pagenum page number (empty = current page)
1741                 * @return int page width.
1742                 * @author Nicola Asuni
1743                 * @access public
1744                 * @since 1.5.2
1745                 * @see getPageDimensions()
1746                 */
1747                 public function getPageWidth($pagenum='') {
1748                         if (empty($pagenum)) {
1749                                 return $this->w;
1750                         }
1751                         return $this->pagedim[$pagenum]['w'];
1752                 }
1753
1754                 /**
1755                 * Returns the page height in units.
1756                 * @param int $pagenum page number (empty = current page)
1757                 * @return int page height.
1758                 * @author Nicola Asuni
1759                 * @access public
1760                 * @since 1.5.2
1761                 * @see getPageDimensions()
1762                 */
1763                 public function getPageHeight($pagenum='') {
1764                         if (empty($pagenum)) {
1765                                 return $this->h;
1766                         }
1767                         return $this->pagedim[$pagenum]['h'];
1768                 }
1769
1770                 /**
1771                 * Returns the page break margin.
1772                 * @param int $pagenum page number (empty = current page)
1773                 * @return int page break margin.
1774                 * @author Nicola Asuni
1775                 * @access public
1776                 * @since 1.5.2
1777                 * @see getPageDimensions()
1778                 */
1779                 public function getBreakMargin($pagenum='') {
1780                         if (empty($pagenum)) {
1781                                 return $this->bMargin;
1782                         }
1783                         return $this->pagedim[$pagenum]['bm'];
1784                 }
1785
1786                 /**
1787                 * Returns the scale factor (number of points in user unit).
1788                 * @return int scale factor.
1789                 * @author Nicola Asuni
1790                 * @access public
1791                 * @since 1.5.2
1792                 */
1793                 public function getScaleFactor() {
1794                         return $this->k;
1795                 }
1796
1797                 /**
1798                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1799                 * @param float $left Left margin.
1800                 * @param float $top Top margin.
1801                 * @param float $right Right margin. Default value is the left one.
1802                 * @access public
1803                 * @since 1.0
1804                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1805                 */
1806                 public function SetMargins($left, $top, $right=-1) {
1807                         //Set left, top and right margins
1808                         $this->lMargin = $left;
1809                         $this->tMargin = $top;
1810                         if ($right == -1) {
1811                                 $right = $left;
1812                         }
1813                         $this->rMargin = $right;
1814                 }
1815
1816                 /**
1817                 * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
1818                 * @param float $margin The margin.
1819                 * @access public
1820                 * @since 1.4
1821                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1822                 */
1823                 public function SetLeftMargin($margin) {
1824                         //Set left margin
1825                         $this->lMargin=$margin;
1826                         if (($this->page > 0) AND ($this->x < $margin)) {
1827                                 $this->x = $margin;
1828                         }
1829                 }
1830
1831                 /**
1832                 * Defines the top margin. The method can be called before creating the first page.
1833                 * @param float $margin The margin.
1834                 * @access public
1835                 * @since 1.5
1836                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1837                 */
1838                 public function SetTopMargin($margin) {
1839                         //Set top margin
1840                         $this->tMargin=$margin;
1841                         if (($this->page > 0) AND ($this->y < $margin)) {
1842                                 $this->y = $margin;
1843                         }
1844                 }
1845
1846                 /**
1847                 * Defines the right margin. The method can be called before creating the first page.
1848                 * @param float $margin The margin.
1849                 * @access public
1850                 * @since 1.5
1851                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1852                 */
1853                 public function SetRightMargin($margin) {
1854                         $this->rMargin=$margin;
1855                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1856                                 $this->x = $this->w - $margin;
1857                         }
1858                 }
1859
1860                 /**
1861                 * Set the internal Cell padding.
1862                 * @param float $pad internal padding.
1863                 * @access public
1864                 * @since 2.1.000 (2008-01-09)
1865                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1866                 */
1867                 public function SetCellPadding($pad) {
1868                         $this->cMargin = $pad;
1869                 }
1870
1871                 /**
1872                 * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
1873                 * @param boolean $auto Boolean indicating if mode should be on or off.
1874                 * @param float $margin Distance from the bottom of the page.
1875                 * @access public
1876                 * @since 1.0
1877                 * @see Cell(), MultiCell(), AcceptPageBreak()
1878                 */
1879                 public function SetAutoPageBreak($auto, $margin=0) {
1880                         //Set auto page break mode and triggering margin
1881                         $this->AutoPageBreak = $auto;
1882                         $this->bMargin = $margin;
1883                         $this->PageBreakTrigger = $this->h - $margin;
1884                 }
1885
1886                 /**
1887                 * Defines the way the document is to be displayed by the viewer.
1888                 * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
1889                 * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
1890                 * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
1891                 * @access public
1892                 * @since 1.2
1893                 */
1894                 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
1895                         //Set display mode in viewer
1896                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1897                                 $this->ZoomMode = $zoom;
1898                         } else {
1899                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1900                         }
1901                         switch ($layout) {
1902                                 case 'default':
1903                                 case 'single':
1904                                 case 'SinglePage': {
1905                                         $this->LayoutMode = 'SinglePage';
1906                                         break;
1907                                 }
1908                                 case 'continuous':
1909                                 case 'OneColumn': {
1910                                         $this->LayoutMode = 'OneColumn';
1911                                         break;
1912                                 }
1913                                 case 'two':
1914                                 case 'TwoColumnLeft': {
1915                                         $this->LayoutMode = 'TwoColumnLeft';
1916                                         break;
1917                                 }
1918                                 case 'TwoColumnRight': {
1919                                         $this->LayoutMode = 'TwoColumnRight';
1920                                         break;
1921                                 }
1922                                 case 'TwoPageLeft': {
1923                                         $this->LayoutMode = 'TwoPageLeft';
1924                                         break;
1925                                 }
1926                                 case 'TwoPageRight': {
1927                                         $this->LayoutMode = 'TwoPageRight';
1928                                         break;
1929                                 }
1930                                 default: {
1931                                         $this->LayoutMode = 'SinglePage';
1932                                 }
1933                         }
1934                         switch ($mode) {
1935                                 case 'UseNone': {
1936                                         $this->PageMode = 'UseNone';
1937                                         break;
1938                                 }
1939                                 case 'UseOutlines': {
1940                                         $this->PageMode = 'UseOutlines';
1941                                         break;
1942                                 }
1943                                 case 'UseThumbs': {
1944                                         $this->PageMode = 'UseThumbs';
1945                                         break;
1946                                 }
1947                                 case 'FullScreen': {
1948                                         $this->PageMode = 'FullScreen';
1949                                         break;
1950                                 }
1951                                 case 'UseOC': {
1952                                         $this->PageMode = 'UseOC';
1953                                         break;
1954                                 }
1955                                 case '': {
1956                                         $this->PageMode = 'UseAttachments';
1957                                         break;
1958                                 }
1959                                 default: {
1960                                         $this->PageMode = 'UseNone';
1961                                 }
1962                         }
1963                 }
1964
1965                 /**
1966                 * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
1967                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1968                 * @param boolean $compress Boolean indicating if compression must be enabled.
1969                 * @access public
1970                 * @since 1.4
1971                 */
1972                 public function SetCompression($compress) {
1973                         //Set page compression
1974                         if (function_exists('gzcompress')) {
1975                                 $this->compress = $compress;
1976                         } else {
1977                                 $this->compress = false;
1978                         }
1979                 }
1980
1981                 /**
1982                 * Defines the title of the document.
1983                 * @param string $title The title.
1984                 * @access public
1985                 * @since 1.2
1986                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1987                 */
1988                 public function SetTitle($title) {
1989                         //Title of document
1990                         $this->title = $title;
1991                 }
1992
1993                 /**
1994                 * Defines the subject of the document.
1995                 * @param string $subject The subject.
1996                 * @access public
1997                 * @since 1.2
1998                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1999                 */
2000                 public function SetSubject($subject) {
2001                         //Subject of document
2002                         $this->subject = $subject;
2003                 }
2004
2005                 /**
2006                 * Defines the author of the document.
2007                 * @param string $author The name of the author.
2008                 * @access public
2009                 * @since 1.2
2010                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2011                 */
2012                 public function SetAuthor($author) {
2013                         //Author of document
2014                         $this->author = $author;
2015                 }
2016
2017                 /**
2018                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2019                 * @param string $keywords The list of keywords.
2020                 * @access public
2021                 * @since 1.2
2022                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2023                 */
2024                 public function SetKeywords($keywords) {
2025                         //Keywords of document
2026                         $this->keywords = $keywords;
2027                 }
2028
2029                 /**
2030                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2031                 * @param string $creator The name of the creator.
2032                 * @access public
2033                 * @since 1.2
2034                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2035                 */
2036                 public function SetCreator($creator) {
2037                         //Creator of document
2038                         $this->creator = $creator;
2039                 }
2040                 
2041                 /**
2042                 * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.
2043                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
2044                 * @param string $msg The error message
2045                 * @access public
2046                 * @since 1.0
2047                 */
2048                 public function Error($msg) {
2049                         // unset all class variables
2050                         $this->_destroy(true);
2051                         // exit program and print error
2052                         die('<strong>TCPDF ERROR: </strong>'.$msg);
2053                 }
2054
2055                 /**
2056                 * This method begins the generation of the PDF document.
2057                 * It is not necessary to call it explicitly because AddPage() does it automatically.
2058                 * Note: no page is created by this method
2059                 * @access public
2060                 * @since 1.0
2061                 * @see AddPage(), Close()
2062                 */
2063                 public function Open() {
2064                         //Begin document
2065                         $this->state = 1;
2066                 }
2067
2068                 /**
2069                 * Terminates the PDF document.
2070                 * It is not necessary to call this method explicitly because Output() does it automatically.
2071                 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2072                 * @access public
2073                 * @since 1.0
2074                 * @see Open(), Output()
2075                 */
2076                 public function Close() {
2077                         if ($this->state == 3) {
2078                                 return;
2079                         }
2080                         if ($this->page == 0) {
2081                                 $this->AddPage();
2082                         }
2083                         // close page
2084                         $this->endPage();
2085                         // close document
2086                         $this->_enddoc();
2087                         // unset all class variables (except critical ones)
2088                         $this->_destroy(false);
2089                 }
2090                 
2091                 /**
2092                 * Move pointer at the specified document page and update page dimensions.
2093                 * @param int $pnum page number
2094                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2095                 * @access public
2096                 * @since 2.1.000 (2008-01-07)
2097                 * @see getPage(), lastpage(), getNumPages()
2098                 */
2099                 public function setPage($pnum, $resetmargins=false) {
2100                         if ($pnum == $this->page) {
2101                                 return;
2102                         }
2103                         if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2104                                 $this->state = 2;
2105                                 // save current graphic settings
2106                                 //$gvars = $this->getGraphicVars();
2107                                 $oldpage = $this->page;
2108                                 $this->page = $pnum;
2109                                 $this->wPt = $this->pagedim[$this->page]['w'];
2110                                 $this->hPt = $this->pagedim[$this->page]['h'];
2111                                 $this->w = $this->wPt / $this->k;
2112                                 $this->h = $this->hPt / $this->k;
2113                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
2114                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
2115                                 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2116                                 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
2117                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
2118                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
2119                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
2120                                 // restore graphic settings
2121                                 //$this->setGraphicVars($gvars);
2122                                 if ($resetmargins) {
2123                                         $this->lMargin = $this->pagedim[$this->page]['olm'];
2124                                         $this->rMargin = $this->pagedim[$this->page]['orm'];
2125                                         $this->SetY($this->tMargin);
2126                                 } else {
2127                                         // account for booklet mode
2128                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
2129                                                 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
2130                                                 $this->lMargin += $deltam;
2131                                                 $this->rMargin -= $deltam;
2132                                         }
2133                                 }
2134                         } else {
2135                                 $this->Error('Wrong page number on setPage() function.');
2136                         }
2137                 }
2138                 
2139                 /**
2140                 * Reset pointer to the last document page.
2141                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2142                 * @access public
2143                 * @since 2.0.000 (2008-01-04)
2144                 * @see setPage(), getPage(), getNumPages()
2145                 */
2146                 public function lastPage($resetmargins=false) {
2147                         $this->setPage($this->getNumPages(), $resetmargins);
2148                 }
2149                 
2150                 /**
2151                 * Get current document page number.
2152                 * @return int page number
2153                 * @access public
2154                 * @since 2.1.000 (2008-01-07)
2155                 * @see setPage(), lastpage(), getNumPages()
2156                 */
2157                 public function getPage() {
2158                         return $this->page;
2159                 }
2160                 
2161                 
2162                 /**
2163                 * Get the total number of insered pages.
2164                 * @return int number of pages
2165                 * @access public
2166                 * @since 2.1.000 (2008-01-07)
2167                 * @see setPage(), getPage(), lastpage()
2168                 */
2169                 public function getNumPages() {
2170                         return $this->numpages;
2171                 }
2172
2173                 /**
2174                 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
2175                 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
2176                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2177                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
2178                 * @access public
2179                 * @since 1.0
2180                 * @see startPage(), endPage()
2181                 */
2182                 public function AddPage($orientation='', $format='') {
2183                         if (!isset($this->original_lMargin)) {
2184                                 $this->original_lMargin = $this->lMargin;
2185                         }
2186                         if (!isset($this->original_rMargin)) {
2187                                 $this->original_rMargin = $this->rMargin;
2188                         }
2189                         // terminate previous page
2190                         $this->endPage();
2191                         // start new page
2192                         $this->startPage($orientation, $format);
2193                 }
2194
2195                 /**
2196                 * Terminate the current page
2197                 * @access protected
2198                 * @since 4.2.010 (2008-11-14)
2199                 * @see startPage(), AddPage()
2200                 */
2201                 protected function endPage() {
2202                         // check if page is already closed
2203                         if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
2204                                 return;
2205                         }
2206                         $this->InFooter = true;
2207                         // print page footer
2208                         $this->setFooter();
2209                         // close page
2210                         $this->_endpage();
2211                         // mark page as closed
2212                         $this->pageopen[$this->page] = false;
2213                         $this->InFooter = false;
2214                 }
2215
2216                 /**
2217                 * Starts a new page to the document. The page must be closed using the endPage() function.
2218                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
2219                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2220                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
2221                 * @access protected
2222                 * @since 4.2.010 (2008-11-14)
2223                 * @see endPage(), AddPage()
2224                 */
2225                 protected function startPage($orientation='', $format='') {
2226                         if ($this->numpages > $this->page) {
2227                                 // this page has been already added
2228                                 $this->setPage($this->page + 1);
2229                                 $this->SetY($this->tMargin);
2230                                 return;
2231                         }
2232                         // start a new page
2233                         if ($this->state == 0) {
2234                                 $this->Open();
2235                         }
2236                         ++$this->numpages;
2237                         $this->swapMargins($this->booklet);
2238                         // save current graphic settings
2239                         $gvars = $this->getGraphicVars();
2240                         // start new page
2241                         $this->_beginpage($orientation, $format);
2242                         // mark page as open
2243                         $this->pageopen[$this->page] = true;
2244                         // restore graphic settings
2245                         $this->setGraphicVars($gvars);
2246                         // mark this point
2247                         $this->setPageMark();
2248                         // print page header
2249                         $this->setHeader();
2250                         // restore graphic settings
2251                         $this->setGraphicVars($gvars);
2252                         // mark this point
2253                         $this->setPageMark();
2254                         // print table header (if any)
2255                         $this->setTableHeader();
2256                 }
2257                         
2258                 /**
2259                  * Set start-writing mark on current page for multicell borders and fills.
2260                  * This function must be called after calling Image() function for a background image.
2261                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
2262                  * @access public
2263                  * @since 4.0.016 (2008-07-30)
2264                  */
2265                 public function setPageMark() {
2266                         $this->intmrk[$this->page] = $this->pagelen[$this->page];
2267                 }
2268                 
2269                 /**
2270                  * Set header data.
2271                  * @param string $ln header image logo
2272                  * @param string $lw header image logo width in mm
2273                  * @param string $ht string to print as title on document header
2274                  * @param string $hs string to print on document header
2275                  * @access public
2276                  */
2277                 public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
2278                         $this->header_logo = $ln;
2279                         $this->header_logo_width = $lw;
2280                         $this->header_title = $ht;
2281                         $this->header_string = $hs;
2282                 }
2283                 
2284                 /**
2285                  * Returns header data:
2286                  * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
2287                  * @return array()
2288                  * @access public
2289                  * @since 4.0.012 (2008-07-24)
2290                  */
2291                 public function getHeaderData() {
2292                         $ret = array();
2293                         $ret['logo'] = $this->header_logo;
2294                         $ret['logo_width'] = $this->header_logo_width;
2295                         $ret['title'] = $this->header_title;
2296                         $ret['string'] = $this->header_string;
2297                         return $ret;
2298                 }
2299                 
2300                 /**
2301                  * Set header margin.
2302                  * (minimum distance between header and top page margin)
2303                  * @param int $hm distance in user units
2304                  * @access public
2305                  */
2306                 public function setHeaderMargin($hm=10) {
2307                         $this->header_margin = $hm;
2308                 }
2309                 
2310                 /**
2311                  * Returns header margin in user units.
2312                  * @return float
2313                  * @since 4.0.012 (2008-07-24)
2314                  * @access public
2315                  */
2316                 public function getHeaderMargin() {
2317                         return $this->header_margin;
2318                 }
2319                 
2320                 /**
2321                  * Set footer margin.
2322                  * (minimum distance between footer and bottom page margin)
2323                  * @param int $fm distance in user units
2324                  * @access public
2325                  */
2326                 public function setFooterMargin($fm=10) {
2327                         $this->footer_margin = $fm;
2328                 }
2329                 
2330                 /**
2331                  * Returns footer margin in user units.
2332                  * @return float
2333                  * @since 4.0.012 (2008-07-24)
2334                  * @access public
2335                  */
2336                 public function getFooterMargin() {
2337                         return $this->footer_margin;
2338                 }
2339                 /**
2340                  * Set a flag to print page header.
2341                  * @param boolean $val set to true to print the page header (default), false otherwise. 
2342                  * @access public
2343                  */
2344                 public function setPrintHeader($val=true) {
2345                         $this->print_header = $val;
2346                 }
2347                 
2348                 /**
2349                  * Set a flag to print page footer.
2350                  * @param boolean $value set to true to print the page footer (default), false otherwise. 
2351                  * @access public
2352                  */
2353                 public function setPrintFooter($val=true) {
2354                         $this->print_footer = $val;
2355                 }
2356                 
2357                 /**
2358                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2359                  * @return float 
2360                  * @access public
2361                  */
2362                 public function getImageRBX() {
2363                         return $this->img_rb_x;
2364                 }
2365                 
2366                 /**
2367                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2368                  * @return float 
2369                  * @access public
2370                  */
2371                 public function getImageRBY() {
2372                         return $this->img_rb_y;
2373                 }
2374                 
2375                 /**
2376                  * This method is used to render the page header.
2377                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2378                  * @access public
2379                  */
2380                 public function Header() {
2381                         $ormargins = $this->getOriginalMargins();
2382                         $headerfont = $this->getHeaderFont();
2383                         $headerdata = $this->getHeaderData();
2384                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2385                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2386                                 $imgy = $this->getImageRBY();
2387                         } else {
2388                                 $imgy = $this->GetY();
2389                         }
2390                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2391                         // set starting margin for text data cell
2392                         if ($this->getRTL()) {
2393                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2394                         } else {
2395                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2396                         }
2397                         $this->SetTextColor(0, 0, 0);
2398                         // header title
2399                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2400                         $this->SetX($header_x);                 
2401                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
2402                         // header string
2403                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2404                         $this->SetX($header_x);
2405                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
2406                         // print an ending header line
2407                         $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2408                         $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
2409                         if ($this->getRTL()) {
2410                                 $this->SetX($ormargins['right']);
2411                         } else {
2412                                 $this->SetX($ormargins['left']);
2413                         }
2414                         $this->Cell(0, 0, '', 'T', 0, 'C');
2415                 }
2416                 
2417                 /**
2418                  * This method is used to render the page footer. 
2419                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2420                  * @access public
2421                  */
2422                 public function Footer() {                              
2423                         $cur_y = $this->GetY();
2424                         $ormargins = $this->getOriginalMargins();
2425                         $this->SetTextColor(0, 0, 0);                   
2426                         //set style for cell border
2427                         $line_width = 0.85 / $this->getScaleFactor();
2428                         $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2429                         //print document barcode
2430                         $barcode = $this->getBarcode();
2431                         if (!empty($barcode)) {
2432                                 $this->Ln($line_width);
2433                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2434                                 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');     
2435                         }
2436                         if (empty($this->pagegroups)) {
2437                                 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
2438                         } else {
2439                                 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
2440                         }               
2441                         $this->SetY($cur_y);
2442                         //Print page number
2443                         if ($this->getRTL()) {
2444                                 $this->SetX($ormargins['right']);
2445                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2446                         } else {
2447                                 $this->SetX($ormargins['left']);
2448                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2449                         }
2450                 }
2451                 
2452                 /**
2453                  * This method is used to render the page header. 
2454                  * @access protected
2455                  * @since 4.0.012 (2008-07-24)
2456                  */
2457                 protected function setHeader() {
2458                         if ($this->print_header) {
2459                                 $lasth = $this->lasth;
2460                                 $this->_out('q');
2461                                 $this->rMargin = $this->original_rMargin;
2462                                 $this->lMargin = $this->original_lMargin;
2463                                 $this->cMargin = 0;
2464                                 //set current position
2465                                 if ($this->rtl) {
2466                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2467                                 } else {
2468                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2469                                 }
2470                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2471                                 $this->Header();
2472                                 //restore position
2473                                 if ($this->rtl) {
2474                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2475                                 } else {
2476                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2477                                 }
2478                                 $this->_out('Q');
2479                                 $this->lasth = $lasth;
2480                         }
2481                 }
2482                 
2483                 /**
2484                  * This method is used to render the page footer. 
2485                  * @access protected
2486                  * @since 4.0.012 (2008-07-24)
2487                  */
2488                 protected function setFooter() {
2489                         //Page footer
2490                         // save current graphic settings
2491                         $gvars = $this->getGraphicVars();
2492                         // mark this point
2493                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
2494                         $this->_out("\n");
2495                         if ($this->print_footer) {
2496                                 $lasth = $this->lasth;
2497                                 $this->_out('q');
2498                                 $this->rMargin = $this->original_rMargin;
2499                                 $this->lMargin = $this->original_lMargin;
2500                                 $this->cMargin = 0;
2501                                 //set current position
2502                                 $footer_y = $this->h - $this->footer_margin;
2503                                 if ($this->rtl) {
2504                                         $this->SetXY($this->original_rMargin, $footer_y);
2505                                 } else {
2506                                         $this->SetXY($this->original_lMargin, $footer_y);
2507                                 }
2508                                 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
2509                                 $this->Footer();
2510                                 //restore position
2511                                 if ($this->rtl) {
2512                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2513                                 } else {
2514                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2515                                 }
2516                                 $this->_out('Q');
2517                                 $this->lasth = $lasth;
2518                         }
2519                         // restore graphic settings
2520                         $this->setGraphicVars($gvars);
2521                         // calculate footer lenght
2522                         $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
2523                 }
2524
2525                 /**
2526                  * This method is used to render the table header on new page (if any). 
2527                  * @access protected
2528                  * @since 4.5.030 (2009-03-25)
2529                  */
2530                 protected function setTableHeader() {
2531                         if (!$this->empty_string($this->theadMargin)) {
2532                                 // restore the original top-margin
2533                                 $this->tMargin = $this->theadMargin;
2534                                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
2535                                 $this->y = $this->theadMargin;
2536                         }
2537                         if (!$this->empty_string($this->thead)) {
2538                                 // print table header
2539                                 $this->writeHTML($this->thead, false, false, false, false, '');
2540                                 // set new top margin to skip the table headers
2541                                 if (!isset($this->theadMargin) OR ($this->empty_string($this->theadMargin))) {
2542                                         $this->theadMargin = $this->tMargin;
2543                                 }
2544                                 $this->tMargin = $this->y;
2545                                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2546                         }
2547                 }
2548                 
2549                 /**
2550                 * Returns the current page number.
2551                 * @return int page number
2552                 * @access public
2553                 * @since 1.0
2554                 * @see AliasNbPages(), getAliasNbPages()
2555                 */
2556                 public function PageNo() {
2557                         return $this->page;
2558                 }
2559
2560                 /**
2561                 * Defines a new spot color. 
2562                 * It can be expressed in RGB components or gray scale. 
2563                 * The method can be called before the first page is created and the value is retained from page to page.
2564                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2565                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2566                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2567                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2568                 * @access public
2569                 * @since 4.0.024 (2008-09-12)
2570                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2571                 */
2572                 public function AddSpotColor($name, $c, $m, $y, $k) {
2573                         if (!isset($this->spot_colors[$name])) {
2574                                 $i = 1 + count($this->spot_colors);
2575                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2576                         }
2577                 }
2578
2579                 /**
2580                 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 
2581                 * It can be expressed in RGB components or gray scale. 
2582                 * The method can be called before the first page is created and the value is retained from page to page.
2583                 * @param array $color array of colors
2584                 * @access public
2585                 * @since 3.1.000 (2008-06-11)
2586                 * @see SetDrawColor()
2587                 */
2588                 public function SetDrawColorArray($color) {
2589                         if (isset($color)) {
2590                                 $color = array_values($color);
2591                                 $r = isset($color[0]) ? $color[0] : -1;
2592                                 $g = isset($color[1]) ? $color[1] : -1;
2593                                 $b = isset($color[2]) ? $color[2] : -1;
2594                                 $k = isset($color[3]) ? $color[3] : -1;
2595                                 if ($r >= 0) {
2596                                         $this->SetDrawColor($r, $g, $b, $k);
2597                                 }
2598                         }
2599                 }
2600
2601                 /**
2602                 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2603                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2604                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2605                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2606                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2607                 * @access public
2608                 * @since 1.3
2609                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2610                 */
2611                 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2612                         // set default values
2613                         if (!is_numeric($col1)) {
2614                                 $col1 = 0;
2615                         }
2616                         if (!is_numeric($col2)) {
2617                                 $col2 = -1;
2618                         }
2619                         if (!is_numeric($col3)) {
2620                                 $col3 = -1;
2621                         }
2622                         if (!is_numeric($col4)) {
2623                                 $col4 = -1;
2624                         }
2625                         //Set color for all stroking operations
2626                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2627                                 // Grey scale
2628                                 $this->DrawColor = sprintf('%.3F G', $col1/255);
2629                         } elseif ($col4 == -1) {
2630                                 // RGB
2631                                 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
2632                         } else {
2633                                 // CMYK
2634                                 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
2635                         }
2636                         if ($this->page > 0) {
2637                                 $this->_out($this->DrawColor);
2638                         }
2639                 }
2640                 
2641                 /**
2642                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2643                 * @param string $name name of the spot color
2644                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2645                 * @access public
2646                 * @since 4.0.024 (2008-09-12)
2647                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2648                 */
2649                 public function SetDrawSpotColor($name, $tint=100) {
2650                         if (!isset($this->spot_colors[$name])) {
2651                                 $this->Error('Undefined spot color: '.$name);
2652                         }
2653                         $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
2654                         if ($this->page > 0) {
2655                                 $this->_out($this->DrawColor);
2656                         }
2657                 }
2658                 
2659                 /**
2660                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 
2661                 * It can be expressed in RGB components or gray scale. 
2662                 * The method can be called before the first page is created and the value is retained from page to page.
2663                 * @param array $color array of colors
2664                 * @access public
2665                 * @since 3.1.000 (2008-6-11)
2666                 * @see SetFillColor()
2667                 */
2668                 public function SetFillColorArray($color) {
2669                         if (isset($color)) {
2670                                 $color = array_values($color);
2671                                 $r = isset($color[0]) ? $color[0] : -1;
2672                                 $g = isset($color[1]) ? $color[1] : -1;
2673                                 $b = isset($color[2]) ? $color[2] : -1;
2674                                 $k = isset($color[3]) ? $color[3] : -1;
2675                                 if ($r >= 0) {
2676                                         $this->SetFillColor($r, $g, $b, $k);
2677                                 }
2678                         }
2679                 }
2680                 
2681                 /**
2682                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2683                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2684                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2685                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2686                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2687                 * @access public
2688                 * @since 1.3
2689                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2690                 */
2691                 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2692                         // set default values
2693                         if (!is_numeric($col1)) {
2694                                 $col1 = 0;
2695                         }
2696                         if (!is_numeric($col2)) {
2697                                 $col2 = -1;
2698                         }
2699                         if (!is_numeric($col3)) {
2700                                 $col3 = -1;
2701                         }
2702                         if (!is_numeric($col4)) {
2703                                 $col4 = -1;
2704                         }
2705                         //Set color for all filling operations
2706                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2707                                 // Grey scale
2708                                 $this->FillColor = sprintf('%.3F g', $col1/255);
2709                                 $this->bgcolor = array('G' => $col1);
2710                         } elseif ($col4 == -1) {
2711                                 // RGB
2712                                 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2713                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2714                         } else {
2715                                 // CMYK
2716                                 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2717                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2718                         }
2719                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2720                         if ($this->page > 0) {
2721                                 $this->_out($this->FillColor);
2722                         }
2723                 }
2724                 
2725                 /**
2726                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2727                 * @param string $name name of the spot color
2728                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2729                 * @access public
2730                 * @since 4.0.024 (2008-09-12)
2731                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2732                 */
2733                 public function SetFillSpotColor($name, $tint=100) {
2734                         if (!isset($this->spot_colors[$name])) {
2735                                 $this->Error('Undefined spot color: '.$name);
2736                         }
2737                         $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2738                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2739                         if ($this->page > 0) {
2740                                 $this->_out($this->FillColor);
2741                         }
2742                 }
2743                 
2744                 /**
2745                 * Defines the color used for text. It can be expressed in RGB components or gray scale. 
2746                 * The method can be called before the first page is created and the value is retained from page to page.
2747                 * @param array $color array of colors
2748                 * @access public
2749                 * @since 3.1.000 (2008-6-11)
2750                 * @see SetFillColor()
2751                 */
2752                 public function SetTextColorArray($color) {
2753                         if (isset($color)) {
2754                                 $color = array_values($color);
2755                                 $r = isset($color[0]) ? $color[0] : -1;
2756                                 $g = isset($color[1]) ? $color[1] : -1;
2757                                 $b = isset($color[2]) ? $color[2] : -1;
2758                                 $k = isset($color[3]) ? $color[3] : -1;
2759                                 if ($r >= 0) {
2760                                         $this->SetTextColor($r, $g, $b, $k);
2761                                 }
2762                         }
2763                 }
2764
2765                 /**
2766                 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2767                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2768                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2769                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2770                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2771                 * @access public
2772                 * @since 1.3
2773                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2774                 */
2775                 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2776                         // set default values
2777                         if (!is_numeric($col1)) {
2778                                 $col1 = 0;
2779                         }
2780                         if (!is_numeric($col2)) {
2781                                 $col2 = -1;
2782                         }
2783                         if (!is_numeric($col3)) {
2784                                 $col3 = -1;
2785                         }
2786                         if (!is_numeric($col4)) {
2787                                 $col4 = -1;
2788                         }
2789                         //Set color for text
2790                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2791                                 // Grey scale
2792                                 $this->TextColor = sprintf('%.3F g', $col1/255);
2793                                 $this->fgcolor = array('G' => $col1);
2794                         } elseif ($col4 == -1) {
2795                                 // RGB
2796                                 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2797                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2798                         } else {
2799                                 // CMYK
2800                                 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2801                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2802                         }
2803                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2804                 }
2805                 
2806                 /**
2807                 * Defines the spot color used for text.
2808                 * @param string $name name of the spot color
2809                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2810                 * @access public
2811                 * @since 4.0.024 (2008-09-12)
2812                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2813                 */
2814                 public function SetTextSpotColor($name, $tint=100) {
2815                         if (!isset($this->spot_colors[$name])) {
2816                                 $this->Error('Undefined spot color: '.$name);
2817                         }
2818                         $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2819                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2820                         if ($this->page > 0) {
2821                                 $this->_out($this->TextColor);
2822                         }
2823                 }
2824
2825                 /**
2826                 * Returns the length of a string in user unit. A font must be selected.<br>
2827                 * @param string $s The string whose length is to be computed
2828                 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
2829                 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.
2830                 * @param float $fontsize Font size in points. The default value is the current size.
2831                 * @return int string length
2832                 * @author Nicola Asuni
2833                 * @access public
2834                 * @since 1.2
2835                 */
2836                 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2837                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
2838                 }
2839                 
2840                 /**
2841                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2842                 * @param string $arr The array of chars whose total length is to be computed
2843                 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
2844                 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.
2845                 * @param float $fontsize Font size in points. The default value is the current size.
2846                 * @return int string length
2847                 * @author Nicola Asuni
2848                 * @access public
2849                 * @since 2.4.000 (2008-03-06)
2850                 */
2851                 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2852                         // store current values
2853                         if (!$this->empty_string($fontname)) {
2854                                 $prev_FontFamily = $this->FontFamily;
2855                                 $prev_FontStyle = $this->FontStyle;
2856                                 $prev_FontSizePt = $this->FontSizePt;
2857                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2858                         }
2859                         $w = 0;
2860                         foreach ($sa as $char) {
2861                                 $w += $this->GetCharWidth($char);
2862                         }
2863                         // restore previous values
2864                         if (!$this->empty_string($fontname)) {
2865                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2866                         }
2867                         return $w;
2868                 }
2869                 
2870                 /**
2871                 * Returns the length of the char in user unit for the current font.<br>
2872                 * @param int $char The char code whose length is to be returned
2873                 * @return int char width
2874                 * @author Nicola Asuni
2875                 * @access public
2876                 * @since 2.4.000 (2008-03-06)
2877                 */
2878                 public function GetCharWidth($char) {
2879                         if ($char == 173) {
2880                                 // SHY character will not be printed
2881                                 return (0);
2882                         }
2883                         $cw = &$this->CurrentFont['cw'];
2884                         if (isset($cw[$char])) {
2885                                 $w = $cw[$char];
2886                         } elseif (isset($this->CurrentFont['dw'])) {
2887                                 // default width
2888                                 $w = $this->CurrentFont['dw'];
2889                         } elseif (isset($cw[32])) {
2890                                 // default width
2891                                 $dw = $cw[32];
2892                         } else {
2893                                 $w = 600;
2894                         }
2895                         return ($w * $this->FontSize / 1000);
2896                 }
2897                 
2898                 /**
2899                 * Returns the numbero of characters in a string.
2900                 * @param string $s The input string.
2901                 * @return int number of characters
2902                 * @access public
2903                 * @since 2.0.0001 (2008-01-07)
2904                 */
2905                 public function GetNumChars($s) {
2906                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
2907                                 return count($this->UTF8StringToArray($s));
2908                         } 
2909                         return strlen($s);
2910                 }
2911                         
2912                 /**
2913                 * Fill the list of available fonts ($this->fontlist).
2914                 * @access protected
2915                 * @since 4.0.013 (2008-07-28)
2916                 */
2917                 protected function getFontsList() {
2918                         $fontsdir = opendir($this->_getfontpath());
2919                         while (($file = readdir($fontsdir)) !== false) {
2920                                 if (substr($file, -4) == '.php') {
2921                                         array_push($this->fontlist, strtolower(basename($file, '.php')));
2922                                 }
2923                         }
2924                         closedir($fontsdir);
2925                 }
2926                 
2927                 /**
2928                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2929                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 
2930                 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
2931                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2932                 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
2933                 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
2934                 * @return array containing the font data, or false in case of error.
2935                 * @access public
2936                 * @since 1.5
2937                 * @see SetFont()
2938                 */
2939                 public function AddFont($family, $style='', $fontfile='') {
2940                         if ($this->empty_string($family)) {
2941                                 if (!$this->empty_string($this->FontFamily)) {
2942                                         $family = $this->FontFamily;
2943                                 } else {
2944                                         $this->Error('Empty font family');
2945                                 }
2946                         }
2947                         $family = strtolower($family);
2948                         if ((!$this->isunicode) AND ($family == 'arial')) {
2949                                 $family = 'helvetica';
2950                         }
2951                         if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
2952                                 $style = '';
2953                         }
2954                         $tempstyle = strtoupper($style);
2955                         $style = '';
2956                         // underline
2957                         if (strpos($tempstyle, 'U') !== false) {
2958                                 $this->underline = true;
2959                         } else {
2960                                 $this->underline = false;
2961                         }
2962                         // line through (deleted)
2963                         if (strpos($tempstyle, 'D') !== false) {
2964                                 $this->linethrough = true;
2965                         } else {
2966                                 $this->linethrough = false;
2967                         }
2968                         // bold
2969                         if (strpos($tempstyle, 'B') !== false) {
2970                                 $style .= 'B';
2971                         }
2972                         // oblique
2973                         if (strpos($tempstyle, 'I') !== false) {
2974                                 $style .= 'I';
2975                         }
2976                         $bistyle = $style;
2977                         $fontkey = $family.$style;
2978                         $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
2979                         $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
2980                         // check if the font has been already added
2981                         if ($this->getFontBuffer($fontkey) !== false) {
2982                                 return $fontdata;
2983                         }
2984                         if (isset($type)) {
2985                                 unset($type); 
2986                         }
2987                         if (isset($cw)) {
2988                                 unset($cw); 
2989                         }
2990                         // get specified font directory (if any)
2991                         $fontdir = '';
2992                         if (!$this->empty_string($fontfile)) {
2993                                 $fontdir = dirname($fontfile);
2994                                 if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
2995                                         $fontdir = '';
2996                                 } else {
2997                                         $fontdir .= '/';
2998                                 }
2999                         }
3000                         // search and include font file
3001                         if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
3002                                 // build a standard filenames for specified font
3003                                 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
3004                                 $fontfile2 = str_replace(' ', '', $family).'.php';
3005                                 // search files on various directories
3006                                 if (file_exists($fontdir.$fontfile1)) {
3007                                         $fontfile = $fontdir.$fontfile1;
3008                                 } elseif (file_exists($this->_getfontpath().$fontfile1)) {
3009                                         $fontfile = $this->_getfontpath().$fontfile1;
3010                                 } elseif (file_exists($fontfile1)) {
3011                                         $fontfile = $fontfile1;
3012                                 } elseif (file_exists($fontdir.$fontfile2)) {
3013                                         $fontfile = $fontdir.$fontfile2;
3014                                 } elseif (file_exists($this->_getfontpath().$fontfile2)) {
3015                                         $fontfile = $this->_getfontpath().$fontfile2;
3016                                 } else {
3017                                         $fontfile = $fontfile2;
3018                                 }
3019                         }
3020                         // include font file
3021                         if (file_exists($fontfile)) {
3022                                 include($fontfile);
3023                         } else {
3024                                 $this->Error('Could not include font definition file: '.$family.'');
3025                         }
3026                         // check font parameters
3027                         if ((!isset($type)) OR (!isset($cw))) {
3028                                 $this->Error('The font definition file has a bad format: '.$fontfile.'');
3029                         }
3030                         if (!isset($file)) {
3031                                 $file = '';
3032                         }
3033                         if (!isset($enc)) {
3034                                 $enc = '';
3035                         }
3036                         if (!isset($dw) OR $this->empty_string($dw)) {
3037                                 // set default width
3038                                 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
3039                                         $dw = $desc['MissingWidth'];
3040                                 } elseif (isset($cw[32])) {
3041                                         $dw = $cw[32];
3042                                 } else {
3043                                         $dw = 600;
3044                                 }
3045                         }
3046                         ++$this->numfonts;                      
3047                         // register CID font (all styles at once)
3048                         if ($type == 'cidfont0') {
3049                                 $file = ''; // not embedded
3050                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
3051                                 $sname = $name.$styles[$bistyle];
3052                                 if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
3053                                         $desc['StemV'] = 120;
3054                                 }
3055                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc));
3056                         } elseif ($type == 'core') {
3057                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw));
3058                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
3059                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'file' => $file, 'enc' => $enc, 'desc' => $desc));
3060                         } elseif ($type == 'TrueTypeUnicode') {
3061                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg));
3062                         } else {
3063                                 $this->Error('Unknow font type: '.$type.'');
3064                         }
3065                         if (isset($diff) AND (!empty($diff))) {
3066                                 //Search existing encodings
3067                                 $d = 0;
3068                                 $nb = count($this->diffs);
3069                                 for ($i=1; $i <= $nb; ++$i) {
3070                                         if ($this->diffs[$i] == $diff) {
3071                                                 $d = $i;
3072                                                 break;
3073                                         }
3074                                 }
3075                                 if ($d == 0) {
3076                                         $d = $nb + 1;
3077                                         $this->diffs[$d] = $diff;
3078                                 }
3079                                 $this->setFontSubBuffer($fontkey, 'diff', $d);
3080                         }
3081                         if (!$this->empty_string($file)) {
3082                                 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
3083                                         $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
3084                                 } elseif ($type != 'core') {
3085                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
3086                                 }
3087                         }
3088                         return $fontdata;
3089                 }
3090
3091                 /**
3092                 * Sets the font used to print character strings. 
3093                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
3094                 * The method can be called before the first page is created and the font is retained from page to page. 
3095                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
3096                 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
3097                 * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
3098                 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
3099                 * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
3100                 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
3101                 * @access public
3102                 * @since 1.0
3103                 * @see AddFont(), SetFontSize()
3104                 */
3105                 public function SetFont($family, $style='', $size=0, $fontfile='') {
3106                         //Select a font; size given in points
3107                         if ($size == 0) {
3108                                 $size = $this->FontSizePt;
3109                         }
3110                         // try to add font (if not already added)
3111                         $fontdata = $this->AddFont($family, $style, $fontfile);
3112                         $this->FontFamily = $fontdata['family'];
3113                         $this->FontStyle = $fontdata['style'];
3114                         $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
3115                         $this->SetFontSize($size);
3116                 }
3117
3118                 /**
3119                 * Defines the size of the current font.
3120                 * @param float $size The size (in points)
3121                 * @access public
3122                 * @since 1.0
3123                 * @see SetFont()
3124                 */
3125                 public function SetFontSize($size) {
3126                         //Set font size in points
3127                         $this->FontSizePt = $size;
3128                         $this->FontSize = $size / $this->k;
3129                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
3130                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
3131                         } else {
3132                                 $this->FontAscent = 0.8 * $this->FontSize;
3133                         }
3134                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
3135                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
3136                         } else {
3137                                 $this->FontDescent = 0.2 * $this->FontSize;
3138                         }
3139                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
3140                                 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
3141                         }
3142                 }
3143
3144                 /**
3145                 * Defines the default monospaced font.
3146                 * @param string $font Font name.
3147                 * @access public
3148                 * @since 4.5.025
3149                 */
3150                 public function SetDefaultMonospacedFont($font) {
3151                         $this->default_monospaced_font = $font;
3152                 }
3153                 
3154                 /**
3155                 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
3156                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
3157                 * @access public
3158                 * @since 1.5
3159                 * @see Cell(), Write(), Image(), Link(), SetLink()
3160                 */
3161                 public function AddLink() {
3162                         //Create a new internal link
3163                         $n = count($this->links) + 1;
3164                         $this->links[$n] = array(0, 0);
3165                         return $n;
3166                 }
3167
3168                 /**
3169                 * Defines the page and position a link points to.
3170                 * @param int $link The link identifier returned by AddLink()
3171                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
3172                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
3173                 * @access public
3174                 * @since 1.5
3175                 * @see AddLink()
3176                 */
3177                 public function SetLink($link, $y=0, $page=-1) {
3178                         if ($y == -1) {
3179                                 $y = $this->y;
3180                         }
3181                         if ($page == -1) {
3182                                 $page = $this->page;
3183                         }
3184                         $this->links[$link] = array($page, $y);
3185                 }
3186
3187                 /**
3188                 * Puts a link on a rectangular area of the page.
3189                 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
3190                 * @param float $x Abscissa of the upper-left corner of the rectangle
3191                 * @param float $y Ordinate of the upper-left corner of the rectangle
3192                 * @param float $w Width of the rectangle
3193                 * @param float $h Height of the rectangle
3194                 * @param mixed $link URL or identifier returned by AddLink()
3195                 * @param int $spaces number of spaces on the text to link
3196                 * @access public
3197                 * @since 1.5
3198                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
3199                 */
3200                 public function Link($x, $y, $w, $h, $link, $spaces=0) {
3201                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
3202                 }
3203                 
3204                 /**
3205                 * Puts a markup annotation on a rectangular area of the page.
3206                 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
3207                 * @param float $x Abscissa of the upper-left corner of the rectangle
3208                 * @param float $y Ordinate of the upper-left corner of the rectangle
3209                 * @param float $w Width of the rectangle
3210                 * @param float $h Height of the rectangle
3211                 * @param string $text annotation text or alternate content
3212                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
3213                 * @param int $spaces number of spaces on the text to link
3214                 * @access public
3215                 * @since 4.0.018 (2008-08-06)
3216                 */
3217                 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
3218                         // recalculate coordinates to account for graphic transformations
3219                         if (isset($this->transfmatrix)) {
3220                                 $maxid = count($this->transfmatrix) - 1;
3221                                 for ($i=$maxid; $i >= 0; $i--) {
3222                                         $ctm = $this->transfmatrix[$i];
3223                                         if (isset($ctm['a'])) {
3224                                                 $x = $x * $this->k;
3225                                                 $y = ($this->h - $y) * $this->k;
3226                                                 $w = $w * $this->k;
3227                                                 $h = $h * $this->k;
3228                                                 // top left
3229                                                 $xt = $x;
3230                                                 $yt = $y;
3231                                                 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3232                                                 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3233                                                 // top right
3234                                                 $xt = $x + $w;
3235                                                 $yt = $y;
3236                                                 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3237                                                 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3238                                                 // bottom left
3239                                                 $xt = $x;
3240                                                 $yt = $y - $h;
3241                                                 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3242                                                 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3243                                                 // bottom right
3244                                                 $xt = $x + $w;
3245                                                 $yt = $y - $h;
3246                                                 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3247                                                 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3248                                                 // new coordinates (rectangle area)
3249                                                 $x = min($x1, $x2, $x3, $x4);
3250                                                 $y = max($y1, $y2, $y3, $y4);
3251                                                 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
3252                                                 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
3253                                                 $x = $x / $this->k;
3254                                                 $y = $this->h - ($y / $this->k);
3255                                         }
3256                                 }
3257                         }
3258                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
3259                         if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
3260                                 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + 100000));
3261                         }
3262                 }
3263
3264                 /**
3265                 * Embedd the attached files.
3266                 * @since 4.4.000 (2008-12-07)
3267                 * @access protected
3268                 * @see Annotation()
3269                 */
3270                 protected function _putEmbeddedFiles() {
3271                         reset($this->embeddedfiles);
3272                         foreach ($this->embeddedfiles as $filename => $filedata) {
3273                                 $data = file_get_contents($filedata['file']);
3274                                 $filter = '';
3275                                 if ($this->compress) {
3276                                         $data = gzcompress($data);
3277                                         $filter = ' /Filter /FlateDecode';
3278                                 }
3279                                 $this->offsets[$filedata['n']] = $this->bufferlen;
3280                                 $this->_out($filedata['n'].' 0 obj');
3281                                 $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
3282                                 $this->_putstream($data);
3283                                 $this->_out('endobj');
3284                         }
3285                 }
3286                 
3287                 /**
3288                 * Prints a character string.
3289                 * The origin is on the left of the first charcter, on the baseline.
3290                 * This method allows to place a string precisely on the page.
3291                 * @param float $x Abscissa of the origin
3292                 * @param float $y Ordinate of the origin
3293                 * @param string $txt String to print
3294                 * @param int $stroke outline size in points (0 = disable)
3295                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
3296                 * @access public
3297                 * @since 1.0
3298                 * @deprecated deprecated since version 4.3.005 (2008-11-25)
3299                 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
3300                 */
3301                 public function Text($x, $y, $txt, $stroke=0, $clip=false) {
3302                         //Output a string
3303                         if ($this->rtl) {
3304                                 // bidirectional algorithm (some chars may be changed affecting the line length)
3305                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
3306                                 $l = $this->GetArrStringWidth($s);
3307                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
3308                         } else {
3309                                 $xr = $x;
3310                         }
3311                         $opt = '';
3312                         if (($stroke > 0) AND (!$clip)) {
3313                                 $opt .= '1 Tr '.intval($stroke).' w ';
3314                         } elseif (($stroke > 0) AND $clip) {
3315                                 $opt .= '5 Tr '.intval($stroke).' w ';
3316                         } elseif ($clip) {
3317                                 $opt .= '7 Tr ';
3318                         }
3319                         $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
3320                         if ($this->underline AND ($txt!='')) {
3321                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
3322                         }
3323                         if ($this->linethrough AND ($txt!='')) { 
3324                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 
3325                         }
3326                         if ($this->ColorFlag AND (!$clip)) {
3327                                 $s='q '.$this->TextColor.' '.$s.' Q';
3328                         }
3329                         $this->_out($s);
3330                 }
3331
3332                 /**
3333                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 
3334                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
3335                 * This method is called automatically and should not be called directly by the application.
3336                 * @return boolean
3337                 * @access public
3338                 * @since 1.4
3339                 * @see SetAutoPageBreak()
3340                 */
3341                 public function AcceptPageBreak() {
3342                         return $this->AutoPageBreak;
3343                 }
3344                 
3345                 /**
3346                 * Add page if needed.
3347                 * @param float $h Cell height. Default value: 0.
3348                 * @param mixed $y starting y position, leave empty for current position.
3349                 * @return boolean true in case of page break, false otherwise.
3350                 * @since 3.2.000 (2008-07-01)
3351                 * @access protected
3352                 */
3353                 protected function checkPageBreak($h=0, $y='') {
3354                         if ($this->empty_string($y)) {
3355                                 $y = $this->y;
3356                         }
3357                         if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
3358                                 //Automatic page break
3359                                 $x = $this->x;
3360                                 $this->AddPage($this->CurOrientation);
3361                                 $this->y = $this->tMargin;
3362                                 $oldpage = $this->page - 1;
3363                                 if ($this->rtl) {
3364                                         if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
3365                                                 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
3366                                         } else {
3367                                                 $this->x = $x;
3368                                         }
3369                                 } else {
3370                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3371                                                 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
3372                                         } else {
3373                                                 $this->x = $x;
3374                                         }
3375                                 }
3376                                 return true;
3377                         }
3378                         return false;
3379                 }
3380
3381                 /**
3382                 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
3383                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3384                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
3385                 * @param float $h Cell height. Default value: 0.
3386                 * @param string $txt String to print. Default value: empty string.
3387                 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3388                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
3389                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
3390                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3391                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3392                 * @param mixed $link URL or identifier returned by AddLink().
3393                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3394                 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3395                 * @access public
3396                 * @since 1.0
3397                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
3398                 */
3399                 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3400                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
3401                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3402                         if ($h < $min_cell_height) {
3403                                 $h = $min_cell_height;
3404                         }
3405                         $this->checkPageBreak($h);
3406                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
3407                 }
3408
3409                 /**
3410                 * Removes SHY characters from text.
3411                 * @param string $txt input string
3412                 * @return string without SHY characters.
3413                 * @access public
3414                 * @since (4.5.019) 2009-02-28
3415                 */
3416                 public function removeSHY($txt='') {
3417                         /*
3418                         * Unicode Data
3419                         * Name : SOFT HYPHEN, commonly abbreviated as SHY
3420                         * HTML Entity (decimal): &#173;
3421                         * HTML Entity (hex): &#xad;
3422                         * HTML Entity (named): &shy;
3423                         * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
3424                         * UTF-8 (hex): 0xC2 0xAD (c2ad)
3425                         * UTF-8 character: chr(194).chr(173)
3426                         */
3427                         $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
3428                         if (!$this->isunicode) {
3429                                 $txt = preg_replace('/([\\xad]{1})/', '', $txt);
3430                         }
3431                         return $txt;
3432                 }
3433                 
3434                 /**
3435                 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
3436                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3437                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
3438                 * @param float $h Cell height. Default value: 0.
3439                 * @param string $txt String to print. Default value: empty string.
3440                 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3441                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
3442                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3443                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3444                 * @param mixed $link URL or identifier returned by AddLink().
3445                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3446                 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3447                 * @access protected
3448                 * @since 1.0
3449                 * @see Cell()
3450                 */
3451                 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3452                         $txt = $this->removeSHY($txt);
3453                         $rs = ''; //string to be returned
3454                         if (!$ignore_min_height) {
3455                                 $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3456                                 if ($h < $min_cell_height) {
3457                                         $h = $min_cell_height;
3458                                 }
3459                         }
3460                         $k = $this->k;
3461                         if ($this->empty_string($w) OR ($w <= 0)) {
3462                                 if ($this->rtl) {
3463                                         $w = $this->x - $this->lMargin;
3464                                 } else {
3465                                         $w = $this->w - $this->rMargin - $this->x;
3466                                 }
3467                         }
3468                         $s = '';                        
3469                         if (($fill == 1) OR ($border == 1)) {
3470                                 if ($fill == 1) {
3471                                         $op = ($border == 1) ? 'B' : 'f';
3472                                 } else {
3473                                         $op = 'S';
3474                                 }
3475                                 if ($this->rtl) {
3476                                         $xk = (($this->x  - $w) * $k);
3477                                 } else {
3478                                         $xk = ($this->x * $k);
3479                                 }
3480                                 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
3481                         }
3482                         if (is_string($border)) {
3483                                 $lm = ($this->LineWidth / 2);
3484                                 $x = $this->x;
3485                                 $y = $this->y;
3486                                 if (strpos($border,'L') !== false) {
3487                                         if ($this->rtl) {
3488                                                 $xk = ($x - $w) * $k;
3489                                         } else {
3490                                                 $xk = $x * $k;
3491                                         }
3492                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
3493                                 }
3494                                 if (strpos($border,'T') !== false) {
3495                                         if ($this->rtl) {
3496                                                 $xk = ($x - $w + $lm) * $k;
3497                                                 $xwk = ($x - $lm) * $k;
3498                                         } else {
3499                                                 $xk = ($x - $lm) * $k;
3500                                                 $xwk = ($x + $w + $lm) * $k;
3501                                         }
3502                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
3503                                 }
3504                                 if (strpos($border,'R') !== false) {
3505                                         if ($this->rtl) {
3506                                                 $xk = $x * $k;
3507                                         } else {
3508                                                 $xk = ($x + $w) * $k;
3509                                         }
3510                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
3511                                 }
3512                                 if (strpos($border,'B') !== false) {
3513                                         if ($this->rtl) {
3514                                                 $xk = ($x - $w + $lm) * $k;
3515                                                 $xwk = ($x - $lm) * $k;
3516                                         } else {
3517                                                 $xk = ($x - $lm) * $k;
3518                                                 $xwk = ($x + $w + $lm) * $k;
3519                                         }
3520                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
3521                                 }
3522                         }
3523                         if ($txt != '') {
3524                                 // text lenght
3525                                 $width = $this->GetStringWidth($txt);
3526                                 // ratio between cell lenght and text lenght
3527                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
3528                                 
3529                                 // stretch text if required
3530                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
3531                                         if ($stretch > 2) {
3532                                                 // spacing
3533                                                 //Calculate character spacing in points
3534                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
3535                                                 //Set character spacing
3536                                                 $rs .= sprintf('BT %.2F Tc ET ', $char_space);
3537                                         } else {
3538                                                 // scaling
3539                                                 //Calculate horizontal scaling
3540                                                 $horiz_scale = $ratio * 100.0;
3541                                                 //Set horizontal scaling
3542                                                 $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
3543                                         }
3544                                         $align = '';
3545                                         $width = $w - (2 * $this->cMargin);
3546                                 } else {
3547                                         $stretch == 0;
3548                                 }
3549                                 if ($align == 'L') {
3550                                         if ($this->rtl) {
3551                                                 $dx = $w - $width - $this->cMargin;
3552                                         } else {
3553                                                 $dx = $this->cMargin;
3554                                         }
3555                                 } elseif ($align == 'R') {
3556                                         if ($this->rtl) {
3557                                                 $dx = $this->cMargin;
3558                                         } else {
3559                                                 $dx = $w - $width - $this->cMargin;
3560                                         }
3561                                 } elseif ($align == 'C') {
3562                                         $dx = ($w - $width) / 2;
3563                                 } elseif ($align == 'J') {
3564                                         if ($this->rtl) {
3565                                                 $dx = $w - $width - $this->cMargin;
3566                                         } else {
3567                                                 $dx = $this->cMargin;
3568                                         }
3569                                 } else {
3570                                         $dx = $this->cMargin;
3571                                 }
3572                                 if ($this->ColorFlag) {
3573                                         $s .= 'q '.$this->TextColor.' ';
3574                                 }
3575                                 $txt2 = $this->_escapetext($txt);
3576                                 if ($this->rtl) {
3577                                         $xdk = ($this->x - $dx - $width) * $k;
3578                                 } else {
3579                                         $xdk = ($this->x + $dx) * $k;
3580                                 }
3581                                 // Justification
3582                                 if ($align == 'J') {
3583                                         // count number of spaces
3584                                         $ns = substr_count($txt, ' ');
3585                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
3586                                                 // get string width without spaces
3587                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3588                                                 // calculate average space width
3589                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3590                                                 // set word position to be used with TJ operator
3591                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3592                                         } else {
3593                                                 // get string width
3594                                                 $width = $this->GetStringWidth($txt);
3595                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3596                                                 $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
3597                                         }
3598                                 }
3599                                 // calculate approximate position of the font base line
3600                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3601                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3602                                 // print text
3603                                 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3604                                 if ($this->rtl) {
3605                                         $xdx = $this->x - $dx - $width;
3606                                 } else {
3607                                         $xdx = $this->x + $dx;
3608                                 }
3609                                 if ($this->underline)  {
3610                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3611                                 }
3612                                 if ($this->linethrough) { 
3613                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3614                                 }
3615                                 if ($this->ColorFlag) {
3616                                         $s .= ' Q';
3617                                 }
3618                                 if ($link) {
3619                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
3620                                 }
3621                         }
3622                         // output cell
3623                         if ($s) {
3624                                 // output cell
3625                                 $rs .= $s;
3626                                 // reset text stretching
3627                                 if ($stretch > 2) {
3628                                         //Reset character horizontal spacing
3629                                         $rs .= ' BT 0 Tc ET';
3630                                 } elseif ($stretch > 0) {
3631                                         //Reset character horizontal scaling
3632                                         $rs .= ' BT 100 Tz ET';
3633                                 }
3634                         }
3635                         // reset word spacing
3636                         if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
3637                                 $rs .= ' BT 0 Tw ET';
3638                         }
3639                         $this->lasth = $h;
3640                         if ($ln > 0) {
3641                                 //Go to the beginning of the next line
3642                                 $this->y += $h;
3643                                 if ($ln == 1) {
3644                                         if ($this->rtl) {
3645                                                 $this->x = $this->w - $this->rMargin;
3646                                         } else {
3647                                                 $this->x = $this->lMargin;
3648                                         }
3649                                 }
3650                         } else {
3651                                 // go left or right by case
3652                                 if ($this->rtl) {
3653                                         $this->x -= $w;
3654                                 } else {
3655                                         $this->x += $w;
3656                                 }
3657                         }
3658                         $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
3659                         $rs = $gstyles.$rs;
3660                         return $rs;
3661                 }
3662
3663                 /**
3664                 * This method allows printing text with line breaks. 
3665                 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
3666                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3667                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3668                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3669                 * @param string $txt String to print
3670                 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3671                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
3672                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3673                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
3674                 * @param int $x x position in user units
3675                 * @param int $y y position in user units
3676                 * @param boolean $reseth if true reset the last cell height (default true).
3677                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3678                 * @param boolean $ishtml set to true if $txt is HTML content (default = false).
3679                 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
3680                 * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
3681                 * @return int Return the number of cells or 1 for html mode.
3682                 * @access public
3683                 * @since 1.3
3684                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3685                 */
3686                 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $addpage=true) {
3687                         if ($this->empty_string($this->lasth) OR $reseth) {
3688                                 //set row height
3689                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3690                         }
3691                         if (!$this->empty_string($y)) {
3692                                 $this->SetY($y);
3693                         } else {
3694                                 $y = $this->GetY();
3695                         }
3696                         // should not add page for each cell
3697             if ($addpage) {
3698                             // check for page break
3699                 $this->checkPageBreak($h);
3700             }
3701                         $y = $this->GetY();
3702                         // get current page number
3703                         $startpage = $this->page;
3704                         if (!$this->empty_string($x)) {
3705                                 $this->SetX($x);
3706                         } else {
3707                                 $x = $this->GetX();
3708                         }
3709                         if ($this->empty_string($w) OR ($w <= 0)) {
3710                                 if ($this->rtl) {
3711                                         $w = $this->x - $this->lMargin;
3712                                 } else {
3713                                         $w = $this->w - $this->rMargin - $this->x;
3714                                 }
3715                         }
3716                         // store original margin values
3717                         $lMargin = $this->lMargin;
3718                         $rMargin = $this->rMargin;
3719                         if ($this->rtl) {
3720                                 $this->SetRightMargin($this->w - $this->x);
3721                                 $this->SetLeftMargin($this->x - $w);
3722                         } else {
3723                                 $this->SetLeftMargin($this->x);
3724                                 $this->SetRightMargin($this->w - $this->x - $w);
3725                         }
3726                         $starty = $this->y;
3727                         if ($autopadding) {
3728                                 // Adjust internal padding
3729                                 if ($this->cMargin < ($this->LineWidth / 2)) {
3730                                         $this->cMargin = ($this->LineWidth / 2);
3731                                 }
3732                                 // Add top space if needed
3733                                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3734                                         $this->y += $this->LineWidth / 2;
3735                                 }
3736                                 // add top padding
3737                                 $this->y += $this->cMargin;
3738                         }
3739                         if ($ishtml) {
3740                                 // ******* Write HTML text
3741                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3742                                 $nl = 1;
3743                         } else {
3744                                 // ******* Write text
3745                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
3746                         }
3747                         if ($autopadding) {
3748                                 // add bottom padding
3749                                 $this->y += $this->cMargin;
3750                                 // Add bottom space if needed
3751                                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3752                                         $this->y += $this->LineWidth / 2;
3753                                 }
3754                         }
3755                         // Get end-of-text Y position
3756                         $currentY = $this->y;
3757                         // get latest page number
3758                         $endpage = $this->page;
3759                         // check if a new page has been created
3760                         if ($endpage > $startpage) {
3761                                 // design borders around HTML cells.
3762                                 for ($page=$startpage; $page <= $endpage; ++$page) {
3763                                         $this->setPage($page);
3764                                         if ($page == $startpage) {
3765                                                 $this->y = $starty; // put cursor at the beginning of cell on the first page
3766                                                 $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
3767                                                 $cborder = $this->getBorderMode($border, $position='start');
3768                                         } elseif ($page == $endpage) {
3769                                                 $this->y = $this->tMargin; // put cursor at the beginning of last page
3770                                                 $h = $currentY - $this->tMargin;
3771                                                 $cborder = $this->getBorderMode($border, $position='end');
3772                                         } else {
3773                                                 $this->y = $this->tMargin; // put cursor at the beginning of the current page
3774                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3775                                                 $cborder = $this->getBorderMode($border, $position='middle');
3776                                         }
3777                                         $nx = $x;
3778                                         // account for margin changes
3779                                         if ($page > $startpage) {
3780                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
3781                                                         $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
3782                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
3783                                                         $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
3784                                                 }
3785                                         }
3786                                         $this->SetX($nx);
3787                                         $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
3788                                         if ($cborder OR $fill) {
3789                                                 $pagebuff = $this->getPageBuffer($this->page);
3790                                                 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
3791                                                 $pend = substr($pagebuff, $this->intmrk[$this->page]);
3792                                                 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3793                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3794                                         }
3795                                 }
3796                         } else {
3797                                 $h = max($h, ($currentY - $y));
3798                                 // put cursor at the beginning of text
3799                                 $this->SetY($y); 
3800                                 $this->SetX($x);
3801                                 // design a cell around the text
3802                                 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
3803                                 if ($border OR $fill) {
3804                                         if (end($this->transfmrk[$this->page]) !== false) {
3805                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
3806                                                 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
3807                                         } elseif ($this->InFooter) {
3808                                                 $pagemark = &$this->footerpos[$this->page];
3809                                         } else {
3810                                                 $pagemark = &$this->intmrk[$this->page];
3811                                         }
3812                                         $pagebuff = $this->getPageBuffer($this->page);
3813                                         $pstart = substr($pagebuff, 0, $pagemark);
3814                                         $pend = substr($pagebuff, $pagemark);
3815                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3816                                         $pagemark += strlen($ccode."\n");
3817                                 }
3818                         }
3819                         // Get end-of-cell Y position
3820                         $currentY = $this->GetY();
3821                         // restore original margin values
3822                         $this->SetLeftMargin($lMargin);
3823                         $this->SetRightMargin($rMargin);
3824                         if ($ln > 0) {
3825                                 //Go to the beginning of the next line
3826                                 $this->SetY($currentY);
3827                                 if ($ln == 2) {
3828                                         $this->SetX($x + $w);
3829                                 }
3830                         } else {
3831                                 // go left or right by case
3832                                 $this->setPage($startpage);
3833                                 $this->y = $y;
3834                                 $this->SetX($x + $w);
3835                         }
3836                         return $nl;
3837                 }
3838
3839                 /**
3840                 * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
3841                 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3842                 * @param string multicell position: 'start', 'middle', 'end'
3843                 * @return border mode
3844                 * @access protected
3845                 * @since 4.4.002 (2008-12-09)
3846                 */
3847                 protected function getBorderMode($border, $position='start') {
3848                         if ((!$this->opencell) AND ($border == 1)) {
3849                                 return 1;
3850                         }
3851                         $cborder = '';
3852                         switch ($position) {
3853                                 case 'start': {
3854                                         if ($border == 1) {
3855                                                 $cborder = 'LTR';
3856                                         } else {
3857                                                 if (!(false === strpos($border, 'L'))) {
3858                                                         $cborder .= 'L';
3859                                                 }
3860                                                 if (!(false === strpos($border, 'T'))) {
3861                                                         $cborder .= 'T';
3862                                                 }
3863                                                 if (!(false === strpos($border, 'R'))) {
3864                                                         $cborder .= 'R';
3865                                                 }
3866                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3867                                                         $cborder .= 'B';
3868                                                 }
3869                                         }
3870                                         break;
3871                                 }
3872                                 case 'middle': {
3873                                         if ($border == 1) {
3874                                                 $cborder = 'LR';
3875                                         } else {
3876                                                 if (!(false === strpos($border, 'L'))) {
3877                                                         $cborder .= 'L';
3878                                                 }
3879                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3880                                                         $cborder .= 'T';
3881                                                 }
3882                                                 if (!(false === strpos($border, 'R'))) {
3883                                                         $cborder .= 'R';
3884                                                 }
3885                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3886                                                         $cborder .= 'B';
3887                                                 }
3888                                         }
3889                                         break;
3890                                 }
3891                                 case 'end': {
3892                                         if ($border == 1) {
3893                                                 $cborder = 'LRB';
3894                                         } else {
3895                                                 if (!(false === strpos($border, 'L'))) {
3896                                                         $cborder .= 'L';
3897                                                 }
3898                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3899                                                         $cborder .= 'T';
3900                                                 }
3901                                                 if (!(false === strpos($border, 'R'))) {
3902                                                         $cborder .= 'R';
3903                                                 }
3904                                                 if (!(false === strpos($border, 'B'))) {
3905                                                         $cborder .= 'B';
3906                                                 }
3907                                         }
3908                                         break;
3909                                 }
3910                                 default: {
3911                                         $cborder = $border;
3912                                         break;
3913                                 }
3914                         }
3915                         return $cborder;
3916                 }
3917
3918                 /**
3919                 * This method returns the estimated number of lines required to print the text.
3920                 * @param string $txt text to print
3921                 * @param float $w width of cell. If 0, they extend up to the right margin of the page.
3922                 * @return int Return the estimated number of lines.
3923                 * @access public
3924                 * @since 4.5.011
3925                 */
3926                 public function getNumLines($txt, $w=0) {
3927                         $lines = 0;
3928                         if ($this->empty_string($w) OR ($w <= 0)) {
3929                                 if ($this->rtl) {
3930                                         $w = $this->x - $this->lMargin;
3931                                 } else {
3932                                         $w = $this->w - $this->rMargin - $this->x;
3933                                 }
3934                         }
3935                         // max column width
3936                         $wmax = $w - (2 * $this->cMargin);
3937                         // remove carriage returns
3938                         $txt = str_replace("\r", '', $txt);
3939                         // remove last newline (if any)
3940                         if (substr($txt,-1) == "\n") {
3941                                 $txt = substr($txt, 0, -1);
3942                         }
3943                         // divide text in blocks
3944                         $txtblocks = explode("\n", $txt);
3945                         // for each block;
3946                         foreach ($txtblocks as $block) {
3947                                 // estimate the number of lines
3948                                 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
3949                         }
3950                         return $lines;
3951                 }
3952                         
3953                 /**
3954                 * This method prints text from the current position.<br />
3955                 * @param float $h Line height
3956                 * @param string $txt String to print
3957                 * @param mixed $link URL or identifier returned by AddLink()
3958                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3959                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3960                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3961                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3962                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3963                 * @param boolean $firstblock if true the string is the starting of a line.
3964                 * @param float $maxh maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
3965                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3966                 * @access public
3967                 * @since 1.5
3968                 */
3969                 public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
3970                         if (strlen($txt) == 0) {
3971                                 $txt = ' ';
3972                         }
3973                         // remove carriage returns
3974                         $s = str_replace("\r", '', $txt);
3975                         // check if string contains arabic text
3976                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3977                                 $arabic = true;
3978                         } else {
3979                                 $arabic = false;
3980                         }
3981                         // check if string contains RTL text
3982                         if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
3983                                 $rtlmode = true;
3984                         } else {
3985                                 $rtlmode = false;
3986                         }
3987                         // get a char width
3988                         $chrwidth = $this->GetCharWidth('.');
3989                         // get array of unicode values
3990                         $chars = $this->UTF8StringToArray($s);
3991                         // get array of chars
3992                         $uchars = $this->UTF8ArrayToUniArray($chars);
3993                         // get the number of characters
3994                         $nb = count($chars);
3995                         // replacement for SHY character (minus symbol)
3996                         $shy_replacement = 45;
3997                         $shy_replacement_char = $this->unichr($shy_replacement);
3998                         // widht for SHY replacement
3999                         $shy_replacement_width = $this->GetCharWidth($shy_replacement);
4000                         // store current position
4001                         $prevx = $this->x;
4002                         $prevy = $this->y;
4003                         // max Y
4004                         $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
4005                         // calculate remaining line width ($w)
4006                         if ($this->rtl) {
4007                                 $w = $this->x - $this->lMargin;
4008                         } else {
4009                                 $w = $this->w - $this->rMargin - $this->x;
4010                         }
4011                         // max column width
4012                         $wmax = $w - (2 * $this->cMargin);
4013                         if ($chrwidth > $wmax) {
4014                                 // a single character do not fit on column
4015                                 return '';
4016                         }
4017                         $i = 0; // character position
4018                         $j = 0; // current starting position
4019                         $sep = -1; // position of the last blank space
4020                         $shy = false; // true if the last blank is a soft hypen (SHY)
4021                         $l = 0; // current string lenght
4022                         $nl = 0; //number of lines
4023                         $linebreak = false;
4024                         // for each character
4025                         while ($i < $nb) {
4026                                 if (($maxh > 0) AND ($this->y >= $maxy) ) {
4027                                         $firstline = true;
4028                                 }
4029                                 //Get the current character
4030                                 $c = $chars[$i];
4031                                 if ($c == 10) { // 10 = "\n" = new line
4032                                         //Explicit line break
4033                                         if ($align == 'J') {
4034                                                 if ($this->rtl) {
4035                                                         $talign = 'R';
4036                                                 } else {
4037                                                         $talign = 'L';
4038                                                 }
4039                                         } else {
4040                                                 $talign = $align;
4041                                         }
4042                                         $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4043                                         if ($firstline) {
4044                                                 $startx = $this->x;
4045                                                 $tmparr = array_slice($chars, $j, $i);
4046                                                 if ($rtlmode) {
4047                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4048                                                 }
4049                                                 $linew = $this->GetArrStringWidth($tmparr);
4050                                                 unset($tmparr);
4051                                                 if ($this->rtl) {
4052                                                         $this->endlinex = $startx - $linew;
4053                                                 } else {
4054                                                         $this->endlinex = $startx + $linew;
4055                                                 }
4056                                                 $w = $linew;
4057                                                 $tmpcmargin = $this->cMargin;
4058                                                 if ($maxh == 0) {
4059                                                         $this->cMargin = 0;
4060                                                 }
4061                                         }
4062                                         $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
4063                                         unset($tmpstr);
4064                                         if ($firstline) {
4065                                                 $this->cMargin = $tmpcmargin;
4066                                                 return ($this->UniArrSubString($uchars, $i));
4067                                         }
4068                                         ++$nl;
4069                                         $j = $i + 1;
4070                                         $l = 0;
4071                                         $sep = -1;
4072                                         $shy = false;
4073                                         // account for margin changes
4074                                         if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4075                                                 // AcceptPageBreak() may be overriden on extended classed to include margin changes
4076                                                 $this->AcceptPageBreak();
4077                                         }
4078                                         $w = $this->getRemainingWidth();
4079                                         $wmax = $w - (2 * $this->cMargin);
4080                                 } else {
4081                                         // 160 is the non-breaking space.
4082                                         // 173 is SHY (Soft Hypen).
4083                                         // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
4084                                         // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
4085                                         // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
4086                                         if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
4087                                                 // update last blank space position
4088                                                 $sep = $i;
4089                                                 // check if is a SHY
4090                                                 if ($c == 173) {
4091                                                         $shy = true;
4092                                                 } else {
4093                                                         $shy = false;
4094                                                 }
4095                                         }
4096                                         // update string length
4097                                         if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
4098                                                 // with bidirectional algorithm some chars may be changed affecting the line length
4099                                                 // *** very slow ***
4100                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
4101                                         } else {
4102                                                 $l += $this->GetCharWidth($c);
4103                                         }
4104                                         if (!$stretch && (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax))) ) {
4105                                                 // we have reached the end of column
4106                                                 if ($sep == -1) {
4107                                                         // check if the line was already started
4108                                                         if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
4109                                                                 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
4110                                                                 // print a void cell and go to next line
4111                                                                 $this->Cell($w, $h, '', 0, 1);
4112                                                                 $linebreak = true;
4113                                                                 if ($firstline) {
4114                                                                         return ($this->UniArrSubString($uchars, $j));
4115                                                                 }
4116                                                         } else {
4117                                                                 // truncate the word because do not fit on column
4118                                                                 $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4119                                                                 if ($firstline) {
4120                                                                         $startx = $this->x;
4121                                                                         $tmparr = array_slice($chars, $j, $i);
4122                                                                         if ($rtlmode) {
4123                                                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4124                                                                         }
4125                                                                         $linew = $this->GetArrStringWidth($tmparr);
4126                                                                         unset($tmparr);
4127                                                                         if ($this->rtl) {
4128                                                                                 $this->endlinex = $startx - $linew;
4129                                                                         } else {
4130                                                                                 $this->endlinex = $startx + $linew;
4131                                                                         }
4132                                                                         $w = $linew;
4133                                                                         $tmpcmargin = $this->cMargin;
4134                                                                         if ($maxh == 0) {
4135                                                                                 $this->cMargin = 0;
4136                                                                         }
4137                                                                 }
4138                                                                 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
4139                                                                 unset($tmpstr);
4140                                                                 if ($firstline) {
4141                                                                         $this->cMargin = $tmpcmargin;
4142                                                                         return ($this->UniArrSubString($uchars, $i));
4143                                                                 }
4144                                                                 $j = $i;
4145                                                                 --$i;
4146                                                         }       
4147                                                 } else {
4148                                                         // word wrapping
4149                                                         if ($this->rtl AND (!$firstblock)) {
4150                                                                 $endspace = 1;
4151                                                         } else {
4152                                                                 $endspace = 0;
4153                                                         }
4154                                                         if ($shy) {
4155                                                                 // add hypen (minus symbol) at the end of the line
4156                                                                 $shy_width = $shy_replacement_width;
4157                                                                 if ($this->rtl) {
4158                                                                         $shy_char_left = $shy_replacement_char;
4159                                                                         $shy_char_right = '';
4160                                                                 } else {
4161                                                                         $shy_char_left = '';
4162                                                                         $shy_char_right = $shy_replacement_char;
4163                                                                 }
4164                                                         } else {
4165                                                                 $shy_width = 0;
4166                                                                 $shy_char_left = '';
4167                                                                 $shy_char_right = '';
4168                                                         }
4169                                                         $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
4170                                                         if ($firstline) {
4171                                                                 $startx = $this->x;
4172                                                                 $tmparr = array_slice($chars, $j, ($sep + $endspace));
4173                                                                 if ($rtlmode) {
4174                                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4175                                                                 }
4176                                                                 $linew = $this->GetArrStringWidth($tmparr);
4177                                                                 unset($tmparr);
4178                                                                 if ($this->rtl) {
4179                                                                         $this->endlinex = $startx - $linew - $shy_width;
4180                                                                 } else {
4181                                                                         $this->endlinex = $startx + $linew + $shy_width;
4182                                                                 }
4183                                                                 $w = $linew;
4184                                                                 $tmpcmargin = $this->cMargin;
4185                                                                 if ($maxh == 0) {
4186                                                                         $this->cMargin = 0;
4187                                                                 }
4188                                                         }
4189                                                         // print the line
4190                                                         $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
4191                                                         unset($tmpstr);
4192                                                         if ($firstline) {
4193                                                                 // return the remaining text
4194                                                                 $this->cMargin = $tmpcmargin;
4195                                                                 return ($this->UniArrSubString($uchars, ($sep + $endspace)));
4196                                                         }
4197                                                         $i = $sep;
4198                                                         $sep = -1;
4199                                                         $shy = false;
4200                                                         $j = ($i+1);
4201                                                 }
4202                                                 // account for margin changes
4203                                                 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4204                                                         // AcceptPageBreak() may be overriden on extended classed to include margin changes
4205                                                         $this->AcceptPageBreak();
4206                                                 }
4207                                                 $w = $this->getRemainingWidth();
4208                                                 $wmax = $w - (2 * $this->cMargin);
4209                                                 if ($linebreak) {
4210                                                         $linebreak = false;
4211                                                 } else {
4212                                                         ++$nl;
4213                                                         $l = 0;
4214                                                 }
4215                                         }
4216                                 }
4217                                 ++$i;
4218                         } // end while i < nb
4219                         // print last substring (if any)
4220                         if ($l > 0) {
4221                             if (!$stretch)
4222                             {
4223                                 switch ($align) {
4224                                         case 'J':
4225                                         case 'C': {
4226                                                 $w = $w;
4227                                                 break;
4228                                         }
4229                                         case 'L': {
4230                                                 if ($this->rtl) {
4231                                                         $w = $w;
4232                                                 } else {
4233                                                         $w = $l;
4234                                                 }
4235                                                 break;
4236                                         }
4237                                         case 'R': {
4238                                                 if ($this->rtl) {
4239                                                         $w = $l;
4240                                                 } else {
4241                                                         $w = $w;
4242                                                 }
4243                                                 break;
4244                                         }
4245                                         default: {
4246                                                 $w = $l;
4247                                                 break;
4248                                         }
4249                                 }
4250                             }
4251                                 $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
4252                                 if ($firstline) {
4253                                         $startx = $this->x;
4254                                         $tmparr = array_slice($chars, $j, $nb);
4255                                         if ($rtlmode) {
4256                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4257                                         }
4258                                         $linew = $this->GetArrStringWidth($tmparr);
4259                                         unset($tmparr);
4260                                         if ($this->rtl) {
4261                                                 $this->endlinex = $startx - $linew;
4262                                         } else {
4263                                                 $this->endlinex = $startx + $linew;
4264                                         }
4265                                         if (!$stretch)
4266                                             $w = $linew;
4267                                         $tmpcmargin = $this->cMargin;
4268                                         if ($maxh == 0) {
4269                                                 $this->cMargin = 0;
4270                                         }
4271                                 }
4272                                 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
4273                                 unset($tmpstr);
4274                                 if ($firstline) {
4275                                         $this->cMargin = $tmpcmargin;
4276                                         return ($this->UniArrSubString($uchars, $nb));
4277                                 }
4278                                 ++$nl;
4279                         }
4280                         if ($firstline) {
4281                                 return '';
4282                         }
4283                         return $nl;
4284                 }
4285                                 
4286                 /**
4287                 * Returns the remaining width between the current position and margins.
4288                 * @return int Return the remaining width
4289                 * @access protected
4290                 */
4291                 protected function getRemainingWidth() {
4292                         if ($this->rtl) {
4293                                 return ($this->x - $this->lMargin);
4294                         } else {
4295                                 return ($this->w - $this->rMargin - $this->x);
4296                         }
4297                 }
4298
4299                 /**
4300                 * Extract a slice of the $strarr array and return it as string.
4301                 * @param string $strarr The input array of characters.
4302                 * @param int $start the starting element of $strarr.
4303                 * @param int $end first element that will not be returned.
4304                 * @return Return part of a string
4305                 * @access public
4306                 */
4307                 public function UTF8ArrSubString($strarr, $start='', $end='') {
4308                         if (strlen($start) == 0) {
4309                                 $start = 0;
4310                         }
4311                         if (strlen($end) == 0) {
4312                                 $end = count($strarr);
4313                         }
4314                         $string = '';
4315                         for ($i=$start; $i < $end; ++$i) {
4316                                 $string .= $this->unichr($strarr[$i]);
4317                         }
4318                         return $string;
4319                 }
4320
4321                 /**
4322                 * Extract a slice of the $uniarr array and return it as string.
4323                 * @param string $uniarr The input array of characters.
4324                 * @param int $start the starting element of $strarr.
4325                 * @param int $end first element that will not be returned.
4326                 * @return Return part of a string
4327                 * @access public
4328                 * @since 4.5.037 (2009-04-07)
4329                 */
4330                 public function UniArrSubString($uniarr, $start='', $end='') {
4331                         if (strlen($start) == 0) {
4332                                 $start = 0;
4333                         }
4334                         if (strlen($end) == 0) {
4335                                 $end = count($uniarr);
4336                         }
4337                         $string = '';
4338                         for ($i=$start; $i < $end; ++$i) {
4339                                 $string .= $uniarr[$i];
4340                         }
4341                         return $string;
4342                 }
4343
4344                 /**
4345                 * Convert an array of UTF8 values to array of unicode characters
4346                 * @param string $ta The input array of UTF8 values.
4347                 * @return Return array of unicode characters
4348                 * @access public
4349                 * @since 4.5.037 (2009-04-07)
4350                 */
4351                 public function UTF8ArrayToUniArray($ta) {
4352                         return array_map(array($this, 'unichr'), $ta);
4353                 }
4354                 
4355                 /**
4356                 * Returns the unicode caracter specified by UTF-8 code
4357                 * @param int $c UTF-8 code
4358                 * @return Returns the specified character.
4359                 * @author Miguel Perez, Nicola Asuni
4360                 * @access public
4361                 * @since 2.3.000 (2008-03-05)
4362                 */
4363                 public function unichr($c) {
4364                         if (!$this->isunicode) {
4365                                 return chr($c);
4366                         } elseif ($c <= 0x7F) {
4367                                 // one byte
4368                                 return chr($c);
4369                         } elseif ($c <= 0x7FF) {
4370                                 // two bytes
4371                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
4372                         } elseif ($c <= 0xFFFF) {
4373                                 // three bytes
4374                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4375                         } elseif ($c <= 0x10FFFF) {
4376                                 // four bytes
4377                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4378                         } else {
4379                                 return '';
4380                         }
4381                 }
4382                 
4383                 /**
4384                 * Puts an image in the page. 
4385                 * The upper-left corner must be given. 
4386                 * The dimensions can be specified in different ways:<ul>
4387                 * <li>explicit width and height (expressed in user unit)</li>
4388                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
4389                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
4390                 * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
4391                 * The format can be specified explicitly or inferred from the file extension.<br />
4392                 * It is possible to put a link on the image.<br />
4393                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
4394                 * @param string $file Name of the file containing the image.
4395                 * @param float $x Abscissa of the upper-left corner.
4396                 * @param float $y Ordinate of the upper-left corner.
4397                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
4398                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
4399                 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
4400                 * @param mixed $link URL or identifier returned by AddLink().
4401                 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
4402                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
4403                 * @param int $dpi dot-per-inch resolution used on resize
4404                 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
4405                 * @param boolean $ismask true if this image is a mask, false otherwise
4406                 * @param mixed $imgmask image object returned by this function or false
4407                 * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
4408                 * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box.
4409                 * @return image information
4410                 * @access public
4411                 * @since 1.1
4412         *
4413         * peter d: Upgrading tcpdf to the latest version causes the whole thing to blow up since we have modifications to the original source as well as sugarpdf which
4414         * also extend from this. The only option is to add a workaround for some of the bugs in this function.
4415                 */
4416                 public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false) {
4417                         if ($x === '') {
4418                                 $x = $this->x;
4419                         }
4420                         if ($y === '') {
4421                                 $y = $this->y;
4422                         }
4423                         // get image dimensions
4424                         $imsize = @getimagesize($file);
4425                         if ($imsize === FALSE) {
4426                                 // encode spaces on filename
4427                                 $file = str_replace(' ', '%20', $file);
4428                                 $imsize = @getimagesize($file);
4429                                 if ($imsize === FALSE) {
4430                                         $this->Error('[Image] No such file or directory in '.$file);
4431                                 }
4432                         }
4433                         // get original image width and height in pixels
4434                         list($pixw, $pixh) = $imsize;
4435                         // calculate image width and height on document
4436                         if (($w <= 0) AND ($h <= 0)) {
4437                                 // convert image size to document unit
4438                                 $w = $this->pixelsToUnits($pixw);
4439                                 $h = $this->pixelsToUnits($pixh);
4440                         } elseif ($w <= 0) {
4441                                 $w = $h * $pixw / $pixh;
4442                         } elseif ($h <= 0) {
4443                                 $h = $w * $pixh / $pixw;
4444                         } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
4445                                 // scale image dimensions proportionally to fit within the ($w, $h) box
4446                 // NOTE: This section doesn't actually work very well, use the resize = true case I added.
4447                                 if ((($w * $pixh) / ($h * $pixw)) < 1) {
4448                                         $h = $w * $pixh / $pixw;
4449                                 } else {
4450                                         $w = $h * $pixw / $pixh;
4451                                 }
4452                         } else if ($resize) { // Added resize case
4453                 // Note: The issue here is that $w and $h represent abstract sizes, we pass it in as pixels,
4454                 // but tcpdf treats it as document units.
4455                 $wratio = $pixw / $w;
4456                 $hratio = $pixh / $h;
4457
4458                 // Check if our image exceeds the boundaries of $w and $h
4459                 if ($wratio > 1 AND $hratio > 1) {
4460                     $favoredRatio = ($wratio > $hratio) ? $wratio : $hratio;
4461                 } else if ($wratio > 1) {
4462                     $favoredRatio = $wratio;
4463                 } else if ($hratio > 1) {
4464                     $favoredRatio = $hratio;
4465                 }
4466
4467                 // Calculate the new boundaries that also happen to fit the box..
4468                 // Dividing by the unit conversion $this->k seems to make the size not blow up
4469                 // later down in the code.
4470                 $w = $pixw / $favoredRatio / $this->k;
4471                 $h = $pixh / $favoredRatio / $this->k;
4472             }
4473                         // calculate new minimum dimensions in pixels
4474                         $neww = round($w * $this->k * $dpi / $this->dpi);
4475                         $newh = round($h * $this->k * $dpi / $this->dpi);
4476
4477                         // check if resize is necessary (resize is used only to reduce the image)
4478
4479             // - commmented out by pete d.
4480             // this is not a good way of checking for resize. and it might overwrite resize if the flag is enabled.
4481                         // if (($neww * $newh) >= ($pixw * $pixh)) {
4482                         //      $resize = false;
4483                         // }
4484
4485                         // check if image has been already added on document
4486                         if (!in_array($file, $this->imagekeys)) {
4487                                 //First use of image, get info
4488                                 if ($type == '') {
4489                                         $fileinfo = pathinfo($file);
4490                                         if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
4491                                                 $type = $fileinfo['extension'];
4492                                         } else {
4493                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
4494                                         }
4495                                 }
4496                                 $type = strtolower($type);
4497                                 if ($type == 'jpg') {
4498                                         $type = 'jpeg';
4499                                 }
4500                                 $mqr = get_magic_quotes_runtime();
4501                                 //set_magic_quotes_runtime(0);
4502                                 // Specific image handlers
4503                                 $mtd = '_parse'.$type;
4504                                 // GD image handler function
4505                                 $gdfunction = 'imagecreatefrom'.$type;
4506                                 $info = false;
4507                                 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
4508                                         // TCPDF image functions
4509                                         $info = $this->$mtd($file);
4510                                         if ($info == 'pngalpha') {
4511                                                 return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
4512                                         }
4513                                 } 
4514                                 if (!$info) {
4515                                         if (function_exists($gdfunction)) {
4516                                                 // GD library
4517                                                 $img = $gdfunction($file);
4518                                                 if ($resize) {
4519                                                         $imgr = imagecreatetruecolor($neww, $newh);
4520                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 
4521                                                         $info = $this->_toJPEG($imgr);
4522                                                 } else {
4523                                                         $info = $this->_toJPEG($img);
4524                                                 }
4525                                         } elseif (extension_loaded('imagick')) {
4526                                                 // ImageMagick library
4527                                                 $img = new Imagick();
4528                                                 $img->readImage($file);
4529                                                 if ($resize) {
4530                                                         $img->resizeImage($neww, $newh, 10, 1, false);
4531                                                 }
4532                                                 $img->setCompressionQuality($this->jpeg_quality);
4533                                                 $img->setImageFormat('jpeg');
4534                                                 $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4535                                                 $img->writeImage($tempname);
4536                                                 $info = $this->_parsejpeg($tempname);
4537                                                 unlink($tempname);
4538                                                 $img->destroy();
4539                                         } 
4540                                         else if ($type == 'jpeg') {
4541                                                 $info = $this->_parsejpeg($file);
4542                                         }                                       
4543                                         else {
4544                                                 return;
4545                                         }
4546                                 }
4547                                 if ($info === false) {
4548                                         //If false, we cannot process image
4549                                         return;
4550                                 }
4551                                 //set_magic_quotes_runtime($mqr);
4552                                 if ($ismask) {
4553                                         // force grayscale
4554                                         $info['cs'] = 'DeviceGray';
4555                                 }
4556                                 $info['i'] = $this->numimages + 1;
4557                                 if ($imgmask !== false) {
4558                                         $info['masked'] = $imgmask;
4559                                 }
4560                                 // add image to document
4561                                 $this->setImageBuffer($file, $info);
4562                         } else {
4563                                 $info = $this->getImageBuffer($file);
4564                         }
4565                         // Check whether we need a new page first as this does not fit
4566                         if ($this->checkPageBreak($h, $y)) {
4567                                 $y = $this->GetY() + $this->cMargin;
4568                         }
4569                         // set bottomcoordinates
4570                         $this->img_rb_y = $y + $h;
4571                         // set alignment
4572                         if ($this->rtl) {
4573                                 if ($palign == 'L') {
4574                                         $ximg = $this->lMargin;
4575                                         // set right side coordinate
4576                                         $this->img_rb_x = $ximg + $w;
4577                                 } elseif ($palign == 'C') {
4578                                         $ximg = ($this->w - $x - $w) / 2;
4579                                         // set right side coordinate
4580                                         $this->img_rb_x = $ximg + $w;
4581                                 } else {
4582                                         $ximg = $this->w - $x - $w;
4583                                         // set left side coordinate
4584                                         $this->img_rb_x = $ximg;
4585                                 }
4586                         } else {
4587                                 if ($palign == 'R') {
4588                                         $ximg = $this->w - $this->rMargin - $w;
4589                                         // set left side coordinate
4590                                         $this->img_rb_x = $ximg;
4591                                 } elseif ($palign == 'C') {
4592                                         $ximg = ($this->w - $x - $w) / 2;
4593                                         // set right side coordinate
4594                                         $this->img_rb_x = $ximg + $w;
4595                                 } else {
4596                                         $ximg = $x;
4597                                         // set right side coordinate
4598                                         $this->img_rb_x = $ximg + $w;
4599                                 }
4600                         }
4601                         if ($ismask) {
4602                                 // embed hidden, ouside the canvas
4603                                 $xkimg = ($this->pagedim[$this->page]['w'] + 10);
4604                         } else {
4605                                 $xkimg = $ximg * $this->k;
4606                         }
4607                         $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
4608                         if (!empty($border)) {
4609                                 $bx = $x;
4610                                 $by = $y;
4611                                 $this->x = $ximg;
4612                                 $this->y = $y;
4613                                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
4614                                 $this->x = $bx;
4615                                 $this->y = $by;
4616                         }
4617                         if ($link) {
4618                                 $this->Link($ximg, $y, $w, $h, $link, 0);
4619                         }
4620                         // set pointer to align the successive text/objects
4621                         switch($align) {
4622                                 case 'T': {
4623                                         $this->y = $y;
4624                                         $this->x = $this->img_rb_x;
4625                                         break;
4626                                 }
4627                                 case 'M': {
4628                                         $this->y = $y + round($h/2);
4629                                         $this->x = $this->img_rb_x;
4630                                         break;
4631                                 }
4632                                 case 'B': {
4633                                         $this->y = $this->img_rb_y;
4634                                         $this->x = $this->img_rb_x;
4635                                         break;
4636                                 }
4637                                 case 'N': {
4638                                         $this->SetY($this->img_rb_y);
4639                                         break;
4640                                 }
4641                                 default:{
4642                                         break;
4643                                 }
4644                         }
4645                         $this->endlinex = $this->img_rb_x;
4646                         return $info['i'];
4647                 }
4648                                 
4649                 /**
4650                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
4651                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
4652                 * @param string $file Image file name.
4653                 * @param image $image Image object.
4654                 * return image JPEG image object.
4655                 * @access protected
4656                 */
4657                 protected function _toJPEG($image) {
4658                         $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4659                         imagejpeg($image, $tempname, $this->jpeg_quality);
4660                         imagedestroy($image);
4661                         $retvars = $this->_parsejpeg($tempname);
4662                         // tidy up by removing temporary image
4663                         unlink($tempname);
4664                         return $retvars;
4665                 }
4666                 
4667                 /**
4668                 * Extract info from a JPEG file without using the GD library.
4669                 * @param string $file image file to parse
4670                 * @return array structure containing the image data
4671                 * @access protected
4672                 */
4673                 protected function _parsejpeg($file) {
4674                         $a = getimagesize($file);
4675                         if (empty($a)) {
4676                                 $this->Error('Missing or incorrect image file: '.$file);
4677                         }
4678                         if ($a[2] != 2) {
4679                                 $this->Error('Not a JPEG file: '.$file);
4680                         }
4681                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
4682                                 $colspace = 'DeviceRGB';
4683                         } elseif ($a['channels'] == 4) {
4684                                 $colspace = 'DeviceCMYK';
4685                         } else {
4686                                 $colspace = 'DeviceGray';
4687                         }
4688                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
4689                         $data = file_get_contents($file);
4690                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
4691                 }
4692
4693                 /**
4694                 * Extract info from a PNG file without using the GD library.
4695                 * @param string $file image file to parse
4696                 * @return array structure containing the image data
4697                 * @access protected
4698                 */
4699                 protected function _parsepng($file) {
4700                         $f = fopen($file, 'rb');
4701                         if ($f === false) {
4702                                 $this->Error('Can\'t open image file: '.$file);
4703                         }
4704                         //Check signature
4705                         if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
4706                                 $this->Error('Not a PNG file: '.$file);
4707                         }
4708                         //Read header chunk
4709                         fread($f, 4);
4710                         if (fread($f, 4) != 'IHDR') {
4711                                 $this->Error('Incorrect PNG file: '.$file);
4712                         }
4713                         $w = $this->_freadint($f);
4714                         $h = $this->_freadint($f);
4715                         $bpc = ord(fread($f, 1));
4716                         if ($bpc > 8) {
4717                                 //$this->Error('16-bit depth not supported: '.$file);
4718                                 fclose($f);
4719                                 return false;
4720                         }
4721                         $ct = ord(fread($f, 1));
4722                         if ($ct == 0) {
4723                                 $colspace = 'DeviceGray';
4724                         } elseif ($ct == 2) {
4725                                 $colspace = 'DeviceRGB';
4726                         } elseif ($ct == 3) {
4727                                 $colspace = 'Indexed';
4728                         } else {
4729                                 // alpha channel
4730                                 fclose($f);
4731                                 return 'pngalpha';
4732                         }
4733                         if (ord(fread($f, 1)) != 0) {
4734                                 //$this->Error('Unknown compression method: '.$file);
4735                                 fclose($f);
4736                                 return false;
4737                         }
4738                         if (ord(fread($f, 1)) != 0) {
4739                                 //$this->Error('Unknown filter method: '.$file);
4740                                 fclose($f);
4741                                 return false;
4742                         }
4743                         if (ord(fread($f, 1)) != 0) {
4744                                 //$this->Error('Interlacing not supported: '.$file);
4745                                 fclose($f);
4746                                 return false;
4747                         }
4748                         fread($f, 4);
4749                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
4750                         //Scan chunks looking for palette, transparency and image data
4751                         $pal = '';
4752                         $trns = '';
4753                         $data = '';
4754                         do {
4755                                 $n = $this->_freadint($f);
4756                                 $type = fread($f, 4);
4757                                 if ($type == 'PLTE') {
4758                                         //Read palette
4759                                         $pal = $this->rfread($f, $n);
4760                                         fread($f, 4);
4761                                 } elseif ($type == 'tRNS') {
4762                                         //Read transparency info
4763                                         $t = $this->rfread($f, $n);
4764                                         if ($ct == 0) {
4765                                                 $trns = array(ord(substr($t, 1, 1)));
4766                                         } elseif ($ct == 2) {
4767                                                 $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
4768                                         } else {
4769                                                 $pos = strpos($t, chr(0));
4770                                                 if ($pos !== false) {
4771                                                         $trns = array($pos);
4772                                                 }
4773                                         }
4774                                         fread($f, 4);
4775                                 } elseif ($type == 'IDAT') {
4776                                         //Read image data block
4777                                         $data .= $this->rfread($f, $n);
4778                                         fread($f, 4);
4779                                 } elseif ($type == 'IEND') {
4780                                         break;
4781                                 } else {
4782                                         $this->rfread($f, $n + 4);
4783                                 }
4784                         } while ($n);
4785                         if (($colspace == 'Indexed') AND (empty($pal))) {
4786                                 //$this->Error('Missing palette in '.$file);
4787                                 fclose($f);
4788                                 return false;
4789                         }
4790                         fclose($f);
4791                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
4792                 }
4793
4794                 /**
4795                 * Binary-safe and URL-safe file read.
4796                 * Reads up to length  bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.
4797                 * @param resource $handle
4798                 * @param int $length
4799                 * @return Returns the read string or FALSE in case of error.
4800                 * @author Nicola Asuni
4801                 * @access protected
4802                 * @since 4.5.027 (2009-03-16)
4803                 */
4804                 protected function rfread($handle, $length) {
4805                         $data = fread($handle, $length);
4806                         if ($data === false) {
4807                                 return false;
4808                         }
4809                         $rest = $length - strlen($data);
4810                         if ($rest > 0) {
4811                                 $data .= $this->rfread($handle, $rest);
4812                         }
4813                         return $data;
4814                 }
4815
4816                 /**
4817                 * Extract info from a PNG image with alpha channel using the GD library.
4818                 * @param string $file Name of the file containing the image.
4819                 * @param float $x Abscissa of the upper-left corner.
4820                 * @param float $y Ordinate of the upper-left corner.
4821                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
4822                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
4823                 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
4824                 * @param mixed $link URL or identifier returned by AddLink().
4825                 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
4826                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
4827                 * @param int $dpi dot-per-inch resolution used on resize
4828                 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
4829                 * @author Valentin Schmidt, Nicola Asuni
4830                 * @access protected
4831                 * @since 4.3.007 (2008-12-04)
4832                 * @see Image()
4833                 */
4834                 protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
4835                         // get image size
4836                         list($wpx, $hpx) = getimagesize($file);
4837                         // generate images
4838                         $img = imagecreatefrompng($file);
4839                         $imgalpha = imagecreate($wpx, $hpx);
4840                         // generate gray scale pallete
4841                         for ($c = 0; $c < 256; ++$c) {
4842                                 ImageColorAllocate($imgalpha, $c, $c, $c);
4843                         }
4844                         // extract alpha channel
4845                         for ($xpx = 0; $xpx < $wpx; ++$xpx) {
4846                                 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
4847                                         $colorindex = imagecolorat($img, $xpx, $ypx);
4848                                         $col = imagecolorsforindex($img, $colorindex);
4849                                         imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
4850                                 }
4851                         }
4852                         // create temp alpha file
4853                         $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
4854                         imagepng($imgalpha, $tempfile_alpha);
4855                         imagedestroy($imgalpha);
4856                         // extract image without alpha channel
4857                         $imgplain = imagecreatetruecolor($wpx, $hpx);
4858                         imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4859                         // create temp image file
4860                         $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
4861                         imagepng($imgplain, $tempfile_plain);
4862                         imagedestroy($imgplain);
4863                         // embed mask image
4864                         $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
4865                         // embed image, masked with previously embedded mask
4866                         $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
4867                         // remove temp files
4868                         unlink($tempfile_alpha);
4869                         unlink($tempfile_plain);
4870                 }
4871
4872                 /**
4873                 * Correct the gamma value to be used with GD library
4874                 * @param float $v the gamma value to be corrected
4875                 * @access protected
4876                 * @since 4.3.007 (2008-12-04)
4877                 */
4878                 protected function getGDgamma($v) {
4879                         return (pow(($v / 255), 2.2) * 255);
4880                 } 
4881                 
4882                 /**
4883                 * Performs a line break. 
4884                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
4885                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
4886                 * @param boolean $cell if true add a cMargin to the x coordinate
4887                 * @access public
4888                 * @since 1.0
4889                 * @see Cell()
4890                 */
4891                 public function Ln($h='', $cell=false) {
4892                         //Line feed; default value is last cell height
4893                         if ($cell) {
4894                                 $cellmargin = $this->cMargin;
4895                         } else {
4896                                 $cellmargin = 0;
4897                         }
4898                         if ($this->rtl) {
4899                                 $this->x = $this->w - $this->rMargin - $cellmargin;
4900                         } else {
4901                                 $this->x = $this->lMargin + $cellmargin;
4902                         }
4903                         if (is_string($h)) {
4904                                 $this->y += $this->lasth;
4905                         } else {
4906                                 $this->y += $h;
4907                         }
4908                         $this->newline = true;
4909                 }
4910
4911                 /**
4912                 * Returns the relative X value of current position.
4913                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
4914                 * @return float
4915                 * @access public
4916                 * @since 1.2
4917                 * @see SetX(), GetY(), SetY()
4918                 */
4919                 public function GetX() {
4920                         //Get x position
4921                         if ($this->rtl) {
4922                                 return ($this->w - $this->x);
4923                         } else {
4924                                 return $this->x;
4925                         }
4926                 }
4927                 
4928                 /**
4929                 * Returns the absolute X value of current position.
4930                 * @return float
4931                 * @access public
4932                 * @since 1.2
4933                 * @see SetX(), GetY(), SetY()
4934                 */
4935                 public function GetAbsX() {
4936                         return $this->x;
4937                 }
4938                 
4939                 /**
4940                 * Returns the ordinate of the current position.
4941                 * @return float
4942                 * @access public
4943                 * @since 1.0
4944                 * @see SetY(), GetX(), SetX()
4945                 */
4946                 public function GetY() {
4947                         //Get y position
4948                         return $this->y;
4949                 }
4950                 
4951                 /**
4952                 * Defines the abscissa of the current position. 
4953                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
4954                 * @param float $x The value of the abscissa.
4955                 * @access public
4956                 * @since 1.2
4957                 * @see GetX(), GetY(), SetY(), SetXY()
4958                 */
4959                 public function SetX($x) {
4960                         //Set x position
4961                         if ($this->rtl) {
4962                                 if ($x >= 0) {
4963                                         $this->x = $this->w - $x;
4964                                 } else {
4965                                         $this->x = abs($x);
4966                                 }
4967                         } else {
4968                                 if ($x >= 0) {
4969                                         $this->x = $x;
4970                                 } else {
4971                                         $this->x = $this->w + $x;
4972                                 }
4973                         }
4974                         if ($this->x < 0) {
4975                                 $this->x = 0;
4976                         }
4977                         if ($this->x > $this->w) {
4978                                 $this->x = $this->w;
4979                         }
4980                 }
4981                 
4982                 /**
4983                 * Moves the current abscissa back to the left margin and sets the ordinate.
4984                 * If the passed value is negative, it is relative to the bottom of the page.
4985                 * @param float $y The value of the ordinate.
4986                 * @param bool $resetx if true (default) reset the X position.
4987                 * @access public
4988                 * @since 1.0
4989                 * @see GetX(), GetY(), SetY(), SetXY()
4990                 */
4991                 public function SetY($y, $resetx=true) {
4992                         if ($resetx) {
4993                                 //reset x
4994                                 if ($this->rtl) {
4995                                         $this->x = $this->w - $this->rMargin;
4996                                 } else {
4997                                         $this->x = $this->lMargin;
4998                                 }
4999                         }
5000                         if ($y >= 0) {
5001                                 $this->y = $y;
5002                         } else {
5003                                 $this->y = $this->h + $y;
5004                         }
5005                         if ($this->y < 0) {
5006                                 $this->y = 0;
5007                         }
5008                         if ($this->y > $this->h) {
5009                                 $this->y = $this->h;
5010                         }
5011                 }
5012                 
5013                 /**
5014                 * Defines the abscissa and ordinate of the current position. 
5015                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
5016                 * @param float $x The value of the abscissa
5017                 * @param float $y The value of the ordinate
5018                 * @access public
5019                 * @since 1.2
5020                 * @see SetX(), SetY()
5021                 */
5022                 public function SetXY($x, $y) {
5023                         //Set x and y positions
5024                         $this->SetY($y);
5025                         $this->SetX($x);
5026                 }
5027
5028                 /**
5029                 * Send the document to a given destination: string, local file or browser. 
5030                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
5031                 * The method first calls Close() if necessary to terminate the document.
5032                 * @param string $name The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
5033                 * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>
5034                 * @access public
5035                 * @since 1.0
5036                 * @see Close()
5037                 */
5038                 public function Output($name='doc.pdf', $dest='I') {
5039                         //Output PDF to some destination
5040                         //Finish document if necessary
5041                         if ($this->state < 3) {
5042                                 $this->Close();
5043                         }
5044                         //Normalize parameters
5045                         if (is_bool($dest)) {
5046                                 $dest = $dest ? 'D' : 'F';
5047                         }
5048                         $dest = strtoupper($dest);
5049                         if ($dest != 'F') {
5050                                 $name = preg_replace('/[\s]+/', '_', $name);
5051                                 $name = preg_replace('/[^a-zA-Z0-9\._-\x{4e00}-\x{9fa5}]/u', '', $name);
5052                         }
5053                         if ($this->sign) {
5054                                 // *** apply digital signature to the document ***
5055                                 // get the document content
5056                                 $pdfdoc = $this->getBuffer();
5057                                 // remove last newline
5058                                 $pdfdoc = substr($pdfdoc, 0, -1);
5059                                 // Remove the original buffer
5060                                 if (isset($this->diskcache) AND $this->diskcache) {
5061                                         // remove buffer file from cache
5062                                         unlink($this->buffer);
5063                                 }
5064                                 unset($this->buffer);
5065                                 // remove filler space
5066                                 $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58;
5067                                 $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght);
5068                                 // define the ByteRange
5069                                 $byte_range = array();
5070                                 $byte_range[0] = 0;
5071                                 $byte_range[1] = $tmppos - 1;
5072                                 $byte_range[2] = $byte_range[1] + $this->signature_max_lenght;
5073                                 $byte_range[3] = strlen($pdfdoc) - $byte_range[1];
5074                                 // replace the ByteRange
5075                                 $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]);
5076                                 $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc);
5077                                 // write the document to a temporary folder
5078                                 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
5079                                 $f = fopen($tempdoc, 'wb');
5080                                 if (!$f) {
5081                                         $this->Error('Unable to create temporary file: '.$tempdoc);
5082                                 }
5083                                 $pdfdoc_lenght = strlen($pdfdoc);
5084                                 fwrite($f, $pdfdoc, $pdfdoc_lenght);
5085                                 fclose($f);
5086                                 // get digital signature.
5087                                 // IS THE FOLLOWING PROCEDURE CORRECT? THE SIGNED DOCUMENTS ARE NOT VALID!
5088                                 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
5089                                 if (empty($this->signature_data['extracerts'])) {
5090                                         openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
5091                                 } else {
5092                                         openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
5093                                 }       
5094                                 unlink($tempdoc);
5095                                 // read signature
5096                                 $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
5097                                 unlink($tempsign);
5098                                 // extract signature
5099                                 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
5100                                 $tmparr = explode("\n\n", $signature);
5101                                 $signature = $tmparr[1];
5102                                 unset($tmparr);
5103                                 // decode signature
5104                                 $signature = base64_decode(trim($signature));
5105                                 // convert signature to hex
5106                                 $signature = current(unpack('H*', $signature));
5107                                 $signature = str_pad($signature, $this->signature_max_lenght, '0');
5108                                 // Add signature to the document
5109                                 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3]));
5110                                 $this->diskcache = false;
5111                                 $this->buffer = &$pdfdoc;
5112                                 $this->bufferlen = strlen($pdfdoc);
5113                         }
5114                         switch($dest) {
5115                                 case 'I': {
5116                                         // Send PDF to the standard output
5117                                         if (ob_get_contents()) {
5118                                                 $this->Error('Some data has already been output, can\'t send PDF file');
5119                                         }
5120                                         if (php_sapi_name() != 'cli') {
5121                                                 //We send to a browser
5122                                                 header('Content-Type: application/pdf');
5123                                                 if (headers_sent()) {
5124                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
5125                                                 }
5126                                                 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5127                                                 header('Pragma: public');
5128                                                 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5129                                                 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');      
5130                                                 header('Content-Length: '.$this->bufferlen);
5131                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
5132                                         }
5133                                         echo $this->getBuffer();
5134                                         break;
5135                                 }
5136                                 case 'D': {
5137                                         // Download PDF as file
5138                                         if (ob_get_contents()) {
5139                                                 $this->Error('Some data has already been output, can\'t send PDF file');
5140                                         }
5141                                         header('Content-Description: File Transfer');
5142                                         if (headers_sent()) {
5143                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
5144                                         }
5145                                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5146                                         header('Pragma: public');
5147                                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5148                                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
5149                                         // force download dialog
5150                                         header('Content-Type: application/force-download');
5151                                         header('Content-Type: application/octet-stream', false);
5152                                         header('Content-Type: application/download', false);
5153                                         header('Content-Type: application/pdf', false);
5154                                         // use the Content-Disposition header to supply a recommended filename
5155                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
5156                                         header('Content-Transfer-Encoding: binary');
5157                                         header('Content-Length: '.$this->bufferlen);
5158                                         echo $this->getBuffer();
5159                                         break;
5160                                 }
5161                                 case 'F': {
5162                                         // Save PDF to a local file
5163                                         if ($this->diskcache) {
5164                                                 copy($this->buffer, $name);
5165                                         } else {
5166                                                 $f = fopen($name, 'wb');
5167                                                 if (!$f) {
5168                                                         $this->Error('Unable to create output file: '.$name);
5169                                                 }
5170                                                 fwrite($f, $this->getBuffer(), $this->bufferlen);
5171                                                 fclose($f);
5172                                         }
5173                                         break;
5174                                 }
5175                                 case 'S': {
5176                                         // Returns PDF as a string
5177                                         return $this->getBuffer();
5178                                 }
5179                                 default: {
5180                                         $this->Error('Incorrect output destination: '.$dest);
5181                                 }
5182                         }
5183                         return '';
5184                 }
5185
5186                 /**
5187                  * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
5188                  * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.
5189                  * @param boolean $preserve_objcopy if true preserves the objcopy variable
5190                  * @access public
5191                  * @since 4.5.016 (2009-02-24)
5192                  */
5193                 public function _destroy($destroyall=false, $preserve_objcopy=false) {
5194                         if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
5195                                 // remove buffer file from cache
5196                                 unlink($this->buffer);
5197                         }
5198                         foreach (array_keys(get_object_vars($this)) as $val) {
5199                                 if ($destroyall OR (
5200                                         ($val != 'internal_encoding') 
5201                                         AND ($val != 'state') 
5202                                         AND ($val != 'bufferlen') 
5203                                         AND ($val != 'buffer') 
5204                                         AND ($val != 'diskcache')
5205                                         AND ($val != 'sign')
5206                                         AND ($val != 'signature_data')
5207                                         AND ($val != 'signature_max_lenght')
5208                                         )) {
5209                                         if (!$preserve_objcopy OR ($val != 'objcopy')) {
5210                                                 unset($this->$val);
5211                                         }
5212                                 }
5213                         }
5214                 }
5215                 
5216                 /**
5217                 * Check for locale-related bug
5218                 * @access protected
5219                 */
5220                 protected function _dochecks() {
5221                         //Check for locale-related bug
5222                         if (1.1 == 1) {
5223                                 $this->Error('Don\'t alter the locale before including class file');
5224                         }
5225                         //Check for decimal separator
5226                         if (sprintf('%.1F', 1.0) != '1.0') {
5227                                 setlocale(LC_NUMERIC, 'C');
5228                         }
5229                 }
5230
5231                 /**
5232                 * Return fonts path
5233                 * @return string
5234                 * @access protected
5235                 */
5236                 protected function _getfontpath() {
5237                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
5238                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
5239                         }
5240                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
5241                 }
5242                 
5243                 /**
5244                 * Output pages.
5245                 * @access protected
5246                 */
5247                 protected function _putpages() {
5248                         $nb = $this->numpages;
5249                         if (!empty($this->AliasNbPages)) {
5250                                 $nbs = $this->formatPageNumber($nb);
5251                                 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
5252                                 $alias_a = $this->_escape($this->AliasNbPages);
5253                                 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
5254                                 if ($this->isunicode) {
5255                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
5256                                         $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
5257                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
5258                                         $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
5259                                 }
5260                         }
5261                         if (!empty($this->AliasNumPage)) {
5262                                 $alias_pa = $this->_escape($this->AliasNumPage);
5263                                 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
5264                                 if ($this->isunicode) {
5265                                         $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
5266                                         $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
5267                                         $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
5268                                         $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
5269                                 }
5270                         }
5271                         $pagegroupnum = 0;
5272                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
5273                         for ($n=1; $n <= $nb; ++$n) {
5274                                 $temppage = $this->getPageBuffer($n);
5275                                 if (!empty($this->pagegroups)) {
5276                                         if(isset($this->newpagegroup[$n])) {
5277                                                 $pagegroupnum = 0;
5278                                         }
5279                                         ++$pagegroupnum;
5280                                         foreach ($this->pagegroups as $k => $v) {
5281                                                 // replace total pages group numbers
5282                                                 $vs = $this->formatPageNumber($v);
5283                                                 $vu = $this->UTF8ToUTF16BE($vs, false);
5284                                                 $alias_ga = $this->_escape($k);
5285                                                 $alias_gau = $this->_escape('{'.$k.'}');
5286                                                 if ($this->isunicode) {
5287                                                         $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
5288                                                         $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
5289                                                         $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
5290                                                         $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
5291                                                 }
5292                                                 $temppage = str_replace($alias_gau, $vu, $temppage);
5293                                                 if ($this->isunicode) {
5294                                                         $temppage = str_replace($alias_gbu, $vu, $temppage);
5295                                                         $temppage = str_replace($alias_gcu, $vu, $temppage);
5296                                                         $temppage = str_replace($alias_gb, $vs, $temppage);
5297                                                         $temppage = str_replace($alias_gc, $vs, $temppage);
5298                                                 }
5299                                                 $temppage = str_replace($alias_ga, $vs, $temppage);
5300                                                 // replace page group numbers
5301                                                 $pvs = $this->formatPageNumber($pagegroupnum);
5302                                                 $pvu = $this->UTF8ToUTF16BE($pvs, false);
5303                                                 $pk = str_replace('{nb', '{pnb', $k);
5304                                                 $alias_pga = $this->_escape($pk);
5305                                                 $alias_pgau = $this->_escape('{'.$pk.'}');
5306                                                 if ($this->isunicode) {
5307                                                         $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
5308                                                         $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
5309                                                         $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
5310                                                         $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
5311                                                 }
5312                                                 $temppage = str_replace($alias_pgau, $pvu, $temppage);
5313                                                 if ($this->isunicode) {
5314                                                         $temppage = str_replace($alias_pgbu, $pvu, $temppage);
5315                                                         $temppage = str_replace($alias_pgcu, $pvu, $temppage);
5316                                                         $temppage = str_replace($alias_pgb, $pvs, $temppage);
5317                                                         $temppage = str_replace($alias_pgc, $pvs, $temppage);
5318                                                 }
5319                                                 $temppage = str_replace($alias_pga, $pvs, $temppage);
5320                                         }
5321                                 }
5322                                 if (!empty($this->AliasNbPages)) {
5323                                         // replace total pages number
5324                                         $temppage = str_replace($alias_au, $nbu, $temppage);
5325                                         if ($this->isunicode) {
5326                                                 $temppage = str_replace($alias_bu, $nbu, $temppage);
5327                                                 $temppage = str_replace($alias_cu, $nbu, $temppage);
5328                                                 $temppage = str_replace($alias_b, $nbs, $temppage);
5329                                                 $temppage = str_replace($alias_c, $nbs, $temppage);
5330                                         }
5331                                         $temppage = str_replace($alias_a, $nbs, $temppage);
5332                                 }
5333                                 if (!empty($this->AliasNumPage)) {
5334                                         // replace page number
5335                                         $pnbs = $this->formatPageNumber($n);
5336                                         $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
5337                                         $temppage = str_replace($alias_pau, $pnbu, $temppage);
5338                                         if ($this->isunicode) {
5339                                                 $temppage = str_replace($alias_pbu, $pnbu, $temppage);
5340                                                 $temppage = str_replace($alias_pcu, $pnbu, $temppage);
5341                                                 $temppage = str_replace($alias_pb, $pnbs, $temppage);
5342                                                 $temppage = str_replace($alias_pc, $pnbs, $temppage);
5343                                         }
5344                                         $temppage = str_replace($alias_pa, $pnbs, $temppage);
5345                                 }
5346                                 $temppage = str_replace($this->epsmarker, '', $temppage);
5347                                 //$this->setPageBuffer($n, $temppage);
5348                                 //Page
5349                                 $this->_newobj();
5350                                 $this->_out('<</Type /Page');
5351                                 $this->_out('/Parent 1 0 R');
5352                                 $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
5353                                 $this->_out('/Resources 2 0 R');
5354                                 $this->_putannots($n);
5355                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
5356                                 $this->_out('endobj');
5357                                 //Page content
5358                                 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
5359                                 $this->_newobj();
5360                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
5361                                 $this->_putstream($p);
5362                                 $this->_out('endobj');
5363                                 if ($this->diskcache) {
5364                                         // remove temporary files
5365                                         unlink($this->pages[$n]);
5366                                 }
5367                         }
5368                         //Pages root
5369                         $this->offsets[1] = $this->bufferlen;
5370                         $this->_out('1 0 obj');
5371                         $this->_out('<</Type /Pages');
5372                         $kids='/Kids [';
5373                         for ($i=0; $i < $nb; ++$i) {
5374                                 $kids .= (3 + (2 * $i)).' 0 R ';
5375                         }
5376                         $this->_out($kids.']');
5377                         $this->_out('/Count '.$nb);
5378                         //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
5379                         $this->_out('>>');
5380                         $this->_out('endobj');
5381                 }
5382
5383                 /**
5384                 * Output Page Annotations.
5385                 * !!! THIS FUNCTION IS NOT YET COMPLETED !!!
5386                 * See section 8.4 of PDF reference.
5387                 * @param int $n page number
5388                 * @access protected
5389                 * @author Nicola Asuni
5390                 * @since 4.0.018 (2008-08-06)
5391                 */
5392                 protected function _putannots($n) {
5393                         if (isset($this->PageAnnots[$n])) {
5394                                 $annots = '/Annots [';
5395                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
5396                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
5397                                         $a = $pl['x'] * $this->k;
5398                                         $b = $this->pagedim[$n]['h'] - ($pl['y']  * $this->k);
5399                                         $c = $pl['w'] * $this->k;
5400                                         $d = $pl['h'] * $this->k;
5401                                         $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d);
5402                                         $annots .= "\n";
5403                                         $annots .= '<</Type /Annot';
5404                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
5405                                         $annots .= ' /Rect ['.$rect.']';
5406                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
5407                                         //$annots .= ' /P ';
5408                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
5409                                         $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis'));
5410                                         if (isset($pl['opt']['f'])) {
5411                                                 $val = 0;
5412                                                 if (is_array($pl['opt']['f'])) {
5413                                                         foreach ($pl['opt']['f'] as $f) {
5414                                                                 switch (strtolower($f)) {
5415                                                                         case 'invisible': {
5416                                                                                 $val += 1 << 0;
5417                                                                                 break;
5418                                                                         }
5419                                                                         case 'hidden': {
5420                                                                                 $val += 1 << 1;
5421                                                                                 break;
5422                                                                         }
5423                                                                         case 'print': {
5424                                                                                 $val += 1 << 2;
5425                                                                                 break;
5426                                                                         }
5427                                                                         case 'nozoom': {
5428                                                                                 $val += 1 << 3;
5429                                                                                 break;
5430                                                                         }
5431                                                                         case 'norotate': {
5432                                                                                 $val += 1 << 4;
5433                                                                                 break;
5434                                                                         }
5435                                                                         case 'noview': {
5436                                                                                 $val += 1 << 5;
5437                                                                                 break;
5438                                                                         }
5439                                                                         case 'readonly': {
5440                                                                                 $val += 1 << 6;
5441                                                                                 break;
5442                                                                         }
5443                                                                         case 'locked': {
5444                                                                                 $val += 1 << 8;
5445                                                                                 break;
5446                                                                         }
5447                                                                         case 'togglenoview': {
5448                                                                                 $val += 1 << 9;
5449                                                                                 break;
5450                                                                         }
5451                                                                         case 'lockedcontents': {
5452                                                                                 $val += 1 << 10;
5453                                                                                 break;
5454                                                                         }
5455                                                                         default: {
5456                                                                                 break;
5457                                                                         }
5458                                                                 }
5459                                                         }
5460                                                 }
5461                                                 $annots .= ' /F '.intval($val);
5462                                         }
5463                                         //$annots .= ' /AP ';
5464                                         //$annots .= ' /AS ';
5465                                         $annots .= ' /Border [';
5466                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
5467                                                 $annots .= intval($pl['opt']['border'][0]).' ';
5468                                                 $annots .= intval($pl['opt']['border'][1]).' ';
5469                                                 $annots .= intval($pl['opt']['border'][2]);
5470                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
5471                                                         $annots .= ' [';
5472                                                         foreach ($pl['opt']['border'][3] as $dash) {
5473                                                                 $annots .= intval($dash).' ';
5474                                                         }
5475                                                         $annots .= ']';
5476                                                 }
5477                                         } else {
5478                                                 $annots .= '0 0 0';
5479                                         }
5480                                         $annots .= ']';
5481                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
5482                                                 $annots .= ' /BS <<Type /Border';
5483                                                 if (isset($pl['opt']['bs']['w'])) {
5484                                                         $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w']));
5485                                                 }
5486                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
5487                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
5488                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
5489                                                 }
5490                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
5491                                                         $annots .= ' /D [';
5492                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
5493                                                                 $cord = floatval($cord);
5494                                                                 $annots .= sprintf(" %.4F", $cord);
5495                                                         }
5496                                                         $annots .= ']';
5497                                                 }
5498                                                 $annots .= '>> ';
5499                                         }
5500                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
5501                                                 $annots .= ' /BE <<';
5502                                                 $bstyles = array('S', 'C');
5503                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
5504                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
5505                                                 } else {
5506                                                         $annots .= ' /S /S';
5507                                                 }
5508                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
5509                                                         $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
5510                                                 }
5511                                                 $annots .= '>>';
5512                                         }
5513                                         $annots .= ' /C [';
5514                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
5515                                                 foreach ($pl['opt']['c'] as $col) {
5516                                                         $col = intval($col);
5517                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
5518                                                         $annots .= sprintf(" %.4F", $color);
5519                                                 }
5520                                         }
5521                                         $annots .= ']';
5522                                         //$annots .= ' /StructParent ';
5523                                         //$annots .= ' /OC ';
5524                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
5525                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
5526                                                 // this is a markup type
5527                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
5528                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
5529                                                 }
5530                                                 //$annots .= ' /Popup ';
5531                                                 if (isset($pl['opt']['ca'])) {
5532                                                         $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
5533                                                 }
5534                                                 if (isset($pl['opt']['rc'])) {
5535                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5536                                                 }
5537                                                 $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis'));
5538                                                 //$annots .= ' /IRT ';
5539                                                 if (isset($pl['opt']['subj'])) {
5540                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
5541                                                 }
5542                                                 //$annots .= ' /RT ';
5543                                                 //$annots .= ' /IT ';
5544                                                 //$annots .= ' /ExData ';
5545                                         }
5546                                         switch (strtolower($pl['opt']['subtype'])) {
5547                                                 case 'text': {
5548                                                         if (isset($pl['opt']['open'])) {
5549                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
5550                                                         }
5551                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
5552                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5553                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
5554                                                         } else {
5555                                                                 $annots .= ' /Name /Note';
5556                                                         }
5557                                                         $statemodels = array('Marked', 'Review');
5558                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
5559                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5560                                                         } else {
5561                                                                 $pl['opt']['statemodel'] = 'Marked';
5562                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5563                                                         }
5564                                                         if ($pl['opt']['statemodel'] == 'Marked') {
5565                                                                 $states = array('Accepted', 'Unmarked');
5566                                                         } else {
5567                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
5568                                                         }
5569                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
5570                                                                 $annots .= ' /State /'.$pl['opt']['state'];
5571                                                         } else {
5572                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
5573                                                                         $annots .= ' /State /Unmarked';
5574                                                                 } else {
5575                                                                         $annots .= ' /State /None';
5576                                                                 }
5577                                                         }
5578                                                         break;
5579                                                 }
5580                                                 case 'link': {
5581                                                         if(is_string($pl['txt'])) {
5582                                                                 // external URI link
5583                                                                 $annots .= ' /A <</S /URI /URI '.$this->_datastring($pl['txt']).'>>';
5584                                                         } else {
5585                                                                 // internal link
5586                                                                 $l = $this->links[$pl['txt']];
5587                                                                 $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
5588                                                         }
5589                                                         $hmodes = array('N', 'I', 'O', 'P');
5590                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
5591                                                                 $annots .= ' /H /'.$pl['opt']['h'];
5592                                                         } else {
5593                                                                 $annots .= ' /H /I';
5594                                                         }
5595                                                         //$annots .= ' /PA ';
5596                                                         //$annots .= ' /Quadpoints ';
5597                                                         break;
5598                                                 }
5599                                                 case 'freetext': {
5600                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
5601                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
5602                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
5603                                                         }
5604                                                         if (isset($pl['opt']['rc'])) {
5605                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5606                                                         }
5607                                                         if (isset($pl['opt']['ds'])) {
5608                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
5609                                                         }
5610                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
5611                                                                 $annots .= ' /CL [';
5612                                                                 foreach ($pl['opt']['cl'] as $cl) {
5613                                                                         $annots .= sprintf("%.4F ", $cl * $this->k);
5614                                                                 }
5615                                                                 $annots .= ']';
5616                                                         }
5617                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
5618                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
5619                                                                 $annots .= ' /IT '.$pl['opt']['it'];
5620                                                         }
5621                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
5622                                                                 $l = $pl['opt']['rd'][0] * $this->k;
5623                                                                 $r = $pl['opt']['rd'][1] * $this->k;
5624                                                                 $t = $pl['opt']['rd'][2] * $this->k;
5625                                                                 $b = $pl['opt']['rd'][3] * $this->k;
5626                                                                 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
5627                                                         }
5628                                                         //$annots .= ' /LE ';
5629                                                         break;
5630                                                 }
5631                                                 // ... to be completed ...
5632                                                 case 'line': {
5633                                                         break;
5634                                                 }
5635                                                 case 'square': {
5636                                                         break;
5637                                                 }
5638                                                 case 'circle': {
5639                                                         break;
5640                                                 }
5641                                                 case 'polygon': {
5642                                                         break;
5643                                                 }
5644                                                 case 'polyline': {
5645                                                         break;
5646                                                 }
5647                                                 case 'highlight': {
5648                                                         break;
5649                                                 }
5650                                                 case 'underline': {
5651                                                         break;
5652                                                 }
5653                                                 case 'squiggly': {
5654                                                         break;
5655                                                 }
5656                                                 case 'strikeout': {
5657                                                         break;
5658                                                 }
5659                                                 case 'stamp': {
5660                                                         break;
5661                                                 }
5662                                                 case 'caret': {
5663                                                         break;
5664                                                 }
5665                                                 case 'ink': {
5666                                                         break;
5667                                                 }
5668                                                 case 'popup': {
5669                                                         break;
5670                                                 }
5671                                                 case 'fileattachment': {
5672                                                         if (!isset($pl['opt']['fs'])) {
5673                                                                 break;
5674                                                         }
5675                                                         $filename = basename($pl['opt']['fs']);
5676                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
5677                                                                 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
5678                                                                 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
5679                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5680                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
5681                                                                 } else {
5682                                                                         $annots .= ' /Name /PushPin';
5683                                                                 }
5684                                                         }
5685                                                         break;
5686                                                 }
5687                                                 case 'sound': {
5688                                                         if (!isset($pl['opt']['sound'])) {
5689                                                                 break;
5690                                                         }
5691                                                         $filename = basename($pl['opt']['sound']);
5692                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
5693                                                                 // ... TO BE COMPLETED ...
5694                                                                 $iconsapp = array('Speaker', 'Mic');
5695                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5696                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
5697                                                                 } else {
5698                                                                         $annots .= ' /Name /Speaker';
5699                                                                 }
5700                                                         }
5701                                                         break;
5702                                                 }
5703                                                 case 'movie': {
5704                                                         break;
5705                                                 }
5706                                                 case 'widget': {
5707                                                         if (isset($pl['opt']['h'])) {
5708                                                                 $annots .= ' /H '.intval($pl['opt']['h']);
5709                                                         }
5710                                                         if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) {
5711                                                                 $annots .= ' /MK <<';
5712                                                                 // ... TO BE COMPLETED ...
5713                                                                 $annots .= '>>';
5714                                                         }
5715                                                         break;
5716                                                 }
5717                                                 case 'screen': {
5718                                                         break;
5719                                                 }
5720                                                 case 'printermark': {
5721                                                         break;
5722                                                 }
5723                                                 case 'trapnet': {
5724                                                         break;
5725                                                 }
5726                                                 case 'watermark': {
5727                                                         break;
5728                                                 }
5729                                                 case '3d': {
5730                                                         break;
5731                                                 }
5732                                                 default: {
5733                                                         break;
5734                                                 }
5735                                         }
5736                                         
5737                                 $annots .= '>>';
5738                                 }
5739                                 $annots .= "\n]";
5740                                 $this->_out($annots);
5741                         }
5742                 }
5743
5744                 /**
5745                 * Output fonts.
5746                 * @access protected
5747                 */
5748                 protected function _putfonts() {
5749                         $nf = $this->n;
5750                         foreach ($this->diffs as $diff) {
5751                                 //Encodings
5752                                 $this->_newobj();
5753                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
5754                                 $this->_out('endobj');
5755                         }
5756                         $mqr = get_magic_quotes_runtime();
5757                         //set_magic_quotes_runtime(0);
5758                         foreach ($this->FontFiles as $file => $info) {
5759                                 // search and get font file to embedd
5760                                 $fontdir = $info['fontdir'];
5761                                 $file = strtolower($file);
5762                                 $fontfile = '';
5763                                 // search files on various directories
5764                                 if (file_exists($fontdir.$file)) {
5765                                         $fontfile = $fontdir.$file;
5766                                 } elseif (file_exists($this->_getfontpath().$file)) {
5767                                         $fontfile = $this->_getfontpath().$file;
5768                                 } elseif (file_exists($file)) {
5769                                         $fontfile = $file;
5770                                 }
5771                                 if (!$this->empty_string($fontfile)) {
5772                                         $font = file_get_contents($fontfile);
5773                                         $compressed = (substr($file, -2) == '.z');
5774                                         if ((!$compressed) AND (isset($info['length2']))) {
5775                                                 $header = (ord($font{0}) == 128);
5776                                                 if ($header) {
5777                                                         //Strip first binary header
5778                                                         $font = substr($font, 6);
5779                                                 }
5780                                                 if ($header AND (ord($font{$info['length1']}) == 128)) {
5781                                                         //Strip second binary header
5782                                                         $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
5783                                                 }
5784                                         }
5785                                         $this->_newobj();
5786                                         $this->FontFiles[$file]['n'] = $this->n;
5787                                         $this->_out('<</Length '.strlen($font));
5788                                         if ($compressed) {
5789                                                 $this->_out('/Filter /FlateDecode');
5790                                         }
5791                                         $this->_out('/Length1 '.$info['length1']);
5792                                         if (isset($info['length2'])) {
5793                                                 $this->_out('/Length2 '.$info['length2'].' /Length3 0');
5794                                         }
5795                                         $this->_out('>>');
5796                                         $this->_putstream($font);
5797                                         $this->_out('endobj');
5798                                 }
5799                         }
5800                         //set_magic_quotes_runtime($mqr);
5801                         foreach ($this->fontkeys as $k) {
5802                                 //Font objects
5803                                 $this->setFontSubBuffer($k, 'n', $this->n + 1);
5804                                 $font = $this->getFontBuffer($k);
5805                                 $type = $font['type'];
5806                                 $name = $font['name'];
5807                                 if ($type == 'core') {
5808                                         //Standard font
5809                                         $this->_newobj();
5810                                         $this->_out('<</Type /Font');
5811                                         $this->_out('/BaseFont /'.$name);
5812                                         $this->_out('/Subtype /Type1');
5813                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
5814                                                 $this->_out('/Encoding /WinAnsiEncoding');
5815                                         }
5816                                         $this->_out('>>');
5817                                         $this->_out('endobj');
5818                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
5819                                         //Additional Type1 or TrueType font
5820                                         $this->_newobj();
5821                                         $this->_out('<</Type /Font');
5822                                         $this->_out('/BaseFont /'.$name);
5823                                         $this->_out('/Subtype /'.$type);
5824                                         $this->_out('/FirstChar 32 /LastChar 255');
5825                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
5826                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
5827                                         if ($font['enc']) {
5828                                                 if (isset($font['diff'])) {
5829                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
5830                                                 } else {
5831                                                         $this->_out('/Encoding /WinAnsiEncoding');
5832                                                 }
5833                                         }
5834                                         $this->_out('>>');
5835                                         $this->_out('endobj');
5836                                         // Widths
5837                                         $this->_newobj();
5838                                         $cw = &$font['cw'];
5839                                         $s = '[';
5840                                         for ($i = 32; $i < 256; ++$i) {
5841                                                 $s .= $cw[$i].' ';
5842                                         }
5843                                         $this->_out($s.']');
5844                                         $this->_out('endobj');
5845                                         //Descriptor
5846                                         $this->_newobj();
5847                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
5848                                         foreach ($font['desc'] as $k => $v) {
5849                                                 $s .= ' /'.$k.' '.$v.'';
5850                                         }
5851                                         if (!$this->empty_string($font['file'])) {
5852                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
5853                                         }
5854                                         $this->_out($s.'>>');
5855                                         $this->_out('endobj');
5856                                 } else {
5857                                         //Allow for additional types
5858                                         $mtd = '_put'.strtolower($type);
5859                                         if (!method_exists($this, $mtd)) {
5860                                                 $this->Error('Unsupported font type: '.$type);
5861                                         }
5862                                         $this->$mtd($font);
5863                                 }
5864                         }
5865                 }
5866                 
5867                 /**
5868                 * Outputs font widths
5869                 * @parameter array $font font data
5870                 * @parameter int $cidoffset offset for CID values
5871                 * @author Nicola Asuni
5872                 * @access protected
5873                 * @since 4.4.000 (2008-12-07)
5874                 */
5875                 protected function _putfontwidths($font, $cidoffset=0) {
5876                         ksort($font['cw']);
5877                         $rangeid = 0;
5878                         $range = array();
5879                         $prevcid = -2;
5880                         $prevwidth = -1;
5881                         $interval = false;
5882                         // for each character
5883                         foreach ($font['cw'] as $cid => $width) {
5884                                 $cid -= $cidoffset;
5885                                 if ($width != $font['dw']) {
5886                                         if ($cid == ($prevcid + 1)) {
5887                                                 // consecutive CID
5888                                                 if ($width == $prevwidth) {
5889                                                         if ($width == $range[$rangeid][0]) {
5890                                                                 $range[$rangeid][] = $width;
5891                                                         } else {
5892                                                                 array_pop($range[$rangeid]);
5893                                                                 // new range
5894                                                                 $rangeid = $prevcid;
5895                                                                 $range[$rangeid] = array();
5896                                                                 $range[$rangeid][] = $prevwidth;
5897                                                                 $range[$rangeid][] = $width;
5898                                                         }
5899                                                         $interval = true;
5900                                                         $range[$rangeid]['interval'] = true;
5901                                                 } else {
5902                                                         if ($interval) {
5903                                                                 // new range
5904                                                                 $rangeid = $cid;
5905                                                                 $range[$rangeid] = array();
5906                                                                 $range[$rangeid][] = $width;
5907                                                         } else {
5908                                                                 $range[$rangeid][] = $width;
5909                                                         }
5910                                                         $interval = false;
5911                                                 }
5912                                         } else {
5913                                                 // new range
5914                                                 $rangeid = $cid;
5915                                                 $range[$rangeid] = array();
5916                                                 $range[$rangeid][] = $width;
5917                                                 $interval = false;
5918                                         }
5919                                         $prevcid = $cid;
5920                                         $prevwidth = $width;
5921                                 }
5922                         }
5923                         // optimize ranges
5924                         $prevk = -1;
5925                         $nextk = -1;
5926                         $prevint = false;
5927                         foreach ($range as $k => $ws) {
5928                                 $cws = count($ws);
5929                                 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
5930                                         if (isset($range[$k]['interval'])) {
5931                                                 unset($range[$k]['interval']);
5932                                         }
5933                                         $range[$prevk] = array_merge($range[$prevk], $range[$k]);
5934                                         unset($range[$k]);
5935                                 } else {
5936                                         $prevk = $k;
5937                                 }
5938                                 $nextk = $k + $cws;
5939                                 if (isset($ws['interval'])) {
5940                                         if ($cws > 3) {
5941                                                 $prevint = true;
5942                                         } else {
5943                                                 $prevint = false;
5944                                         }
5945                                         unset($range[$k]['interval']);
5946                                         --$nextk;
5947                                 } else {
5948                                         $prevint = false;
5949                                 }
5950                         }
5951                         // output data
5952                         $w = '';
5953                         foreach ($range as $k => $ws) {
5954                                 if (count(array_count_values($ws)) == 1) {
5955                                         // interval mode is more compact
5956                                         $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
5957                                 } else {
5958                                         // range mode
5959                                         $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
5960                                 }
5961                         }
5962                         $this->_out('/W ['.$w.' ]');
5963                 }
5964                 
5965                 /**
5966                 * Adds unicode fonts.<br>
5967                 * Based on PDF Reference 1.3 (section 5)
5968                 * @parameter array $font font data
5969                 * @access protected
5970                 * @author Nicola Asuni
5971                 * @since 1.52.0.TC005 (2005-01-05)
5972                 */
5973                 protected function _puttruetypeunicode($font) {
5974                         // Type0 Font
5975                         // A composite font composed of other fonts, organized hierarchically
5976                         $this->_newobj();
5977                         $this->_out('<</Type /Font');
5978                         $this->_out('/Subtype /Type0');
5979                         $this->_out('/BaseFont /'.$font['name'].'');
5980                         $this->_out('/Encoding /Identity-H'); //The horizontal identity mapping for 2-byte CIDs; may be used with CIDFonts using any Registry, Ordering, and Supplement values.
5981                         $this->_out('/ToUnicode /Identity-H');
5982                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5983                         $this->_out('>>');
5984                         $this->_out('endobj');
5985                         // CIDFontType2
5986                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5987                         $this->_newobj();
5988                         $this->_out('<</Type /Font');
5989                         $this->_out('/Subtype /CIDFontType2');
5990                         $this->_out('/BaseFont /'.$font['name'].'');
5991                         // A dictionary containing entries that define the character collection of the CIDFont.
5992                         $cidinfo = '/Registry '.$this->_datastring('Adobe');
5993                         $cidinfo .= ' /Ordering '.$this->_datastring('Identity');
5994                         $cidinfo .= ' /Supplement 0';
5995                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
5996                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
5997                         $this->_out('/DW '.$font['dw'].''); // default width
5998                         $this->_putfontwidths($font, 0);
5999                         $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
6000                         $this->_out('>>');
6001                         $this->_out('endobj');                  
6002                         // Font descriptor
6003                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
6004                         $this->_newobj();
6005                         $this->_out('<</Type /FontDescriptor');
6006                         $this->_out('/FontName /'.$font['name']);
6007                         foreach ($font['desc'] as $key => $value) {
6008                                 $this->_out('/'.$key.' '.$value);
6009                         }
6010                         $fontdir = '';
6011                         if (!$this->empty_string($font['file'])) {
6012                                 // A stream containing a TrueType font
6013                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
6014                                 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
6015                         }
6016                         $this->_out('>>');
6017                         $this->_out('endobj');
6018                         $this->_newobj();
6019                         if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
6020                                 // Embed CIDToGIDMap
6021                                 // A specification of the mapping from CIDs to glyph indices
6022                                 // search and get CTG font file to embedd
6023                                 $ctgfile = strtolower($font['ctg']);
6024                                 // search and get ctg font file to embedd
6025                                 $fontfile = '';
6026                                 // search files on various directories
6027                                 if (file_exists($fontdir.$ctgfile)) {
6028                                         $fontfile = $fontdir.$ctgfile;
6029                                 } elseif (file_exists($this->_getfontpath().$ctgfile)) {
6030                                         $fontfile = $this->_getfontpath().$ctgfile;
6031                                 } elseif (file_exists($ctgfile)) {
6032                                         $fontfile = $ctgfile;
6033                                 }
6034                                 if ($this->empty_string($fontfile)) {
6035                                         $this->Error('Font file not found: '.$ctgfile);
6036                                 }
6037                                 $size = filesize($fontfile);
6038                                 $this->_out('<</Length '.$size.'');
6039                                 if (substr($fontfile, -2) == '.z') { // check file extension
6040                                         // Decompresses data encoded using the public-domain 
6041                                         // zlib/deflate compression method, reproducing the 
6042                                         // original text or binary data
6043                                         $this->_out('/Filter /FlateDecode');
6044                                 }
6045                                 $this->_out('>>');
6046                                 $this->_putstream(file_get_contents($fontfile));
6047                         }
6048                         $this->_out('endobj');
6049                 }
6050                 
6051                 /**
6052                  * Output CID-0 fonts.
6053                  * @param array $font font data
6054                  * @access protected
6055                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
6056                  * @since 3.2.000 (2008-06-23)
6057                  */
6058                 protected function _putcidfont0($font) {
6059                         $cidoffset = 31;
6060                         if (isset($font['cidinfo']['uni2cid'])) {
6061                                 // convert unicode to cid.
6062                                 $uni2cid = $font['cidinfo']['uni2cid'];
6063                                 $cw = array();
6064                                 foreach ($font['cw'] as $uni => $width) {
6065                                         if (isset($uni2cid[$uni])) {
6066                                                 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
6067                                         } elseif ($uni < 256) {
6068                                                 $cw[$uni] = $width;
6069                                         } // else unknown character
6070                                 }
6071                                 $font = array_merge($font, array('cw' => $cw));
6072                         }
6073                         $name = $font['name'];
6074                         $enc = $font['enc'];
6075                         if ($enc) {
6076                                 $longname = $name.'-'.$enc;
6077                         } else {
6078                                 $longname = $name;
6079                         }
6080                         $this->_newobj();
6081                         $this->_out('<</Type /Font');
6082                         $this->_out('/BaseFont /'.$longname);
6083                         $this->_out('/Subtype /Type0');
6084                         if ($enc) {
6085                                 $this->_out('/Encoding /'.$enc);
6086                         }
6087                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
6088                         $this->_out('>>');
6089                         $this->_out('endobj');
6090                         $this->_newobj();
6091                         $this->_out('<</Type /Font');
6092                         $this->_out('/BaseFont /'.$name);
6093                         $this->_out('/Subtype /CIDFontType0');
6094                         $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
6095                         $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
6096                         $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
6097                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
6098                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
6099                         $this->_out('/DW '.$font['dw']);
6100                         $this->_putfontwidths($font, $cidoffset);
6101                         $this->_out('>>');
6102                         $this->_out('endobj');
6103                         $this->_newobj();
6104                         $s = '<</Type /FontDescriptor /FontName /'.$name;
6105                         foreach ($font['desc'] as $k => $v) {
6106                                 if ($k != 'Style') {
6107                                         $s .= ' /'.$k.' '.$v.'';
6108                                 }
6109                         }
6110                         $this->_out($s.'>>');
6111                         $this->_out('endobj');
6112                 }
6113
6114                 /**
6115                  * Output images.
6116                  * @access protected
6117                  */
6118                 protected function _putimages() {
6119                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
6120                         foreach ($this->imagekeys as $file) {
6121                                 $info = $this->getImageBuffer($file);
6122                                 $this->_newobj();
6123                                 $this->setImageSubBuffer($file, 'n', $this->n);
6124                                 $this->_out('<</Type /XObject');
6125                                 $this->_out('/Subtype /Image');
6126                                 $this->_out('/Width '.$info['w']);
6127                                 $this->_out('/Height '.$info['h']);
6128                                 if (isset($info['masked'])) {
6129                                         $this->_out('/SMask '.($this->n - 1).' 0 R');
6130                                 }
6131                                 if ($info['cs'] == 'Indexed') {
6132                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
6133                                 } else {
6134                                         $this->_out('/ColorSpace /'.$info['cs']);
6135                                         if ($info['cs'] == 'DeviceCMYK') {
6136                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
6137                                         }
6138                                 }
6139                                 $this->_out('/BitsPerComponent '.$info['bpc']);
6140                                 if (isset($info['f'])) {
6141                                         $this->_out('/Filter /'.$info['f']);
6142                                 }
6143                                 if (isset($info['parms'])) {
6144                                         $this->_out($info['parms']);
6145                                 }
6146                                 if (isset($info['trns']) AND is_array($info['trns'])) {
6147                                         $trns='';
6148                                         $count_info = count($info['trns']);
6149                                         for ($i=0; $i < $count_info; ++$i) {
6150                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
6151                                         }
6152                                         $this->_out('/Mask ['.$trns.']');
6153                                 }
6154                                 $this->_out('/Length '.strlen($info['data']).'>>');
6155                                 $this->_putstream($info['data']);
6156                                 $this->_out('endobj');
6157                                 //Palette
6158                                 if ($info['cs'] == 'Indexed') {
6159                                         $this->_newobj();
6160                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
6161                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
6162                                         $this->_putstream($pal);
6163                                         $this->_out('endobj');
6164                                 }
6165                         }
6166                 }
6167
6168                 /**
6169                 * Output Spot Colors Resources.
6170                 * @access protected
6171                 * @since 4.0.024 (2008-09-12)
6172                 */
6173                 protected function _putspotcolors() {
6174                         foreach ($this->spot_colors as $name => $color) {
6175                                 $this->_newobj();
6176                                 $this->spot_colors[$name]['n'] = $this->n;
6177                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
6178                                 $this->_out('/DeviceCMYK <<');
6179                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
6180                                 $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
6181                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
6182                                 $this->_out('endobj');
6183                         }
6184                 }
6185
6186                 /**
6187                 * Output object dictionary for images.
6188                 * @access protected
6189                 */
6190                 protected function _putxobjectdict() {
6191                         foreach ($this->imagekeys as $file) {
6192                                 $info = $this->getImageBuffer($file);
6193                                 $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
6194                         }
6195                 }
6196
6197                 /**
6198                 * Output Resources Dictionary.
6199                 * @access protected
6200                 */
6201                 protected function _putresourcedict() {
6202                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
6203                         $this->_out('/Font <<');
6204                         foreach ($this->fontkeys as $fontkey) {
6205                                 $font = $this->getFontBuffer($fontkey);
6206                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
6207                         }
6208                         $this->_out('>>');
6209                         $this->_out('/XObject <<');
6210                         $this->_putxobjectdict();
6211                         $this->_out('>>');
6212                         // visibility
6213                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
6214                         // transparency
6215                         $this->_out('/ExtGState <<');
6216                         foreach ($this->extgstates as $k => $extgstate) {
6217                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
6218                         }
6219                         $this->_out('>>');
6220                         // gradients
6221                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
6222                                 $this->_out('/Shading <<');
6223                                 foreach ($this->gradients as $id => $grad) {
6224                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
6225                                 }
6226                                 $this->_out('>>');
6227                         }
6228                         // spot colors
6229                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
6230                                 $this->_out('/ColorSpace <<');
6231                                 foreach ($this->spot_colors as $color) {
6232                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
6233                                 }
6234                                 $this->_out('>>');
6235                         }
6236                 }
6237                 
6238                 /**
6239                 * Output Resources.
6240                 * @access protected
6241                 */
6242                 protected function _putresources() {
6243                         $this->_putextgstates();
6244                         $this->_putocg();
6245                         $this->_putfonts();
6246                         $this->_putimages();
6247                         $this->_putspotcolors();
6248                         $this->_putshaders();
6249                         //Resource dictionary
6250                         $this->offsets[2] = $this->bufferlen;
6251                         $this->_out('2 0 obj');
6252                         $this->_out('<<');
6253                         $this->_putresourcedict();
6254                         $this->_out('>>');
6255                         $this->_out('endobj');
6256                         $this->_putjavascript();
6257                         $this->_putbookmarks();
6258                         $this->_putEmbeddedFiles();
6259                         // encryption
6260                         if ($this->encrypted) {
6261                                 $this->_newobj();
6262                                 $this->enc_obj_id = $this->n;
6263                                 $this->_out('<<');
6264                                 $this->_putencryption();
6265                                 $this->_out('>>');
6266                                 $this->_out('endobj');
6267                         }
6268                 }
6269                 
6270                 /**
6271                 * Adds some Metadata information
6272                 * (see Chapter 10.2 of PDF Reference)
6273                 * @access protected
6274                 */
6275                 protected function _putinfo() {
6276                         if (!$this->empty_string($this->title)) {
6277                                 $this->_out('/Title '.$this->_textstring($this->title));
6278                         }
6279                         if (!$this->empty_string($this->author)) {
6280                                 $this->_out('/Author '.$this->_textstring($this->author));
6281                         }
6282                         if (!$this->empty_string($this->subject)) {
6283                                 $this->_out('/Subject '.$this->_textstring($this->subject));
6284                         }
6285                         if (!$this->empty_string($this->keywords)) {
6286                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
6287                         }
6288                         if (!$this->empty_string($this->creator)) {
6289                                 $this->_out('/Creator '.$this->_textstring($this->creator));
6290                         }
6291                         if (defined('PDF_PRODUCER')) {
6292                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
6293                         }
6294                         $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis')));
6295                         $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis')));       
6296                 }
6297                 
6298                 /**
6299                 * Output Catalog.
6300                 * @access protected
6301                 */
6302                 protected function _putcatalog() {
6303                         $this->_out('/Type /Catalog');
6304                         $this->_out('/Pages 1 0 R');
6305                         if ($this->ZoomMode == 'fullpage') {
6306                                 $this->_out('/OpenAction [3 0 R /Fit]');
6307                         } elseif ($this->ZoomMode == 'fullwidth') {
6308                                 $this->_out('/OpenAction [3 0 R /FitH null]');
6309                         } elseif ($this->ZoomMode == 'real') {
6310                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
6311                         } elseif (!is_string($this->ZoomMode)) {
6312                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
6313                         }
6314                         if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
6315                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
6316                         }
6317                         if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
6318                                 $this->_out('/PageMode /'.$this->PageMode);
6319                         }
6320                         if (isset($this->l['a_meta_language'])) {
6321                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
6322                         }
6323                         $this->_out('/Names <<');
6324                         if (!$this->empty_string($this->javascript)) {
6325                                 $this->_out('/JavaScript '.($this->n_js).' 0 R');
6326                         }
6327                         $this->_out('>>');
6328                         if (count($this->outlines) > 0) {
6329                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
6330                                 $this->_out('/PageMode /UseOutlines');
6331                         }
6332                         $this->_putviewerpreferences();
6333                         $p = $this->n_ocg_print.' 0 R';
6334                         $v = $this->n_ocg_view.' 0 R';
6335                         $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
6336                         $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
6337                 }
6338                 
6339                 /**
6340                 * Output viewer preferences.
6341                 * @author Nicola asuni
6342                 * @since 3.1.000 (2008-06-09)
6343                 * @access protected
6344                 */
6345                 protected function _putviewerpreferences() {
6346                         $this->_out('/ViewerPreferences<<');
6347                         if ($this->rtl) {
6348                                 $this->_out('/Direction /R2L');
6349                         } else {
6350                                 $this->_out('/Direction /L2R');
6351                         }
6352                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
6353                                 $this->_out('/HideToolbar true');
6354                         }
6355                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
6356                                 $this->_out('/HideMenubar true');
6357                         }
6358                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
6359                                 $this->_out('/HideWindowUI true');
6360                         }
6361                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
6362                                 $this->_out('/FitWindow true');
6363                         }
6364                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
6365                                 $this->_out('/CenterWindow true');
6366                         }
6367                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
6368                                 $this->_out('/DisplayDocTitle true');
6369                         }
6370                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
6371                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
6372                         }
6373                         if (isset($this->viewer_preferences['ViewArea'])) {
6374                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
6375                         }
6376                         if (isset($this->viewer_preferences['ViewClip'])) {
6377                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
6378                         }
6379                         if (isset($this->viewer_preferences['PrintArea'])) {
6380                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
6381                         }
6382                         if (isset($this->viewer_preferences['PrintClip'])) {
6383                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
6384                         }
6385                         if (isset($this->viewer_preferences['PrintScaling'])) {
6386                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
6387                         }
6388                         if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
6389                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
6390                         }
6391                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
6392                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
6393                                         $this->_out('/PickTrayByPDFSize true');
6394                                 } else {
6395                                         $this->_out('/PickTrayByPDFSize false');
6396                                 }
6397                         }
6398                         if (isset($this->viewer_preferences['PrintPageRange'])) {
6399                                 $PrintPageRangeNum = '';
6400                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
6401                                         $PrintPageRangeNum .= ' '.($v - 1).'';
6402                                 }
6403                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
6404                         }
6405                         if (isset($this->viewer_preferences['NumCopies'])) {
6406                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
6407                         }
6408                         $this->_out('>>');
6409                 }
6410
6411                 /**
6412                 * Output trailer.
6413                 * @access protected
6414                 */
6415                 protected function _puttrailer() {
6416                         $this->_out('/Size '.($this->n + 1));
6417                         $this->_out('/Root '.$this->n.' 0 R');
6418                         $this->_out('/Info '.($this->n - 1).' 0 R');
6419                         if ($this->encrypted) {
6420                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
6421                                 $this->_out('/ID [()()]');
6422                         }
6423                 }
6424
6425                 /**
6426                 * Output PDF header.
6427                 * @access protected
6428                 */
6429                 protected function _putheader() {
6430                         $this->_out('%PDF-'.$this->PDFVersion);
6431                 }
6432
6433                 /**
6434                 * Output end of document (EOF).
6435                 * @access protected
6436                 */
6437                 protected function _enddoc() {
6438                         $this->state = 1;
6439                         $this->_putheader();
6440                         $this->_putpages();
6441                         $this->_putresources();
6442                         //Info
6443                         $this->_newobj();
6444                         $this->_out('<<');
6445                         $this->_putinfo();
6446                         $this->_out('>>');
6447                         $this->_out('endobj');
6448                         //Catalog
6449                         $this->_newobj();
6450                         $this->_out('<<');
6451                         $this->_putcatalog();
6452                         $this->_putcertification();
6453                         $this->_putuserrights();
6454                         $this->_out('>>');
6455                         $this->_out('endobj');
6456                         //Cross-ref
6457                         $o = $this->bufferlen;
6458                         $this->_out('xref');
6459                         $this->_out('0 '.($this->n + 1));
6460                         $this->_out('0000000000 65535 f ');
6461                         for ($i=1; $i <= $this->n; ++$i) {
6462                                 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
6463                         }
6464                         if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
6465                                 $this->_out('100000 '.count($this->embeddedfiles));
6466                                 foreach ($this->embeddedfiles as $filename => $filedata) {
6467                                         $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
6468                                 }
6469                         }
6470                         //Trailer
6471                         $this->_out('trailer');
6472                         $this->_out('<<');
6473                         $this->_puttrailer();
6474                         $this->_out('>>');
6475                         $this->_out('startxref');
6476                         $this->_out($o);
6477                         $this->_out('%%EOF');
6478                         $this->state = 3; // end-of-doc
6479                         if ($this->diskcache) {
6480                                 // remove temporary files used for images
6481                                 foreach ($this->imagekeys as $key) {
6482                                         // remove temporary files
6483                                         unlink($this->images[$key]);
6484                                 }
6485                                 foreach ($this->fontkeys as $key) {
6486                                         // remove temporary files
6487                                         unlink($this->fonts[$key]);
6488                                 }
6489                         }
6490                 }
6491
6492                 /**
6493                 * Initialize a new page.
6494                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
6495                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
6496                 * @access protected
6497                 */
6498                 protected function _beginpage($orientation='', $format='') {
6499                         ++$this->page;
6500                         $this->setPageBuffer($this->page, '');
6501                         // initialize array for graphics tranformation positions inside a page buffer
6502                         $this->transfmrk[$this->page] = array();
6503                         $this->state = 2;
6504                         if ($this->empty_string($orientation)) {
6505                                 if (isset($this->CurOrientation)) {
6506                                         $orientation = $this->CurOrientation;
6507                                 } else {
6508                                         $orientation = 'P';
6509                                 }
6510                         }
6511                         if ($this->empty_string($format)) {
6512                                 $this->setPageOrientation($orientation);
6513                         } else {
6514                                 $this->setPageFormat($format, $orientation);
6515                         }
6516                         if ($this->rtl) {
6517                                 $this->x = $this->w - $this->rMargin;
6518                         } else {
6519                                 $this->x = $this->lMargin;
6520                         }
6521                         $this->y = $this->tMargin;
6522                         if (isset($this->newpagegroup[$this->page])) {
6523                                 // start a new group
6524                                 $n = sizeof($this->pagegroups) + 1;
6525                                 $alias = '{nb'.$n.'}';
6526                                 $this->pagegroups[$alias] = 1;
6527                                 $this->currpagegroup = $alias;
6528                         } elseif ($this->currpagegroup) {
6529                                 ++$this->pagegroups[$this->currpagegroup];
6530                         }
6531                 }
6532
6533                 /**
6534                 * Mark end of page.
6535                 * @access protected
6536                 */
6537                 protected function _endpage() {
6538                         $this->setVisibility('all');
6539                         $this->state = 1;
6540                 }
6541
6542                 /**
6543                 * Begin a new object.
6544                 * @access protected
6545                 */
6546                 protected function _newobj() {
6547                         ++$this->n;
6548                         $this->offsets[$this->n] = $this->bufferlen;
6549                         $this->_out($this->n.' 0 obj');
6550                 }
6551
6552                 /**
6553                 * Underline text.
6554                 * @param int $x X coordinate
6555                 * @param int $y Y coordinate
6556                 * @param string $txt text to underline
6557                 * @access protected
6558                 */
6559                 protected function _dounderline($x, $y, $txt) {
6560                         $up = $this->CurrentFont['up'];
6561                         $ut = $this->CurrentFont['ut'];
6562                         $w = $this->GetStringWidth($txt);
6563                         return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
6564                 }
6565                 
6566                 /**
6567                 * Line through text.
6568                 * @param int $x X coordinate
6569                 * @param int $y Y coordinate
6570                 * @param string $txt text to linethrough
6571                 * @access protected
6572                 */
6573                 protected function _dolinethrough($x, $y, $txt) {
6574                         $up = $this->CurrentFont['up'];
6575                         $ut = $this->CurrentFont['ut'];
6576                         $w = $this->GetStringWidth($txt);
6577                         return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - ($this->FontSize/2) - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt);
6578                 }
6579                 
6580                 /**
6581                 * Read a 4-byte integer from file.
6582                 * @param string $f file name.
6583                 * @return 4-byte integer
6584                 * @access protected
6585                 */
6586                 protected function _freadint($f) {
6587                         $a = unpack('Ni', fread($f, 4));
6588                         return $a['i'];
6589                 }
6590                 
6591                 /**
6592                 * Add "\" before "\", "(" and ")"
6593                 * @param string $s string to escape.
6594                 * @return string escaped string.
6595                 * @access protected
6596                 */
6597                 protected function _escape($s) {
6598                         // the chr(13) substitution fixes the Bugs item #1421290.
6599                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
6600                 }
6601                 
6602                 /**
6603                 * Format a date string for meta information
6604                 * @param string $s date string to escape.
6605                 * @return string escaped string.
6606                 * @access protected
6607                 */
6608                 protected function _datastring($s) {
6609                         if ($this->encrypted) {
6610                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
6611                         }
6612                         return '('. $this->_escape($s).')';
6613                 }
6614                 
6615                 /**
6616                 * Format a text string for meta information
6617                 * @param string $s string to escape.
6618                 * @return string escaped string.
6619                 * @access protected
6620                 */
6621                 protected function _textstring($s) {
6622                         if ($this->isunicode) {
6623                                 //Convert string to UTF-16BE
6624                                 $s = $this->UTF8ToUTF16BE($s, true);
6625                         }
6626                         return $this->_datastring($s);
6627                 }
6628                                 
6629                 /**
6630                 * Format a text string
6631                 * @param string $s string to escape.
6632                 * @return string escaped string.
6633                 * @access protected
6634                 */
6635                 protected function _escapetext($s) {
6636                         if ($this->isunicode) {
6637                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
6638                                         $s = $this->UTF8ToLatin1($s);
6639                                 } else {
6640                                         //Convert string to UTF-16BE and reverse RTL language
6641                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
6642                                 }
6643                         }
6644                         return $this->_escape($s);
6645                 }
6646                 
6647                 /**
6648                 * Output a stream.
6649                 * @param string $s string to output.
6650                 * @access protected
6651                 */
6652                 protected function _putstream($s) {
6653                         if ($this->encrypted) {
6654                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
6655                         }
6656                         $this->_out('stream');
6657                         $this->_out($s);
6658                         $this->_out('endstream');
6659                 }
6660                 
6661                 /**
6662                 * Output a string to the document.
6663                 * @param string $s string to output.
6664                 * @access protected
6665                 */
6666                 protected function _out($s) {
6667                         if ($this->state == 2) {
6668                                 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
6669                                         // puts data before page footer
6670                                         $pagebuff = $this->getPageBuffer($this->page);
6671                                         $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
6672                                         $footer = substr($pagebuff, -$this->footerlen[$this->page]);
6673                                         $this->setPageBuffer($this->page, $page.$s."\n".$footer);
6674                                         // update footer position
6675                                         $this->footerpos[$this->page] += strlen($s."\n");       
6676                                 } else {
6677                                         $this->setPageBuffer($this->page, $s."\n", true);
6678                                 }
6679                         } else {
6680                                 $this->setBuffer($s."\n");
6681                         }
6682                 }
6683                 
6684                  /**
6685                  * Converts UTF-8 strings to codepoints array.<br>
6686                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
6687                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
6688                  * <pre>
6689                  *        Char. number range  |        UTF-8 octet sequence
6690                  *       (hexadecimal)    |              (binary)
6691                  *    --------------------+-----------------------------------------------
6692                  *    0000 0000-0000 007F | 0xxxxxxx
6693                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
6694                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
6695                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
6696                  *    ---------------------------------------------------------------------
6697                  *
6698                  *   ABFN notation:
6699                  *   ---------------------------------------------------------------------
6700                  *   UTF8-octets = *( UTF8-char )
6701                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
6702                  *   UTF8-1      = %x00-7F
6703                  *   UTF8-2      = %xC2-DF UTF8-tail
6704                  *
6705                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
6706                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
6707                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
6708                  *                 %xF4 %x80-8F 2( UTF8-tail )
6709                  *   UTF8-tail   = %x80-BF
6710                  *   ---------------------------------------------------------------------
6711                  * </pre>
6712                  * @param string $str string to process.
6713                  * @return array containing codepoints (UTF-8 characters values)
6714                  * @access protected
6715                  * @author Nicola Asuni
6716                  * @since 1.53.0.TC005 (2005-01-05)
6717                  */
6718                 protected function UTF8StringToArray($str) {
6719                         if (isset($this->cache_UTF8StringToArray['_'.$str])) {
6720                                 // return cached value
6721                                 return($this->cache_UTF8StringToArray['_'.$str]);
6722                         }
6723                         // check cache size
6724                         if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
6725                                 // remove first element
6726                                 array_shift($this->cache_UTF8StringToArray);
6727                         }
6728                         ++$this->cache_size_UTF8StringToArray;
6729                         if (!$this->isunicode) {
6730                                 // split string into array of equivalent codes
6731                                 $strarr = array();
6732                                 $strlen = strlen($str);
6733                                 for ($i=0; $i < $strlen; ++$i) {
6734                                         $strarr[] = ord($str{$i});
6735                                 }
6736                                 // insert new value on cache
6737                                 $this->cache_UTF8StringToArray['_'.$str] = $strarr;
6738                                 return $strarr;
6739                         }
6740                         $unicode = array(); // array containing unicode values
6741                         $bytes  = array(); // array containing single character byte sequences
6742                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
6743                         $str .= ''; // force $str to be a string
6744                         $length = strlen($str);
6745                         for ($i = 0; $i < $length; ++$i) {
6746                                 $char = ord($str{$i}); // get one string character at time
6747                                 if (count($bytes) == 0) { // get starting octect
6748                                         if ($char <= 0x7F) {
6749                                                 $unicode[] = $char; // use the character "as is" because is ASCII
6750                                                 $numbytes = 1;
6751                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
6752                                                 $bytes[] = ($char - 0xC0) << 0x06; 
6753                                                 $numbytes = 2;
6754                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
6755                                                 $bytes[] = ($char - 0xE0) << 0x0C; 
6756                                                 $numbytes = 3;
6757                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
6758                                                 $bytes[] = ($char - 0xF0) << 0x12; 
6759                                                 $numbytes = 4;
6760                                         } else {
6761                                                 // use replacement character for other invalid sequences
6762                                                 $unicode[] = 0xFFFD;
6763                                                 $bytes = array();
6764                                                 $numbytes = 1;
6765                                         }
6766                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
6767                                         $bytes[] = $char - 0x80;
6768                                         if (count($bytes) == $numbytes) {
6769                                                 // compose UTF-8 bytes to a single unicode value
6770                                                 $char = $bytes[0];
6771                                                 for ($j = 1; $j < $numbytes; ++$j) {
6772                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
6773                                                 }
6774                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
6775                                                         /* The definition of UTF-8 prohibits encoding character numbers between
6776                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
6777                                                         encoding form (as surrogate pairs) and do not directly represent
6778                                                         characters. */
6779                                                         $unicode[] = 0xFFFD; // use replacement character
6780                                                 } else {
6781                                                         $unicode[] = $char; // add char to array
6782                                                 }
6783                                                 // reset data for next char
6784                                                 $bytes = array(); 
6785                                                 $numbytes = 1;
6786                                         }
6787                                 } else {
6788                                         // use replacement character for other invalid sequences
6789                                         $unicode[] = 0xFFFD;
6790                                         $bytes = array();
6791                                         $numbytes = 1;
6792                                 }
6793                         }
6794                         // insert new value on cache
6795                         $this->cache_UTF8StringToArray['_'.$str] = $unicode;
6796                         return $unicode;
6797                 }
6798                 
6799                 /**
6800                  * Converts UTF-8 strings to UTF16-BE.<br>
6801                  * @param string $str string to process.
6802                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
6803                  * @return string
6804                  * @access protected
6805                  * @author Nicola Asuni
6806                  * @since 1.53.0.TC005 (2005-01-05)
6807                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
6808                  */
6809                 protected function UTF8ToUTF16BE($str, $setbom=true) {
6810                         if (!$this->isunicode) {
6811                                 return $str; // string is not in unicode
6812                         }
6813                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6814                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
6815                 }
6816                 
6817                 /**
6818                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
6819                  * @param string $str string to process.
6820                  * @return string
6821                  * @author Andrew Whitehead, Nicola Asuni
6822                  * @access protected
6823                  * @since 3.2.000 (2008-06-23)
6824                  */
6825                 protected function UTF8ToLatin1($str) {
6826                         global $utf8tolatin;
6827                         if (!$this->isunicode) {
6828                                 return $str; // string is not in unicode
6829                         }
6830                         $outstr = ''; // string to be returned
6831                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6832                         foreach ($unicode as $char) {
6833                                 if ($char < 256) {
6834                                         $outstr .= chr($char);
6835                                 } elseif (array_key_exists($char, $utf8tolatin)) {
6836                                         // map from UTF-8
6837                                         $outstr .= chr($utf8tolatin[$char]);
6838                                 } elseif ($char == 0xFFFD) {
6839                                         // skip
6840                                 } else {
6841                                         $outstr .= '?';
6842                                 }
6843                         }
6844                         return $outstr;
6845                 }
6846
6847                 /**
6848                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
6849                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
6850                  * <pre>
6851                  *   Encoding UTF-16:
6852                  * 
6853                  *   Encoding of a single character from an ISO 10646 character value to
6854                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
6855                  *    than 0x10FFFF.
6856                  * 
6857                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
6858                  *       terminate.
6859                  * 
6860                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
6861                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
6862                  *       represented in 20 bits.
6863                  * 
6864                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
6865                  *       0xDC00, respectively. These integers each have 10 bits free to
6866                  *       encode the character value, for a total of 20 bits.
6867                  * 
6868                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
6869                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
6870                  *       bits of W2. Terminate.
6871                  * 
6872                  *    Graphically, steps 2 through 4 look like:
6873                  *    U' = yyyyyyyyyyxxxxxxxxxx
6874                  *    W1 = 110110yyyyyyyyyy
6875                  *    W2 = 110111xxxxxxxxxx
6876                  * </pre>
6877                  * @param array $unicode array containing UTF-8 unicode values
6878                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
6879                  * @return string
6880                  * @access protected
6881                  * @author Nicola Asuni
6882                  * @since 2.1.000 (2008-01-08)
6883                  * @see UTF8ToUTF16BE()
6884                  */
6885                 protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
6886                         $outstr = ''; // string to be returned
6887                         if ($setbom) {
6888                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
6889                         }
6890                         foreach ($unicode as $char) {
6891                                 if ($char == 0xFFFD) {
6892                                         $outstr .= "\xFF\xFD"; // replacement character
6893                                 } elseif ($char < 0x10000) {
6894                                         $outstr .= chr($char >> 0x08);
6895                                         $outstr .= chr($char & 0xFF);
6896                                 } else {
6897                                         $char -= 0x10000;
6898                                         $w1 = 0xD800 | ($char >> 0x10);
6899                                         $w2 = 0xDC00 | ($char & 0x3FF); 
6900                                         $outstr .= chr($w1 >> 0x08);
6901                                         $outstr .= chr($w1 & 0xFF);
6902                                         $outstr .= chr($w2 >> 0x08);
6903                                         $outstr .= chr($w2 & 0xFF);
6904                                 }
6905                         }
6906                         return $outstr;
6907                 }
6908                 // ====================================================
6909                 
6910                 /**
6911                  * Set header font.
6912                  * @param array $font font
6913                  * @access public
6914                  * @since 1.1
6915                  */
6916                 public function setHeaderFont($font) {
6917                         $this->header_font = $font;
6918                 }
6919                 
6920                 /**
6921                  * Get header font.
6922                  * @return array()
6923                  * @access public
6924                  * @since 4.0.012 (2008-07-24)
6925                  */
6926                 public function getHeaderFont() {
6927                         return $this->header_font;
6928                 }
6929                 
6930                 /**
6931                  * Set footer font.
6932                  * @param array $font font
6933                  * @access public
6934                  * @since 1.1
6935                  */
6936                 public function setFooterFont($font) {
6937                         $this->footer_font = $font;
6938                 }
6939                 
6940                 /**
6941                  * Get Footer font.
6942                  * @return array()
6943                  * @access public
6944                  * @since 4.0.012 (2008-07-24)
6945                  */
6946                 public function getFooterFont() {
6947                         return $this->footer_font;
6948                 }
6949                 
6950                 /**
6951                  * Set language array.
6952                  * @param array $language
6953                  * @access public
6954                  * @since 1.1
6955                  */
6956                 public function setLanguageArray($language) {
6957                         $this->l = $language;
6958                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
6959                 }
6960                 
6961                 /**
6962                  * Returns the PDF data.
6963                  * @access public
6964                  */
6965                 public function getPDFData() {
6966                         if ($this->state < 3) {
6967                                 $this->Close();
6968                         }
6969                         return $this->buffer;
6970                 }
6971                                 
6972                 /**
6973                  * Output anchor link.
6974                  * @param string $url link URL or internal link (i.e.: &lt;a href="#23"&gt;link to page 23&lt;/a&gt;)
6975                  * @param string $name link name
6976                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
6977                  * @param boolean $firstline if true prints only the first line and return the remaining string.
6978                  * @param array $color array of RGB text color
6979                  * @param string $style font style (U, D, B, I)
6980                  * @return the number of cells used or the remaining text if $firstline = true;
6981                  * @access public
6982                  */
6983                 public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
6984                         if (!$this->empty_string($url) AND ($url{0} == '#')) {
6985                                 // convert url to internal link
6986                                 $page = intval(substr($url, 1));
6987                                 $url = $this->AddLink();
6988                                 $this->SetLink($url, 0, $page);
6989                         }
6990                         // store current settings
6991                         $prevcolor = $this->fgcolor;
6992                         $prevstyle = $this->FontStyle;
6993                         if (empty($color)) {
6994                                 $this->SetTextColorArray($this->htmlLinkColorArray);
6995                         } else {
6996                                 $this->SetTextColorArray($color);
6997                         }
6998                         if ($style == -1) {
6999                                 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
7000                         } else {
7001                                 $this->SetFont('', $this->FontStyle.$style);
7002                         }
7003                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
7004                         // restore settings
7005                         $this->SetFont('', $prevstyle);
7006                         $this->SetTextColorArray($prevcolor);
7007                         return $ret;
7008                 }
7009                 
7010                 /**
7011                  * Returns an associative array (keys: R,G,B) from an html color name or a six-digit or three-digit hexadecimal color representation (i.e. #3FE5AA or #7FF).
7012                  * @param string $color html color 
7013                  * @return array RGB color or false in case of error.
7014                  * @access public
7015                  */             
7016                 public function convertHTMLColorToDec($color='#FFFFFF') {
7017                         global $webcolor;
7018                         $returncolor = false;
7019                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
7020                         $color = strtolower($color);
7021                         if (strlen($color) == 0) {
7022                                 return false;
7023                         }
7024                         if (substr($color, 0, 3) == 'rgb') {
7025                                 $codes = substr($color, 4);
7026                                 $codes = str_replace(')', '', $codes);
7027                                 $returncolor = explode(',', $codes, 3);
7028                                 return $returncolor;
7029                         }
7030                         if (substr($color, 0, 1) != '#') {
7031                                 // decode color name
7032                                 if (isset($webcolor[$color])) {
7033                                         $color_code = $webcolor[$color];
7034                                 } else {
7035                                         return false;
7036                                 }
7037                         } else {
7038                                 $color_code = substr($color, 1);
7039                         }
7040                         switch (strlen($color_code)) {
7041                                 case 3: {
7042                                         // three-digit hexadecimal representation
7043                                         $r = substr($color_code, 0, 1);
7044                                         $g = substr($color_code, 1, 1);
7045                                         $b = substr($color_code, 2, 1);
7046                                         $returncolor['R'] = hexdec($r.$r);
7047                                         $returncolor['G'] = hexdec($g.$g);
7048                                         $returncolor['B'] = hexdec($b.$b);
7049                                         break;
7050                                 }
7051                                 case 6: {
7052                                         // six-digit hexadecimal representation
7053                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
7054                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
7055                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
7056                                         break;
7057                                 }
7058                         }
7059                         return $returncolor;
7060                 }
7061                 
7062                 /**
7063                  * Converts pixels to User's Units.
7064                  * @param int $px pixels
7065                  * @return float value in user's unit
7066                  * @access public
7067                  * @see setImageScale(), getImageScale()
7068                  */
7069                 public function pixelsToUnits($px) {
7070                         return ($px / ($this->imgscale * $this->k));
7071                 }
7072                         
7073                 /**
7074                  * Reverse function for htmlentities. 
7075                  * Convert entities in UTF-8.
7076                  * @param $text_to_convert Text to convert.
7077                  * @return string converted
7078                  * @access public
7079                 */
7080                 public function unhtmlentities($text_to_convert) {
7081                         return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
7082                 }
7083                 
7084                 // ENCRYPTION METHODS ----------------------------------
7085                 // SINCE 2.0.000 (2008-01-02)
7086                 
7087                 /**
7088                 * Compute encryption key depending on object number where the encrypted data is stored
7089                 * @param int $n object number
7090                 * @access protected
7091                 * @since 2.0.000 (2008-01-02)
7092                 */
7093                 protected function _objectkey($n) {
7094                         return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
7095                 }
7096                 
7097                 /**
7098                  * Put encryption on PDF document.
7099                  * @access protected
7100                  * @since 2.0.000 (2008-01-02)
7101                  */
7102                 protected function _putencryption() {
7103                         $this->_out('/Filter /Standard');
7104                         $this->_out('/V 1');
7105                         $this->_out('/R 2');
7106                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
7107                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
7108                         $this->_out('/P '.$this->Pvalue);
7109                 }
7110                 
7111                 /**
7112                 * Returns the input text exrypted using RC4 algorithm and the specified key.
7113                 * RC4 is the standard encryption algorithm used in PDF format
7114                 * @param string $key encryption key
7115                 * @param String $text input text to be encrypted
7116                 * @return String encrypted text
7117                 * @access protected
7118                 * @since 2.0.000 (2008-01-02)
7119                 * @author Klemen Vodopivec
7120                 */
7121                 protected function _RC4($key, $text) {
7122                         if ($this->last_rc4_key != $key) {
7123                                 $k = str_repeat($key, ((256 / strlen($key)) + 1));
7124                                 $rc4 = range(0, 255);
7125                                 $j = 0;
7126                                 for ($i = 0; $i < 256; ++$i) {
7127                                         $t = $rc4[$i];
7128                                         $j = ($j + $t + ord($k{$i})) % 256;
7129                                         $rc4[$i] = $rc4[$j];
7130                                         $rc4[$j] = $t;
7131                                 }
7132                                 $this->last_rc4_key = $key;
7133                                 $this->last_rc4_key_c = $rc4;
7134                         } else {
7135                                 $rc4 = $this->last_rc4_key_c;
7136                         }
7137                         $len = strlen($text);
7138                         $a = 0;
7139                         $b = 0;
7140                         $out = '';
7141                         for ($i = 0; $i < $len; ++$i) {
7142                                 $a = ($a + 1) % 256;
7143                                 $t = $rc4[$a];
7144                                 $b = ($b + $t) % 256;
7145                                 $rc4[$a] = $rc4[$b];
7146                                 $rc4[$b] = $t;
7147                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
7148                                 $out .= chr(ord($text{$i}) ^ $k);
7149                         }
7150                         return $out;
7151                 }
7152                 
7153                 /**
7154                 * Encrypts a string using MD5 and returns it's value as a binary string.
7155                 * @param string $str input string
7156                 * @return String MD5 encrypted binary string
7157                 * @access protected
7158                 * @since 2.0.000 (2008-01-02)
7159                 * @author Klemen Vodopivec
7160                 */
7161                 protected function _md5_16($str) {
7162                         return pack('H*', md5($str));
7163                 }
7164                 
7165                 /**
7166                 * Compute O value (used for RC4 encryption)
7167                 * @param String $user_pass user password
7168                 * @param String $owner_pass user password
7169                 * @return String O value
7170                 * @access protected
7171                 * @since 2.0.000 (2008-01-02)
7172                 * @author Klemen Vodopivec
7173                 */
7174                 protected function _Ovalue($user_pass, $owner_pass) {
7175                         $tmp = $this->_md5_16($owner_pass);
7176                         $owner_RC4_key = substr($tmp, 0, 5);
7177                         return $this->_RC4($owner_RC4_key, $user_pass);
7178                 }
7179                 
7180                 /**
7181                 * Compute U value (used for RC4 encryption)
7182                 * @return String U value
7183                 * @access protected
7184                 * @since 2.0.000 (2008-01-02)
7185                 * @author Klemen Vodopivec
7186                 */
7187                 protected function _Uvalue() {
7188                         return $this->_RC4($this->encryption_key, $this->padding);
7189                 }
7190                 
7191                 /**
7192                 * Compute encryption key
7193                 * @param String $user_pass user password
7194                 * @param String $owner_pass user password
7195                 * @param String $protection protection type
7196                 * @access protected
7197                 * @since 2.0.000 (2008-01-02)
7198                 * @author Klemen Vodopivec
7199                 */
7200                 protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
7201                         // Pad passwords
7202                         $user_pass = substr($user_pass.$this->padding, 0, 32);
7203                         $owner_pass = substr($owner_pass.$this->padding, 0, 32);
7204                         // Compute O value
7205                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
7206                         // Compute encyption key
7207                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
7208                         $this->encryption_key = substr($tmp, 0, 5);
7209                         // Compute U value
7210                         $this->Uvalue = $this->_Uvalue();
7211                         // Compute P value
7212                         $this->Pvalue = -(($protection^255) + 1);
7213                 }
7214                 
7215                 /**
7216                 * Set document protection
7217                 * The permission array is composed of values taken from the following ones:
7218                 * - copy: copy text and images to the clipboard
7219                 * - print: print the document
7220                 * - modify: modify it (except for annotations and forms)
7221                 * - annot-forms: add annotations and forms 
7222                 * Remark: the protection against modification is for people who have the full Acrobat product.
7223                 * If you don't set any password, the document will open as usual. If you set a user password, the PDF viewer will ask for it before displaying the document. The master password, if different from the user one, can be used to get full access.
7224                 * Note: protecting a document requires to encrypt it, which increases the processing time a lot. This can cause a PHP time-out in some cases, especially if the document contains images or fonts.
7225                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
7226                 * @param String $user_pass user password. Empty by default.
7227                 * @param String $owner_pass owner password. If not specified, a random value is used.
7228                 * @access public
7229                 * @since 2.0.000 (2008-01-02)
7230                 * @author Klemen Vodopivec
7231                 */
7232                 public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
7233                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
7234                         $protection = 192;
7235                         foreach ($permissions as $permission) {
7236                                 if (!isset($options[$permission])) {
7237                                         $this->Error('Incorrect permission: '.$permission);
7238                                 }
7239                                 $protection += $options[$permission];
7240                         }
7241                         if ($owner_pass === null) {
7242                                 $owner_pass = uniqid(rand());
7243                         }
7244                         $this->encrypted = true;
7245                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
7246                 }
7247                 
7248                 // END OF ENCRYPTION FUNCTIONS -------------------------
7249                 
7250                 // START TRANSFORMATIONS SECTION -----------------------
7251                 
7252                 /**
7253                 * Starts a 2D tranformation saving current graphic state.
7254                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
7255                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
7256                 * @access public
7257                 * @since 2.1.000 (2008-01-07)
7258                 * @see StartTransform(), StopTransform()
7259                 */
7260                 public function StartTransform() {
7261                         $this->_out('q');
7262                         $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
7263                 }
7264                 
7265                 /**
7266                 * Stops a 2D tranformation restoring previous graphic state.
7267                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
7268                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
7269                 * @access public
7270                 * @since 2.1.000 (2008-01-07)
7271                 * @see StartTransform(), StopTransform()
7272                 */
7273                 public function StopTransform() {
7274                         $this->_out('Q');
7275                         if (isset($this->transfmatrix)) {
7276                                 array_pop($this->transfmatrix);
7277                         }
7278                         array_pop($this->transfmrk[$this->page]);
7279                 }
7280                 /**
7281                 * Horizontal Scaling.
7282                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
7283                 * @param int $x abscissa of the scaling center. Default is current x position
7284                 * @param int $y ordinate of the scaling center. Default is current y position
7285                 * @access public
7286                 * @since 2.1.000 (2008-01-07)
7287                 * @see StartTransform(), StopTransform()
7288                 */
7289                 public function ScaleX($s_x, $x='', $y='') {
7290                         $this->Scale($s_x, 100, $x, $y);
7291                 }
7292                 
7293                 /**
7294                 * Vertical Scaling.
7295                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
7296                 * @param int $x abscissa of the scaling center. Default is current x position
7297                 * @param int $y ordinate of the scaling center. Default is current y position
7298                 * @access public
7299                 * @since 2.1.000 (2008-01-07)
7300                 * @see StartTransform(), StopTransform()
7301                 */
7302                 public function ScaleY($s_y, $x='', $y='') {
7303                         $this->Scale(100, $s_y, $x, $y);
7304                 }
7305                 
7306                 /**
7307                 * Vertical and horizontal proportional Scaling.
7308                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
7309                 * @param int $x abscissa of the scaling center. Default is current x position
7310                 * @param int $y ordinate of the scaling center. Default is current y position
7311                 * @access public
7312                 * @since 2.1.000 (2008-01-07)
7313                 * @see StartTransform(), StopTransform()
7314                 */
7315                 public function ScaleXY($s, $x='', $y='') {
7316                         $this->Scale($s, $s, $x, $y);
7317                 }
7318                 
7319                 /**
7320                 * Vertical and horizontal non-proportional Scaling.
7321                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
7322                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
7323                 * @param int $x abscissa of the scaling center. Default is current x position
7324                 * @param int $y ordinate of the scaling center. Default is current y position
7325                 * @access public
7326                 * @since 2.1.000 (2008-01-07)
7327                 * @see StartTransform(), StopTransform()
7328                 */
7329                 public function Scale($s_x, $s_y, $x='', $y='') {
7330                         if ($x === '') {
7331                                 $x = $this->x;
7332                         }
7333                         if ($y === '') {
7334                                 $y = $this->y;
7335                         }
7336                         if ($this->rtl) {
7337                                 $x = $this->w - $x;
7338                         }
7339                         if (($s_x == 0) OR ($s_y == 0)) {
7340                                 $this->Error('Please do not use values equal to zero for scaling');
7341                         }
7342                         $y = ($this->h - $y) * $this->k;
7343                         $x *= $this->k;
7344                         //calculate elements of transformation matrix
7345                         $s_x /= 100;
7346                         $s_y /= 100;
7347                         $tm[0] = $s_x;
7348                         $tm[1] = 0;
7349                         $tm[2] = 0;
7350                         $tm[3] = $s_y;
7351                         $tm[4] = $x * (1 - $s_x);
7352                         $tm[5] = $y * (1 - $s_y);
7353                         //scale the coordinate system
7354                         $this->Transform($tm);
7355                 }
7356                 
7357                 /**
7358                 * Horizontal Mirroring.
7359                 * @param int $x abscissa of the point. Default is current x position
7360                 * @access public
7361                 * @since 2.1.000 (2008-01-07)
7362                 * @see StartTransform(), StopTransform()
7363                 */
7364                 public function MirrorH($x='') {
7365                         $this->Scale(-100, 100, $x);
7366                 }
7367                 
7368                 /**
7369                 * Verical Mirroring.
7370                 * @param int $y ordinate of the point. Default is current y position
7371                 * @access public
7372                 * @since 2.1.000 (2008-01-07)
7373                 * @see StartTransform(), StopTransform()
7374                 */
7375                 public function MirrorV($y='') {
7376                         $this->Scale(100, -100, '', $y);
7377                 }
7378                 
7379                 /**
7380                 * Point reflection mirroring.
7381                 * @param int $x abscissa of the point. Default is current x position
7382                 * @param int $y ordinate of the point. Default is current y position
7383                 * @access public
7384                 * @since 2.1.000 (2008-01-07)
7385                 * @see StartTransform(), StopTransform()
7386                 */
7387                 public function MirrorP($x='',$y='') {
7388                         $this->Scale(-100, -100, $x, $y);
7389                 }
7390                 
7391                 /**
7392                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
7393                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
7394                 * @param int $x abscissa of the point. Default is current x position
7395                 * @param int $y ordinate of the point. Default is current y position
7396                 * @access public
7397                 * @since 2.1.000 (2008-01-07)
7398                 * @see StartTransform(), StopTransform()
7399                 */
7400                 public function MirrorL($angle=0, $x='',$y='') {
7401                         $this->Scale(-100, 100, $x, $y);
7402                         $this->Rotate(-2*($angle-90), $x, $y);
7403                 }
7404                 
7405                 /**
7406                 * Translate graphic object horizontally.
7407                 * @param int $t_x movement to the right (or left for RTL)
7408                 * @access public
7409                 * @since 2.1.000 (2008-01-07)
7410                 * @see StartTransform(), StopTransform()
7411                 */
7412                 public function TranslateX($t_x) {
7413                         $this->Translate($t_x, 0);
7414                 }
7415                 
7416                 /**
7417                 * Translate graphic object vertically.
7418                 * @param int $t_y movement to the bottom
7419                 * @access public
7420                 * @since 2.1.000 (2008-01-07)
7421                 * @see StartTransform(), StopTransform()
7422                 */
7423                 public function TranslateY($t_y) {
7424                         $this->Translate(0, $t_y);
7425                 }
7426                 
7427                 /**
7428                 * Translate graphic object horizontally and vertically.
7429                 * @param int $t_x movement to the right
7430                 * @param int $t_y movement to the bottom
7431                 * @access public
7432                 * @since 2.1.000 (2008-01-07)
7433                 * @see StartTransform(), StopTransform()
7434                 */
7435                 public function Translate($t_x, $t_y) {
7436                         if ($this->rtl) {
7437                                 $t_x = -$t_x;
7438                         }
7439                         //calculate elements of transformation matrix
7440                         $tm[0] = 1;
7441                         $tm[1] = 0;
7442                         $tm[2] = 0;
7443                         $tm[3] = 1;
7444                         $tm[4] = $t_x * $this->k;
7445                         $tm[5] = -$t_y * $this->k;
7446                         //translate the coordinate system
7447                         $this->Transform($tm);
7448                 }
7449                 
7450                 /**
7451                 * Rotate object.
7452                 * @param float $angle angle in degrees for counter-clockwise rotation
7453                 * @param int $x abscissa of the rotation center. Default is current x position
7454                 * @param int $y ordinate of the rotation center. Default is current y position
7455                 * @access public
7456                 * @since 2.1.000 (2008-01-07)
7457                 * @see StartTransform(), StopTransform()
7458                 */
7459                 public function Rotate($angle, $x='', $y='') {
7460                         if ($x === '') {
7461                                 $x = $this->x;
7462                         }
7463                         if ($y === '') {
7464                                 $y = $this->y;
7465                         }
7466                         if ($this->rtl) {
7467                                 $x = $this->w - $x;
7468                                 $angle = -$angle;
7469                         }
7470                         $y = ($this->h - $y) * $this->k;
7471                         $x *= $this->k;
7472                         //calculate elements of transformation matrix
7473                         $tm[0] = cos(deg2rad($angle));
7474                         $tm[1] = sin(deg2rad($angle));
7475                         $tm[2] = -$tm[1];
7476                         $tm[3] = $tm[0];
7477                         $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
7478                         $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
7479                         //rotate the coordinate system around ($x,$y)
7480                         $this->Transform($tm);
7481                 }
7482                 
7483                 /**
7484                 * Skew horizontally.
7485                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
7486                 * @param int $x abscissa of the skewing center. default is current x position
7487                 * @param int $y ordinate of the skewing center. default is current y position
7488                 * @access public
7489                 * @since 2.1.000 (2008-01-07)
7490                 * @see StartTransform(), StopTransform()
7491                 */
7492                 public function SkewX($angle_x, $x='', $y='') {
7493                         $this->Skew($angle_x, 0, $x, $y);
7494                 }
7495                 
7496                 /**
7497                 * Skew vertically.
7498                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
7499                 * @param int $x abscissa of the skewing center. default is current x position
7500                 * @param int $y ordinate of the skewing center. default is current y position
7501                 * @access public
7502                 * @since 2.1.000 (2008-01-07)
7503                 * @see StartTransform(), StopTransform()
7504                 */
7505                 public function SkewY($angle_y, $x='', $y='') {
7506                         $this->Skew(0, $angle_y, $x, $y);
7507                 }
7508                 
7509                 /**
7510                 * Skew.
7511                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
7512                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
7513                 * @param int $x abscissa of the skewing center. default is current x position
7514                 * @param int $y ordinate of the skewing center. default is current y position
7515                 * @access public
7516                 * @since 2.1.000 (2008-01-07)
7517                 * @see StartTransform(), StopTransform()
7518                 */
7519                 public function Skew($angle_x, $angle_y, $x='', $y='') {
7520                         if ($x === '') {
7521                                 $x = $this->x;
7522                         }
7523                         if ($y === '') {
7524                                 $y = $this->y;
7525                         }
7526                         if ($this->rtl) {
7527                                 $x = $this->w - $x;
7528                                 $angle_x = -$angle_x;
7529                         }
7530                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
7531                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
7532                         }
7533                         $x *= $this->k;
7534                         $y = ($this->h - $y) * $this->k;
7535                         //calculate elements of transformation matrix
7536                         $tm[0] = 1;
7537                         $tm[1] = tan(deg2rad($angle_y));
7538                         $tm[2] = tan(deg2rad($angle_x));
7539                         $tm[3] = 1;
7540                         $tm[4] = -$tm[2] * $y;
7541                         $tm[5] = -$tm[1] * $x;
7542                         //skew the coordinate system
7543                         $this->Transform($tm);
7544                 }
7545                 
7546                 /**
7547                 * Apply graphic transformations.
7548                 * @access protected
7549                 * @since 2.1.000 (2008-01-07)
7550                 * @see StartTransform(), StopTransform()
7551                 */
7552                 protected function Transform($tm) {
7553                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
7554                         // store transformation matrix
7555                         $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
7556                         // update tranformation mark
7557                         if (end($this->transfmrk[$this->page]) !== false) {
7558                                 $key = key($this->transfmrk[$this->page]);
7559                                 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
7560                         }
7561                 }
7562                 
7563                 // END TRANSFORMATIONS SECTION -------------------------
7564                 
7565                 
7566                 // START GRAPHIC FUNCTIONS SECTION ---------------------
7567                 // The following section is based on the code provided by David Hernandez Sanz
7568                 
7569                 /**
7570                 * Defines the line width. By default, the value equals 0.2 mm. The method can be called before the first page is created and the value is retained from page to page.
7571                 * @param float $width The width.
7572                 * @access public
7573                 * @since 1.0
7574                 * @see Line(), Rect(), Cell(), MultiCell()
7575                 */
7576                 public function SetLineWidth($width) {
7577                         //Set line width
7578                         $this->LineWidth = $width;
7579                         $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
7580                         $this->_out($this->linestyleWidth);
7581                 }
7582                 
7583                 /**
7584                 * Returns the current the line width.
7585                 * @return int Line width 
7586                 * @access public
7587                 * @since 2.1.000 (2008-01-07)
7588                 * @see Line(), SetLineWidth()
7589                 */
7590                 public function GetLineWidth() {
7591                         return $this->LineWidth;
7592                 }
7593                 
7594                 /**
7595                 * Set line style.
7596                 * @param array $style Line style. Array with keys among the following:
7597                 * <ul>
7598                 *        <li>width (float): Width of the line in user units.</li>
7599                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
7600                 * butt, round, square. The difference between "square" and "butt" is that
7601                 * "square" projects a flat end past the end of the line.</li>
7602                 *        <li>join (string): Type of join. Possible values are: miter, round,
7603                 * bevel.</li>
7604                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
7605                 * series of length values, which are the lengths of the on and off dashes.
7606                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
7607                 * 1 off, 2 on, 1 off, ...</li>
7608                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
7609                 * the point at which the pattern starts.</li>
7610                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
7611                 * </ul>
7612                 * @access public
7613                 * @since 2.1.000 (2008-01-08)
7614                 */
7615                 public function SetLineStyle($style) {
7616                         extract($style);
7617                         if (isset($width)) {
7618                                 $width_prev = $this->LineWidth;
7619                                 $this->SetLineWidth($width);
7620                                 $this->LineWidth = $width_prev;
7621                         }
7622                         if (isset($cap)) {
7623                                 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
7624                                 if (isset($ca[$cap])) {
7625                                         $this->linestyleCap = $ca[$cap].' J';
7626                                         $this->_out($this->linestyleCap);
7627                                 }
7628                         }
7629                         if (isset($join)) {
7630                                 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
7631                                 if (isset($ja[$join])) {
7632                                         $this->linestyleJoin = $ja[$join].' j';
7633                                         $this->_out($this->linestyleJoin);
7634                                 }
7635                         }
7636                         if (isset($dash)) {
7637                                 $dash_string = '';
7638                                 if ($dash) {
7639                                         if (preg_match('/^.+,/', $dash)) {
7640                                                 $tab = explode(',', $dash);
7641                                         } else {
7642                                                 $tab = array($dash);
7643                                         }
7644                                         $dash_string = '';
7645                                         foreach ($tab as $i => $v) {
7646                                                 if ($i) {
7647                                                         $dash_string .= ' ';
7648                                                 }
7649                                                 $dash_string .= sprintf("%.2F", $v);
7650                                         }
7651                                 }
7652                                 if (!isset($phase) OR !$dash) {
7653                                         $phase = 0;
7654                                 }
7655                                 $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
7656                                 $this->_out($this->linestyleDash);
7657                         }
7658                         if (isset($color)) {
7659                                 $this->SetDrawColorArray($color);
7660                         }
7661                 }
7662                 
7663                 /*
7664                 * Set a draw point.
7665                 * @param float $x Abscissa of point.
7666                 * @param float $y Ordinate of point.
7667                 * @access protected
7668                 * @since 2.1.000 (2008-01-08)
7669                 */
7670                 protected function _outPoint($x, $y) {
7671                         if ($this->rtl) {
7672                                 $x = $this->w - $x;
7673                         }
7674                         $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
7675                 }
7676                 
7677                 /*
7678                 * Draws a line from last draw point.
7679                 * @param float $x Abscissa of end point.
7680                 * @param float $y Ordinate of end point.
7681                 * @access protected
7682                 * @since 2.1.000 (2008-01-08)
7683                 */
7684                 protected function _outLine($x, $y) {
7685                         if ($this->rtl) {
7686                                 $x = $this->w - $x;
7687                         }
7688                         $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
7689                 }
7690                 
7691                 /**
7692                 * Draws a rectangle.
7693                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
7694                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
7695                 * @param float $w Width.
7696                 * @param float $h Height.
7697                 * @param string $op options
7698                 * @access protected
7699                 * @since 2.1.000 (2008-01-08)
7700                 */
7701                 protected function _outRect($x, $y, $w, $h, $op) {
7702                         if ($this->rtl) {
7703                                 $x = $this->w - $x - $w;
7704                         }
7705                         $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
7706                 }
7707                 
7708                 /*
7709                 * Draws a Bezier curve from last draw point.
7710                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
7711                 * @param float $x1 Abscissa of control point 1.
7712                 * @param float $y1 Ordinate of control point 1.
7713                 * @param float $x2 Abscissa of control point 2.
7714                 * @param float $y2 Ordinate of control point 2.
7715                 * @param float $x3 Abscissa of end point.
7716                 * @param float $y3 Ordinate of end point.
7717                 * @access protected
7718                 * @since 2.1.000 (2008-01-08)
7719                 */
7720                 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
7721                         if ($this->rtl) {
7722                                 $x1 = $this->w - $x1;
7723                                 $x2 = $this->w - $x2;
7724                                 $x3 = $this->w - $x3;
7725                         }
7726                         $this->_out(sprintf("%.2F %.2F %.2F %.2F %.2F %.2F c", $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
7727                 }
7728                 
7729                 /**
7730                 * Draws a line between two points.
7731                 * @param float $x1 Abscissa of first point.
7732                 * @param float $y1 Ordinate of first point.
7733                 * @param float $x2 Abscissa of second point.
7734                 * @param float $y2 Ordinate of second point.
7735                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7736                 * @access public
7737                 * @since 1.0
7738                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
7739                 */
7740                 public function Line($x1, $y1, $x2, $y2, $style=array()) {
7741                         if ($style) {
7742                                 $this->SetLineStyle($style);
7743                         }
7744                         $this->_outPoint($x1, $y1);
7745                         $this->_outLine($x2, $y2);
7746                         $this->_out(' S');
7747                 }
7748                 
7749                 /**
7750                 * Draws a rectangle.
7751                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
7752                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
7753                 * @param float $w Width.
7754                 * @param float $h Height.
7755                 * @param string $style Style of rendering. Possible values are:
7756                 * <ul>
7757                 *        <li>D or empty string: Draw (default).</li>
7758                 *        <li>F: Fill.</li>
7759                 *        <li>DF or FD: Draw and fill.</li>
7760                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7761                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7762                 * </ul>
7763                 * @param array $border_style Border style of rectangle. Array with keys among the following:
7764                 * <ul>
7765                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
7766                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
7767                 * </ul>
7768                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
7769                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7770                 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
7771                 * @access public
7772                 * @since 1.0
7773                 * @see SetLineStyle()
7774                 */
7775                 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
7776                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7777                                 $this->SetFillColorArray($fill_color);
7778                         }
7779                         switch ($style) {
7780                                 case 'F': {
7781                                         $op = 'f';
7782                                         $border_style = array();
7783                                         $this->_outRect($x, $y, $w, $h, $op);
7784                                         break;
7785                                 }
7786                                 case 'DF':
7787                                 case 'FD': {
7788                                         if ((!$border_style) OR (isset($border_style['all']))) {
7789                                                 $op = 'B';
7790                                                 if (isset($border_style['all'])) {
7791                                                         $this->SetLineStyle($border_style['all']);
7792                                                         $border_style = array();
7793                                                 }
7794                                         } else {
7795                                                 $op = 'f';
7796                                         }
7797                                         $this->_outRect($x, $y, $w, $h, $op);
7798                                         break;
7799                                 }
7800                                 case 'CNZ': {
7801                                         $op = 'W n';
7802                                         $this->_outRect($x, $y, $w, $h, $op);
7803                                         break;
7804                                 }
7805                                 case 'CEO': {
7806                                         $op = 'W* n';
7807                                         $this->_outRect($x, $y, $w, $h, $op);
7808                                         break;
7809                                 }
7810                                 default: {
7811                                         $op = 'S';
7812                                         if ((!$border_style) OR (isset($border_style['all']))) {
7813                                                 if (isset($border_style['all']) AND $border_style['all']) {
7814                                                         $this->SetLineStyle($border_style['all']);
7815                                                         $border_style = array();
7816                                                 }
7817                                                 $this->_outRect($x, $y, $w, $h, $op);
7818                                         }
7819                                         break;
7820                                 }
7821                         }
7822                         if ($border_style) {
7823                                 $border_style2 = array();
7824                                 foreach ($border_style as $line => $value) {
7825                                         $lenght = strlen($line);
7826                                         for ($i = 0; $i < $lenght; ++$i) {
7827                                                 $border_style2[$line[$i]] = $value;
7828                                         }
7829                                 }
7830                                 $border_style = $border_style2;
7831                                 if (isset($border_style['L']) AND $border_style['L']) {
7832                                         $this->Line($x, $y, $x, $y + $h, $border_style['L']);
7833                                 }
7834                                 if (isset($border_style['T']) AND $border_style['T']) {
7835                                         $this->Line($x, $y, $x + $w, $y, $border_style['T']);
7836                                 }
7837                                 if (isset($border_style['R']) AND $border_style['R']) {
7838                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
7839                                 }
7840                                 if (isset($border_style['B']) AND $border_style['B']) {
7841                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
7842                                 }
7843                         }
7844                 }
7845                 
7846                 
7847                 /**
7848                 * Draws a Bezier curve.
7849                 * The Bezier curve is a tangent to the line between the control points at
7850                 * either end of the curve.
7851                 * @param float $x0 Abscissa of start point.
7852                 * @param float $y0 Ordinate of start point.
7853                 * @param float $x1 Abscissa of control point 1.
7854                 * @param float $y1 Ordinate of control point 1.
7855                 * @param float $x2 Abscissa of control point 2.
7856                 * @param float $y2 Ordinate of control point 2.
7857                 * @param float $x3 Abscissa of end point.
7858                 * @param float $y3 Ordinate of end point.
7859                 * @param string $style Style of rendering. Possible values are:
7860                 * <ul>
7861                 *        <li>D or empty string: Draw (default).</li>
7862                 *        <li>F: Fill.</li>
7863                 *        <li>DF or FD: Draw and fill.</li>
7864                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7865                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7866                 * </ul>
7867                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7868                 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
7869                 * @access public
7870                 * @see SetLineStyle()
7871                 * @since 2.1.000 (2008-01-08)
7872                 */
7873                 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
7874                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7875                                 $this->SetFillColorArray($fill_color);
7876                         }
7877                         switch ($style) {
7878                                 case 'F': {
7879                                         $op = 'f';
7880                                         $line_style = array();
7881                                         break;
7882                                 }
7883                                 case 'FD': 
7884                                 case 'DF': {
7885                                         $op = 'B';
7886                                         break;
7887                                 }
7888                                 case 'CNZ': {
7889                                         $op = 'W n';
7890                                         break;
7891                                 }
7892                                 case 'CEO': {
7893                                         $op = 'W* n';
7894                                         break;
7895                                 }
7896                                 default: {
7897                                         $op = 'S';
7898                                         break;
7899                                 }
7900                         }
7901                         if ($line_style) {
7902                                 $this->SetLineStyle($line_style);
7903                         }
7904                         $this->_outPoint($x0, $y0);
7905                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7906                         $this->_out($op);
7907                 }
7908                 
7909                 /**
7910                 * Draws a poly-Bezier curve.
7911                 * Each Bezier curve segment is a tangent to the line between the control points at
7912                 * either end of the curve.
7913                 * @param float $x0 Abscissa of start point.
7914                 * @param float $y0 Ordinate of start point.
7915                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
7916                 * @param string $style Style of rendering. Possible values are:
7917                 * <ul>
7918                 *        <li>D or empty string: Draw (default).</li>
7919                 *        <li>F: Fill.</li>
7920                 *        <li>DF or FD: Draw and fill.</li>
7921                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7922                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7923                 * </ul>
7924                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7925                 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
7926                 * @access public
7927                 * @see SetLineStyle()
7928                 * @since 3.0008 (2008-05-12)
7929                 */
7930                 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
7931                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7932                                 $this->SetFillColorArray($fill_color);
7933                         }
7934                         switch ($style) {
7935                                 case 'F': {
7936                                         $op = 'f';
7937                                         $line_style = array();
7938                                         break;
7939                                 }
7940                                 case 'FD':
7941                                 case 'DF': {
7942                                         $op = 'B';
7943                                         break;
7944                                 }
7945                                 case 'CNZ': {
7946                                         $op = 'W n';
7947                                         break;
7948                                 }
7949                                 case 'CEO': {
7950                                         $op = 'W* n';
7951                                         break;
7952                                 }
7953                                 default: {
7954                                         $op = 'S';
7955                                         break;
7956                                 }
7957                         }
7958                         if ($line_style) {
7959                                 $this->SetLineStyle($line_style);
7960                         }
7961                         $this->_outPoint($x0, $y0);
7962                         foreach ($segments as $segment) {
7963                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
7964                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7965                         }       
7966                         $this->_out($op);
7967                 }
7968                 
7969                 /**
7970                 * Draws an ellipse.
7971                 * An ellipse is formed from n Bezier curves.
7972                 * @param float $x0 Abscissa of center point.
7973                 * @param float $y0 Ordinate of center point.
7974                 * @param float $rx Horizontal radius.
7975                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
7976                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
7977                 * @param float $astart: Angle start of draw line. Default value: 0.
7978                 * @param float $afinish: Angle finish of draw line. Default value: 360.
7979                 * @param string $style Style of rendering. Possible values are:
7980                 * <ul>
7981                 *        <li>D or empty string: Draw (default).</li>
7982                 *        <li>F: Fill.</li>
7983                 *        <li>DF or FD: Draw and fill.</li>
7984                 *        <li>C: Draw close.</li>
7985                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7986                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7987                 * </ul>
7988                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7989                 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
7990                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
7991                 * @access public
7992                 * @since 2.1.000 (2008-01-08)
7993                 */
7994                 public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
7995                         if ($angle) {
7996                                 $this->StartTransform();
7997                                 $this->Rotate($angle, $x0, $y0);
7998                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
7999                                 $this->StopTransform();
8000                                 return;
8001                         }
8002                         if ($rx) {
8003                                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8004                                         $this->SetFillColorArray($fill_color);
8005                                 }
8006                                 switch ($style) {
8007                                         case 'F': {
8008                                                 $op = 'f';
8009                                                 $line_style = array();
8010                                                 break;
8011                                         }
8012                                         case 'FD': 
8013                                         case 'DF': {
8014                                                 $op = 'B';
8015                                                 break;
8016                                         }
8017                                         case 'C': {
8018                                                 $op = 's'; // Small 's' signifies closing the path as well
8019                                                 break;
8020                                         }
8021                                         case 'CNZ': {
8022                                                 $op = 'W n';
8023                                                 break;
8024                                         }
8025                                         case 'CEO': {
8026                                                 $op = 'W* n';
8027                                                 break;
8028                                         }
8029                                         default: {
8030                                                 $op = 'S';
8031                                                 break;
8032                                         }
8033                                 }
8034                                 if ($line_style) {
8035                                         $this->SetLineStyle($line_style);
8036                                 }
8037                                 if (!$ry) {
8038                                         $ry = $rx;
8039                                 }
8040                                 $rx *= $this->k;
8041                                 $ry *= $this->k;
8042                                 if ($nc < 2) {
8043                                         $nc = 2;
8044                                 }
8045                                 $astart = deg2rad((float) $astart);
8046                                 $afinish = deg2rad((float) $afinish);
8047                                 $total_angle = $afinish - $astart;
8048                                 $dt = $total_angle / $nc;
8049                                 $dtm = $dt / 3;
8050                                 $x0 *= $this->k;
8051                                 $y0 = ($this->h - $y0) * $this->k;
8052                                 $t1 = $astart;
8053                                 $a0 = $x0 + ($rx * cos($t1));
8054                                 $b0 = $y0 + ($ry * sin($t1));
8055                                 $c0 = -$rx * sin($t1);
8056                                 $d0 = $ry * cos($t1);
8057                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
8058                                 for ($i = 1; $i <= $nc; ++$i) {
8059                                         // Draw this bit of the total curve
8060                                         $t1 = ($i * $dt) + $astart;
8061                                         $a1 = $x0 + ($rx * cos($t1));
8062                                         $b1 = $y0 + ($ry * sin($t1));
8063                                         $c1 = -$rx * sin($t1);
8064                                         $d1 = $ry * cos($t1);
8065                                         $this->_outCurve(($a0 + ($c0 * $dtm)) / $this->k, $this->h - (($b0 + ($d0 * $dtm)) / $this->k), ($a1 - ($c1 * $dtm)) / $this->k, $this->h - (($b1 - ($d1 * $dtm)) / $this->k), $a1 / $this->k, $this->h - ($b1 / $this->k));
8066                                         $a0 = $a1;
8067                                         $b0 = $b1;
8068                                         $c0 = $c1;
8069                                         $d0 = $d1;
8070                                 }
8071                                 $this->_out($op);
8072                         }
8073                 }
8074                 
8075                 /**
8076                 * Draws a circle.
8077                 * A circle is formed from n Bezier curves.
8078                 * @param float $x0 Abscissa of center point.
8079                 * @param float $y0 Ordinate of center point.
8080                 * @param float $r Radius.
8081                 * @param float $astart: Angle start of draw line. Default value: 0.
8082                 * @param float $afinish: Angle finish of draw line. Default value: 360.
8083                 * @param string $style Style of rendering. Possible values are:
8084                 * <ul>
8085                 *        <li>D or empty string: Draw (default).</li>
8086                 *        <li>F: Fill.</li>
8087                 *        <li>DF or FD: Draw and fill.</li>
8088                 *        <li>C: Draw close.</li>
8089                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8090                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8091                 * </ul>
8092                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8093                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8094                 * @param integer $nc Number of curves used in circle. Default value: 8.
8095                 * @access public
8096                 * @since 2.1.000 (2008-01-08)
8097                 */
8098                 public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
8099                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8100                 }
8101                 
8102                 /**
8103                 * Draws a polygon.
8104                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
8105                 * @param string $style Style of rendering. Possible values are:
8106                 * <ul>
8107                 *        <li>D or empty string: Draw (default).</li>
8108                 *        <li>F: Fill.</li>
8109                 *        <li>DF or FD: Draw and fill.</li>
8110                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8111                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8112                 * </ul>
8113                 * @param array $line_style Line style of polygon. Array with keys among the following:
8114                 * <ul>
8115                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
8116                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
8117                 * </ul>
8118                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
8119                 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
8120                 * @access public
8121                 * @since 2.1.000 (2008-01-08)
8122                 */
8123                 public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) {
8124                         $np = count($p) / 2;
8125                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8126                                 $this->SetFillColorArray($fill_color);
8127                         }
8128                         switch ($style) {
8129                                 case 'F': {
8130                                         $line_style = array();
8131                                         $op = 'f';
8132                                         break;
8133                                 }
8134                                 case 'FD': 
8135                                 case 'DF': {
8136                                         $op = 'B';
8137                                         break;
8138                                 }
8139                                 case 'CNZ': {
8140                                         $op = 'W n';
8141                                         break;
8142                                 }
8143                                 case 'CEO': {
8144                                         $op = 'W* n';
8145                                         break;
8146                                 }                               
8147                                 default: {
8148                                         $op = 'S';
8149                                         break;
8150                                 }
8151                         }
8152                         $draw = true;
8153                         if ($line_style) {
8154                                 if (isset($line_style['all'])) {
8155                                         $this->SetLineStyle($line_style['all']);
8156                                 } else { // 0 .. (np - 1), op = {B, S}
8157                                         $draw = false;
8158                                         if ('B' == $op) {
8159                                                 $op = 'f';
8160                                                 $this->_outPoint($p[0], $p[1]);
8161                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8162                                                         $this->_outLine($p[$i], $p[$i + 1]);
8163                                                 }
8164                                                 $this->_outLine($p[0], $p[1]);
8165                                                 $this->_out($op);
8166                                         }
8167                                         $p[($np * 2)] = $p[0];
8168                                         $p[(($np * 2) + 1)] = $p[1];
8169                                         for ($i = 0; $i < $np; ++$i) {
8170                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
8171                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
8172                                                 }
8173                                         }
8174                                 }
8175                         }
8176                         if ($draw) {
8177                                 $this->_outPoint($p[0], $p[1]);
8178                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8179                                         $this->_outLine($p[$i], $p[$i + 1]);
8180                                 }
8181                                 $this->_outLine($p[0], $p[1]);
8182                                 $this->_out($op);
8183                         }
8184                 }
8185                 
8186                 /**
8187                 * Draws a regular polygon.
8188                 * @param float $x0 Abscissa of center point.
8189                 * @param float $y0 Ordinate of center point.
8190                 * @param float $r: Radius of inscribed circle.
8191                 * @param integer $ns Number of sides.
8192                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
8193                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
8194                 * @param string $style Style of rendering. Possible values are:
8195                 * <ul>
8196                 *        <li>D or empty string: Draw (default).</li>
8197                 *        <li>F: Fill.</li>
8198                 *        <li>DF or FD: Draw and fill.</li>
8199                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8200                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8201                 * </ul>
8202                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
8203                 * <ul>
8204                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
8205                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
8206                 * </ul>
8207                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
8208                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8209                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
8210                 * <ul>
8211                 *        <li>D or empty string: Draw (default).</li>
8212                 *        <li>F: Fill.</li>
8213                 *        <li>DF or FD: Draw and fill.</li>
8214                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8215                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8216                 * </ul>
8217                 * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8218                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
8219                 * @access public
8220                 * @since 2.1.000 (2008-01-08)
8221                 */
8222                 public function RegularPolygon($x0, $y0, $r, $ns, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
8223                         if (3 > $ns) {
8224                                 $ns = 3;
8225                         }
8226                         if ($draw_circle) {
8227                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8228                         }
8229                         $p = array();
8230                         for ($i = 0; $i < $ns; ++$i) {
8231                                 $a = $angle + ($i * 360 / $ns);
8232                                 $a_rad = deg2rad((float) $a);
8233                                 $p[] = $x0 + ($r * sin($a_rad));
8234                                 $p[] = $y0 + ($r * cos($a_rad));
8235                         }
8236                         $this->Polygon($p, $style, $line_style, $fill_color);
8237                 }
8238                 
8239                 /**
8240                 * Draws a star polygon
8241                 * @param float $x0 Abscissa of center point.
8242                 * @param float $y0 Ordinate of center point.
8243                 * @param float $r Radius of inscribed circle.
8244                 * @param integer $nv Number of vertices.
8245                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
8246                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
8247                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
8248                 * @param string $style Style of rendering. Possible values are:
8249                 * <ul>
8250                 *        <li>D or empty string: Draw (default).</li>
8251                 *        <li>F: Fill.</li>
8252                 *        <li>DF or FD: Draw and fill.</li>
8253                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8254                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8255                 * </ul>
8256                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
8257                 * <ul>
8258                 *        <li>all: Line style of all sides. Array like for
8259                 * {@link SetLineStyle SetLineStyle}.</li>
8260                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
8261                 * </ul>
8262                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
8263                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8264                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
8265                 * <ul>
8266                 *        <li>D or empty string: Draw (default).</li>
8267                 *        <li>F: Fill.</li>
8268                 *        <li>DF or FD: Draw and fill.</li>
8269                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8270                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8271                 * </ul>
8272                 * @param array $circle_outLine_style Line style of inscribed circle (if draws). Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8273                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
8274                 * @access public
8275                 * @since 2.1.000 (2008-01-08)
8276                 */
8277                 public function StarPolygon($x0, $y0, $r, $nv, $ng, $angle=0, $draw_circle=false, $style='', $line_style=array(), $fill_color=array(), $circle_style='', $circle_outLine_style=array(), $circle_fill_color=array()) {
8278                         if (2 > $nv) {
8279                                 $nv = 2;
8280                         }
8281                         if ($draw_circle) {
8282                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8283                         }
8284                         $p2 = array();
8285                         $visited = array();
8286                         for ($i = 0; $i < $nv; ++$i) {
8287                                 $a = $angle + ($i * 360 / $nv);
8288                                 $a_rad = deg2rad((float) $a);
8289                                 $p2[] = $x0 + ($r * sin($a_rad));
8290                                 $p2[] = $y0 + ($r * cos($a_rad));
8291                                 $visited[] = false;
8292                         }
8293                         $p = array();
8294                         $i = 0;
8295                         do {
8296                                 $p[] = $p2[$i * 2];
8297                                 $p[] = $p2[($i * 2) + 1];
8298                                 $visited[$i] = true;
8299                                 $i += $ng;
8300                                 $i %= $nv;
8301                         } while (!$visited[$i]);
8302                         $this->Polygon($p, $style, $line_style, $fill_color);
8303                 }
8304                 
8305                 /**
8306                 * Draws a rounded rectangle.
8307                 * @param float $x Abscissa of upper-left corner.
8308                 * @param float $y Ordinate of upper-left corner.
8309                 * @param float $w Width.
8310                 * @param float $h Height.
8311                 * @param float $r Radius of the rounded corners.
8312                 * @param string $round_corner Draws rounded corner or not. String with a 0 (not rounded i-corner) or 1 (rounded i-corner) in i-position. Positions are, in order and begin to 0: top left, top right, bottom right and bottom left. Default value: all rounded corner ("1111").
8313                 * @param string $style Style of rendering. Possible values are:
8314                 * <ul>
8315                 *        <li>D or empty string: Draw (default).</li>
8316                 *        <li>F: Fill.</li>
8317                 *        <li>DF or FD: Draw and fill.</li>
8318                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8319                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8320                 * </ul>
8321                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8322                 * @param array $fill_color Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K). Default value: default color (empty array).
8323                 * @access public
8324                 * @since 2.1.000 (2008-01-08)
8325                 */
8326                 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
8327                         if ('0000' == $round_corner) { // Not rounded
8328                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
8329                         } else { // Rounded
8330                                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8331                                         $this->SetFillColorArray($fill_color);
8332                                 }
8333                                 switch ($style) {
8334                                         case 'F': {
8335                                                 $border_style = array();
8336                                                 $op = 'f';
8337                                                 break;
8338                                         }
8339                                         case 'FD': 
8340                                         case 'DF': {
8341                                                 $op = 'B';
8342                                                 break;
8343                                         }
8344                                         case 'CNZ': {
8345                                                 $op = 'W n';
8346                                                 break;
8347                                         }
8348                                         case 'CEO': {
8349                                                 $op = 'W* n';
8350                                                 break;
8351                                         }
8352                                         default: {
8353                                                 $op = 'S';
8354                                                 break;
8355                                         }
8356                                 }
8357                                 if ($border_style) {
8358                                         $this->SetLineStyle($border_style);
8359                                 }
8360                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
8361                                 $this->_outPoint($x + $r, $y);
8362                                 $xc = $x + $w - $r;
8363                                 $yc = $y + $r;
8364                                 $this->_outLine($xc, $y);
8365                                 if ($round_corner[0]) {
8366                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
8367                                 } else {
8368                                         $this->_outLine($x + $w, $y);
8369                                 }
8370                                 $xc = $x + $w - $r;
8371                                 $yc = $y + $h - $r;
8372                                 $this->_outLine($x + $w, $yc);
8373                                 if ($round_corner[1]) {
8374                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
8375                                 } else {
8376                                         $this->_outLine($x + $w, $y + $h);
8377                                 }
8378                                 $xc = $x + $r;
8379                                 $yc = $y + $h - $r;
8380                                 $this->_outLine($xc, $y + $h);
8381                                 if ($round_corner[2]) {
8382                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
8383                                 } else {
8384                                         $this->_outLine($x, $y + $h);
8385                                 }
8386                                 $xc = $x + $r;
8387                                 $yc = $y + $r;
8388                                 $this->_outLine($x, $yc);
8389                                 if ($round_corner[3]) {
8390                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
8391                                 } else {
8392                                         $this->_outLine($x, $y);
8393                                         $this->_outLine($x + $r, $y);
8394                                 }
8395                                 $this->_out($op);
8396                         }
8397                 }
8398                 
8399                 // END GRAPHIC FUNCTIONS SECTION -----------------------
8400                 
8401                 // BIDIRECTIONAL TEXT SECTION --------------------------
8402                 /**
8403                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
8404                  * @param string $str string to manipulate.
8405                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
8406                  * @return string
8407                  * @access protected
8408                  * @author Nicola Asuni
8409                  * @since 2.1.000 (2008-01-08)
8410                 */
8411                 protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
8412                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
8413                 }
8414                 
8415                 /**
8416                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
8417                  * @param array $ta array of characters composing the string.
8418                  * @param string $str string to process
8419                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
8420                  * @return string
8421                  * @author Nicola Asuni
8422                  * @access protected
8423                  * @since 2.4.000 (2008-03-06)
8424                 */
8425                 protected function utf8Bidi($ta, $str='', $forcertl=false) {
8426                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
8427                         // paragraph embedding level
8428                         $pel = 0;
8429                         // max level
8430                         $maxlevel = 0;
8431                         if ($this->empty_string($str)) {
8432                                 // create string from array
8433                                 $str = $this->UTF8ArrSubString($ta);
8434                         }
8435                         // check if string contains arabic text
8436                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
8437                                 $arabic = true;
8438                         } else {
8439                                 $arabic = false;
8440                         }
8441                         // check if string contains RTL text
8442                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
8443                                 return $ta;
8444                         }
8445                         
8446                         // get number of chars
8447                         $numchars = count($ta);
8448                         
8449                         if ($forcertl == 'R') {
8450                                         $pel = 1;
8451                         } elseif ($forcertl == 'L') {
8452                                         $pel = 0;
8453                         } else {
8454                                 // P2. In each paragraph, find the first character of type L, AL, or R.
8455                                 // P3. If a character is found in P2 and it is of type AL or R, then set the paragraph embedding level to one; otherwise, set it to zero.
8456                                 for ($i=0; $i < $numchars; ++$i) {
8457                                         $type = $unicode[$ta[$i]];
8458                                         if ($type == 'L') {
8459                                                 $pel = 0;
8460                                                 break;
8461                                         } elseif (($type == 'AL') OR ($type == 'R')) {
8462                                                 $pel = 1;
8463                                                 break;
8464                                         }
8465                                 }
8466                         }
8467                         
8468                         // Current Embedding Level
8469                         $cel = $pel;
8470                         // directional override status
8471                         $dos = 'N';
8472                         $remember = array();
8473                         // start-of-level-run
8474                         $sor = $pel % 2 ? 'R' : 'L';
8475                         $eor = $sor;
8476                         
8477                         // Array of characters data
8478                         $chardata = Array();
8479                         
8480                         // X1. Begin by setting the current embedding level to the paragraph embedding level. Set the directional override status to neutral. Process each character iteratively, applying rules X2 through X9. Only embedding levels from 0 to 61 are valid in this phase.
8481                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
8482                         for ($i=0; $i < $numchars; ++$i) {
8483                                 if ($ta[$i] == K_RLE) {
8484                                         // X2. With each RLE, compute the least greater odd embedding level.
8485                                         //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
8486                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8487                                         $next_level = $cel + ($cel % 2) + 1;
8488                                         if ($next_level < 62) {
8489                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
8490                                                 $cel = $next_level;
8491                                                 $dos = 'N';
8492                                                 $sor = $eor;
8493                                                 $eor = $cel % 2 ? 'R' : 'L';
8494                                         }
8495                                 } elseif ($ta[$i] == K_LRE) {
8496                                         // X3. With each LRE, compute the least greater even embedding level.
8497                                         //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
8498                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8499                                         $next_level = $cel + 2 - ($cel % 2);
8500                                         if ( $next_level < 62 ) {
8501                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
8502                                                 $cel = $next_level;
8503                                                 $dos = 'N';
8504                                                 $sor = $eor;
8505                                                 $eor = $cel % 2 ? 'R' : 'L';
8506                                         }
8507                                 } elseif ($ta[$i] == K_RLO) {
8508                                         // X4. With each RLO, compute the least greater odd embedding level.
8509                                         //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to right-to-left.
8510                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8511                                         $next_level = $cel + ($cel % 2) + 1;
8512                                         if ($next_level < 62) {
8513                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
8514                                                 $cel = $next_level;
8515                                                 $dos = 'R';
8516                                                 $sor = $eor;
8517                                                 $eor = $cel % 2 ? 'R' : 'L';
8518                                         }
8519                                 } elseif ($ta[$i] == K_LRO) {
8520                                         // X5. With each LRO, compute the least greater even embedding level.
8521                                         //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to left-to-right.
8522                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8523                                         $next_level = $cel + 2 - ($cel % 2);
8524                                         if ( $next_level < 62 ) {
8525                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
8526                                                 $cel = $next_level;
8527                                                 $dos = 'L';
8528                                                 $sor = $eor;
8529                                                 $eor = $cel % 2 ? 'R' : 'L';
8530                                         }
8531                                 } elseif ($ta[$i] == K_PDF) {
8532                                         // X7. With each PDF, determine the matching embedding or override code. If there was a valid matching code, restore (pop) the last remembered (pushed) embedding level and directional override.
8533                                         if (count($remember)) {
8534                                                 $last = count($remember ) - 1;
8535                                                 if (($remember[$last]['num'] == K_RLE) OR 
8536                                                           ($remember[$last]['num'] == K_LRE) OR 
8537                                                           ($remember[$last]['num'] == K_RLO) OR 
8538                                                           ($remember[$last]['num'] == K_LRO)) {
8539                                                         $match = array_pop($remember);
8540                                                         $cel = $match['cel'];
8541                                                         $dos = $match['dos'];
8542                                                         $sor = $eor;
8543                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
8544                                                 }
8545                                         }
8546                                 } elseif (($ta[$i] != K_RLE) AND
8547                                                                  ($ta[$i] != K_LRE) AND
8548                                                                  ($ta[$i] != K_RLO) AND
8549                                                                  ($ta[$i] != K_LRO) AND
8550                                                                  ($ta[$i] != K_PDF)) {
8551                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
8552                                         //      a. Set the level of the current character to the current embedding level.
8553                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
8554                                         if ($dos != 'N') {
8555                                                 $chardir = $dos;
8556                                         } else {
8557                                                 $chardir = $unicode[$ta[$i]];
8558                                         }
8559                                         // stores string characters and other information
8560                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
8561                                 }
8562                         } // end for each char
8563                         
8564                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
8565                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
8566                         // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
8567                         
8568                         // 3.3.3 Resolving Weak Types
8569                         // Weak types are now resolved one level run at a time. At level run boundaries where the type of the character on the other side of the boundary is required, the type assigned to sor or eor is used.
8570                         // Nonspacing marks are now resolved based on the previous characters.
8571                         $numchars = count($chardata);
8572                         
8573                         // W1. Examine each nonspacing mark (NSM) in the level run, and change the type of the NSM to the type of the previous character. If the NSM is at the start of the level run, it will get the type of sor.
8574                         $prevlevel = -1; // track level changes
8575                         $levcount = 0; // counts consecutive chars at the same level
8576                         for ($i=0; $i < $numchars; ++$i) {
8577                                 if ($chardata[$i]['type'] == 'NSM') {
8578                                         if ($levcount) {
8579                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8580                                         } elseif ($i > 0) {
8581                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
8582                                         }
8583                                 }
8584                                 if ($chardata[$i]['level'] != $prevlevel) {
8585                                         $levcount = 0;
8586                                 } else {
8587                                         ++$levcount;
8588                                 }
8589                                 $prevlevel = $chardata[$i]['level'];
8590                         }
8591                         
8592                         // W2. Search backward from each instance of a European number until the first strong type (R, L, AL, or sor) is found. If an AL is found, change the type of the European number to Arabic number.
8593                         $prevlevel = -1;
8594                         $levcount = 0;
8595                         for ($i=0; $i < $numchars; ++$i) {
8596                                 if ($chardata[$i]['char'] == 'EN') {
8597                                         for ($j=$levcount; $j >= 0; $j--) {
8598                                                 if ($chardata[$j]['type'] == 'AL') {
8599                                                         $chardata[$i]['type'] = 'AN';
8600                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
8601                                                         break;
8602                                                 }
8603                                         }
8604                                 }
8605                                 if ($chardata[$i]['level'] != $prevlevel) {
8606                                         $levcount = 0;
8607                                 } else {
8608                                         ++$levcount;
8609                                 }
8610                                 $prevlevel = $chardata[$i]['level'];
8611                         }
8612                         
8613                         // W3. Change all ALs to R.
8614                         for ($i=0; $i < $numchars; ++$i) {
8615                                 if ($chardata[$i]['type'] == 'AL') {
8616                                         $chardata[$i]['type'] = 'R';
8617                                 } 
8618                         }
8619                         
8620                         // W4. A single European separator between two European numbers changes to a European number. A single common separator between two numbers of the same type changes to that type.
8621                         $prevlevel = -1;
8622                         $levcount = 0;
8623                         for ($i=0; $i < $numchars; ++$i) {
8624                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8625                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8626                                                 $chardata[$i]['type'] = 'EN';
8627                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8628                                                 $chardata[$i]['type'] = 'EN';
8629                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
8630                                                 $chardata[$i]['type'] = 'AN';
8631                                         }
8632                                 }
8633                                 if ($chardata[$i]['level'] != $prevlevel) {
8634                                         $levcount = 0;
8635                                 } else {
8636                                         ++$levcount;
8637                                 }
8638                                 $prevlevel = $chardata[$i]['level'];
8639                         }
8640                         
8641                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
8642                         $prevlevel = -1;
8643                         $levcount = 0;
8644                         for ($i=0; $i < $numchars; ++$i) {
8645                                 if ($chardata[$i]['type'] == 'ET') {
8646                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
8647                                                 $chardata[$i]['type'] = 'EN';
8648                                         } else {
8649                                                 $j = $i+1;
8650                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
8651                                                         if ($chardata[$j]['type'] == 'EN') {
8652                                                                 $chardata[$i]['type'] = 'EN';
8653                                                                 break;
8654                                                         } elseif ($chardata[$j]['type'] != 'ET') {
8655                                                                 break;
8656                                                         }
8657                                                         ++$j;
8658                                                 }
8659                                         }
8660                                 }
8661                                 if ($chardata[$i]['level'] != $prevlevel) {
8662                                         $levcount = 0;
8663                                 } else {
8664                                         ++$levcount;
8665                                 }
8666                                 $prevlevel = $chardata[$i]['level'];
8667                         }
8668                         
8669                         // W6. Otherwise, separators and terminators change to Other Neutral.
8670                         $prevlevel = -1;
8671                         $levcount = 0;
8672                         for ($i=0; $i < $numchars; ++$i) {
8673                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
8674                                         $chardata[$i]['type'] = 'ON';
8675                                 }
8676                                 if ($chardata[$i]['level'] != $prevlevel) {
8677                                         $levcount = 0;
8678                                 } else {
8679                                         ++$levcount;
8680                                 }
8681                                 $prevlevel = $chardata[$i]['level'];
8682                         }
8683                         
8684                         //W7. Search backward from each instance of a European number until the first strong type (R, L, or sor) is found. If an L is found, then change the type of the European number to L.
8685                         $prevlevel = -1;
8686                         $levcount = 0;
8687                         for ($i=0; $i < $numchars; ++$i) {
8688                                 if ($chardata[$i]['char'] == 'EN') {
8689                                         for ($j=$levcount; $j >= 0; $j--) {
8690                                                 if ($chardata[$j]['type'] == 'L') {
8691                                                         $chardata[$i]['type'] = 'L';
8692                                                 } elseif ($chardata[$j]['type'] == 'R') {
8693                                                         break;
8694                                                 }
8695                                         }
8696                                 }
8697                                 if ($chardata[$i]['level'] != $prevlevel) {
8698                                         $levcount = 0;
8699                                 } else {
8700                                         ++$levcount;
8701                                 }
8702                                 $prevlevel = $chardata[$i]['level'];
8703                         }
8704                         
8705                         // N1. A sequence of neutrals takes the direction of the surrounding strong text if the text on both sides has the same direction. European and Arabic numbers act as if they were R in terms of their influence on neutrals. Start-of-level-run (sor) and end-of-level-run (eor) are used at level run boundaries.
8706                         $prevlevel = -1;
8707                         $levcount = 0;
8708                         for ($i=0; $i < $numchars; ++$i) {
8709                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8710                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8711                                                 $chardata[$i]['type'] = 'L';
8712                                         } elseif (($chardata[$i]['type'] == 'N') AND
8713                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8714                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8715                                                 $chardata[$i]['type'] = 'R';
8716                                         } elseif ($chardata[$i]['type'] == 'N') {
8717                                                 // N2. Any remaining neutrals take the embedding direction
8718                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8719                                         }
8720                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8721                                         // first char
8722                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8723                                                 $chardata[$i]['type'] = 'L';
8724                                         } elseif (($chardata[$i]['type'] == 'N') AND
8725                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
8726                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8727                                                 $chardata[$i]['type'] = 'R';
8728                                         } elseif ($chardata[$i]['type'] == 'N') {
8729                                                 // N2. Any remaining neutrals take the embedding direction
8730                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8731                                         }
8732                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
8733                                         //last char
8734                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
8735                                                 $chardata[$i]['type'] = 'L';
8736                                         } elseif (($chardata[$i]['type'] == 'N') AND
8737                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8738                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
8739                                                 $chardata[$i]['type'] = 'R';
8740                                         } elseif ($chardata[$i]['type'] == 'N') {
8741                                                 // N2. Any remaining neutrals take the embedding direction
8742                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8743                                         }
8744                                 } elseif ($chardata[$i]['type'] == 'N') {
8745                                         // N2. Any remaining neutrals take the embedding direction
8746                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
8747                                 }
8748                                 if ($chardata[$i]['level'] != $prevlevel) {
8749                                         $levcount = 0;
8750                                 } else {
8751                                         ++$levcount;
8752                                 }
8753                                 $prevlevel = $chardata[$i]['level'];
8754                         }
8755                         
8756                         // I1. For all characters with an even (left-to-right) embedding direction, those of type R go up one level and those of type AN or EN go up two levels.
8757                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
8758                         for ($i=0; $i < $numchars; ++$i) {
8759                                 $odd = $chardata[$i]['level'] % 2;
8760                                 if ($odd) {
8761                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8762                                                 $chardata[$i]['level'] += 1;
8763                                         }
8764                                 } else {
8765                                         if ($chardata[$i]['type'] == 'R') {
8766                                                 $chardata[$i]['level'] += 1;
8767                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8768                                                 $chardata[$i]['level'] += 2;
8769                                         }
8770                                 }
8771                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
8772                         }
8773                         
8774                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
8775                         //      1. Segment separators,
8776                         //      2. Paragraph separators,
8777                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
8778                         //      4. Any sequence of white space characters at the end of the line.
8779                         for ($i=0; $i < $numchars; ++$i) {
8780                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
8781                                         $chardata[$i]['level'] = $pel;
8782                                 } elseif ($chardata[$i]['type'] == 'WS') {
8783                                         $j = $i+1;
8784                                         while ($j < $numchars) {
8785                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
8786                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
8787                                                         $chardata[$i]['level'] = $pel;
8788                                                         break;
8789                                                 } elseif ($chardata[$j]['type'] != 'WS') {
8790                                                         break;
8791                                                 }
8792                                                 ++$j;
8793                                         }
8794                                 }
8795                         }
8796                         
8797                         // Arabic Shaping
8798                         // Cursively connected scripts, such as Arabic or Syriac, require the selection of positional character shapes that depend on adjacent characters. Shaping is logically applied after the Bidirectional Algorithm is used and is limited to characters within the same directional run. 
8799                         if ($arabic) {
8800                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
8801                                 $alfletter = array(1570,1571,1573,1575);
8802                                 $chardata2 = $chardata;
8803                                 $laaletter = false;
8804                                 $charAL = array();
8805                                 $x = 0;
8806                                 for ($i=0; $i < $numchars; ++$i) {
8807                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
8808                                                 $charAL[$x] = $chardata[$i];
8809                                                 $charAL[$x]['i'] = $i;
8810                                                 $chardata[$i]['x'] = $x;
8811                                                 ++$x;
8812                                         }
8813                                 }
8814                                 $numAL = $x;
8815                                 for ($i=0; $i < $numchars; ++$i) {
8816                                         $thischar = $chardata[$i];
8817                                         if ($i > 0) {
8818                                                 $prevchar = $chardata[($i-1)];
8819                                         } else {
8820                                                 $prevchar = false;
8821                                         }
8822                                         if (($i+1) < $numchars) {
8823                                                 $nextchar = $chardata[($i+1)];
8824                                         } else {
8825                                                 $nextchar = false;
8826                                         }
8827                                         if ($unicode[$thischar['char']] == 'AL') {
8828                                                 $x = $thischar['x'];
8829                                                 if ($x > 0) {
8830                                                         $prevchar = $charAL[($x-1)];
8831                                                 } else {
8832                                                         $prevchar = false;
8833                                                 }
8834                                                 if (($x+1) < $numAL) {
8835                                                         $nextchar = $charAL[($x+1)];
8836                                                 } else {
8837                                                         $nextchar = false;
8838                                                 }
8839                                                 // if laa letter
8840                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
8841                                                         $arabicarr = $laa_array;
8842                                                         $laaletter = true;
8843                                                         if ($x > 1) {
8844                                                                 $prevchar = $charAL[($x-2)];
8845                                                         } else {
8846                                                                 $prevchar = false;
8847                                                         }
8848                                                 } else {
8849                                                         $arabicarr = $unicode_arlet;
8850                                                         $laaletter = false;
8851                                                 }
8852                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
8853                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8854                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8855                                                         ($prevchar['type'] == $thischar['type']) AND
8856                                                         ($nextchar['type'] == $thischar['type']) AND
8857                                                         ($nextchar['char'] != 1567)) {
8858                                                         if (in_array($prevchar['char'], $endedletter)) {
8859                                                                 if (isset($arabicarr[$thischar['char']][2])) {
8860                                                                         // initial
8861                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8862                                                                 }
8863                                                         } else {
8864                                                                 if (isset($arabicarr[$thischar['char']][3])) {
8865                                                                         // medial
8866                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
8867                                                                 }
8868                                                         }
8869                                                 } elseif (($nextchar !== false) AND
8870                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8871                                                         ($nextchar['type'] == $thischar['type']) AND
8872                                                         ($nextchar['char'] != 1567)) {
8873                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
8874                                                                 // initial
8875                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8876                                                         }
8877                                                 } elseif ((($prevchar !== false) AND
8878                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8879                                                         ($prevchar['type'] == $thischar['type'])) OR
8880                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
8881                                                         // final
8882                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
8883                                                                 ($chardata[$i-1]['char'] == 1604) AND
8884                                                                 ($chardata[$i-2]['char'] == 1604)) {
8885                                                                 //Allah Word
8886                                                                 // mark characters to delete with false
8887                                                                 $chardata2[$i-2]['char'] = false;
8888                                                                 $chardata2[$i-1]['char'] = false; 
8889                                                                 $chardata2[$i]['char'] = 65010;
8890                                                         } else {
8891                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
8892                                                                         if (isset($arabicarr[$thischar['char']][0])) {
8893                                                                                 // isolated
8894                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8895                                                                         }
8896                                                                 } else {
8897                                                                         if (isset($arabicarr[$thischar['char']][1])) {
8898                                                                                 // final
8899                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
8900                                                                         }
8901                                                                 }
8902                                                         }
8903                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
8904                                                         // isolated
8905                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8906                                                 }
8907                                                 // if laa letter
8908                                                 if ($laaletter) {
8909                                                         // mark characters to delete with false
8910                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
8911                                                 }
8912                                         } // end if AL (Arabic Letter)
8913                                 } // end for each char
8914                                 /* 
8915                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 
8916                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
8917                                  */
8918                                 $cw = &$this->CurrentFont['cw'];
8919                                 for ($i = 0; $i < ($numchars-1); ++$i) {
8920                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
8921                                                 // check if the subtitution font is defined on current font
8922                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
8923                                                         $chardata2[$i]['char'] = false;
8924                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
8925                                                 }
8926                                         }
8927                                 }
8928                                 // remove marked characters
8929                                 foreach ($chardata2 as $key => $value) {
8930                                         if ($value['char'] === false) {
8931                                                 unset($chardata2[$key]);
8932                                         }
8933                                 }
8934                                 $chardata = array_values($chardata2);
8935                                 $numchars = count($chardata);
8936                                 unset($chardata2);
8937                                 unset($arabicarr);
8938                                 unset($laaletter);
8939                                 unset($charAL);
8940                         }
8941                         
8942                         // L2. From the highest level found in the text to the lowest odd level on each line, including intermediate levels not actually present in the text, reverse any contiguous sequence of characters that are at that level or higher.
8943                         for ($j=$maxlevel; $j > 0; $j--) {
8944                                 $ordarray = Array();
8945                                 $revarr = Array();
8946                                 $onlevel = false;
8947                                 for ($i=0; $i < $numchars; ++$i) {
8948                                         if ($chardata[$i]['level'] >= $j) {
8949                                                 $onlevel = true;
8950                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
8951                                                         // L4. A character is depicted by a mirrored glyph if and only if (a) the resolved directionality of that character is R, and (b) the Bidi_Mirrored property value of that character is true.
8952                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
8953                                                 }
8954                                                 $revarr[] = $chardata[$i];
8955                                         } else {
8956                                                 if ($onlevel) {
8957                                                         $revarr = array_reverse($revarr);
8958                                                         $ordarray = array_merge($ordarray, $revarr);
8959                                                         $revarr = Array();
8960                                                         $onlevel = false;
8961                                                 }
8962                                                 $ordarray[] = $chardata[$i];
8963                                         }
8964                                 }
8965                                 if ($onlevel) {
8966                                         $revarr = array_reverse($revarr);
8967                                         $ordarray = array_merge($ordarray, $revarr);
8968                                 }
8969                                 $chardata = $ordarray;
8970                         }
8971                         
8972                         $ordarray = array();
8973                         for ($i=0; $i < $numchars; ++$i) {
8974                                 $ordarray[] = $chardata[$i]['char'];
8975                         }
8976                         
8977                         return $ordarray;
8978                 }
8979                 
8980                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
8981                 
8982                 /*
8983                 * Adds a bookmark.
8984                 * @param string $txt bookmark description.
8985                 * @param int $level bookmark level (minimum value is 0).
8986                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
8987                 * @param int $page target page number (leave empty for current page).
8988                 * @access public
8989                 * @author Olivier Plathey, Nicola Asuni
8990                 * @since 2.1.002 (2008-02-12)
8991                 */
8992                 public function Bookmark($txt, $level=0, $y=-1, $page='') {
8993                         if ($level < 0) {
8994                                 $level = 0;
8995                         }
8996                         if (isset($this->outlines[0])) {
8997                                 $lastoutline = end($this->outlines);
8998                                 $maxlevel = $lastoutline['l'] + 1;
8999                         } else {
9000                                 $maxlevel = 0;
9001                         }
9002                         if ($level > $maxlevel) {
9003                                 $level = $maxlevel;
9004                         }
9005                         if ($y == -1) {
9006                                 $y = $this->GetY();
9007                         }
9008                         if (empty($page)) {
9009                                 $page = $this->PageNo();
9010                         }
9011                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
9012                 }
9013                 
9014                 /*
9015                 * Create a bookmark PDF string.
9016                 * @access protected
9017                 * @author Olivier Plathey, Nicola Asuni
9018                 * @since 2.1.002 (2008-02-12)
9019                 */
9020                 protected function _putbookmarks() {
9021                         $nb = count($this->outlines);
9022                         if ($nb == 0) {
9023                                 return;
9024                         }
9025                         $lru = array();
9026                         $level = 0;
9027                         foreach ($this->outlines as $i => $o) {
9028                                 if ($o['l'] > 0) {
9029                                         $parent = $lru[($o['l'] - 1)];
9030                                         //Set parent and last pointers
9031                                         $this->outlines[$i]['parent'] = $parent;
9032                                         $this->outlines[$parent]['last'] = $i;
9033                                         if ($o['l'] > $level) {
9034                                                 //Level increasing: set first pointer
9035                                                 $this->outlines[$parent]['first'] = $i;
9036                                         }
9037                                 } else {
9038                                         $this->outlines[$i]['parent'] = $nb;
9039                                 }
9040                                 if (($o['l'] <= $level) AND ($i > 0)) {
9041                                         //Set prev and next pointers
9042                                         $prev = $lru[$o['l']];
9043                                         $this->outlines[$prev]['next'] = $i;
9044                                         $this->outlines[$i]['prev'] = $prev;
9045                                 }
9046                                 $lru[$o['l']] = $i;
9047                                 $level = $o['l'];
9048                         }
9049                         //Outline items
9050                         $n = $this->n + 1;
9051                         foreach ($this->outlines as $i => $o) {
9052                                 $this->_newobj();
9053                                 $this->_out('<</Title '.$this->_textstring($o['t']));
9054                                 $this->_out('/Parent '.($n + $o['parent']).' 0 R');
9055                                 if (isset($o['prev']))
9056                                 $this->_out('/Prev '.($n + $o['prev']).' 0 R');
9057                                 if (isset($o['next']))
9058                                 $this->_out('/Next '.($n + $o['next']).' 0 R');
9059                                 if (isset($o['first']))
9060                                 $this->_out('/First '.($n + $o['first']).' 0 R');
9061                                 if (isset($o['last']))
9062                                 $this->_out('/Last '.($n + $o['last']).' 0 R');
9063                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
9064                                 $this->_out('/Count 0>>');
9065                                 $this->_out('endobj');
9066                         }
9067                         //Outline root
9068                         $this->_newobj();
9069                         $this->OutlineRoot = $this->n;
9070                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
9071                         $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
9072                         $this->_out('endobj');
9073                 }
9074                 
9075                 
9076                 // --- JAVASCRIPT - FORMS ------------------------------
9077                 
9078                 /*
9079                 * Adds a javascript
9080                 * @access public
9081                 * @author Johannes G�ntert, Nicola Asuni
9082                 * @since 2.1.002 (2008-02-12)
9083                 */
9084                 public function IncludeJS($script) {
9085                         $this->javascript .= $script;
9086                 }
9087                 
9088                 /*
9089                 * Create a javascript PDF string.
9090                 * @access protected
9091                 * @author Johannes G�ntert, Nicola Asuni
9092                 * @since 2.1.002 (2008-02-12)
9093                 */
9094                 protected function _putjavascript() {
9095                         if (empty($this->javascript)) {
9096                                 return;
9097                         }
9098                         // the following two lines are uded to avoid form fields duplication after saving
9099                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
9100                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
9101                         $this->_newobj();
9102                         $this->n_js = $this->n;
9103                         $this->_out('<<');
9104                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
9105                         $this->_out('>>');
9106                         $this->_out('endobj');
9107                         $this->_newobj();
9108                         $this->_out('<<');
9109                         $this->_out('/S /JavaScript');
9110                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
9111                         $this->_out('>>');
9112                         $this->_out('endobj');
9113                 }
9114                 
9115                 /*
9116                 * Convert color to javascript color.
9117                 * @param string $color color name or #RRGGBB
9118                 * @access protected
9119                 * @author Denis Van Nuffelen, Nicola Asuni
9120                 * @since 2.1.002 (2008-02-12)
9121                 */
9122                 protected function _JScolor($color) {
9123                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
9124                         if (substr($color,0,1) == '#') {
9125                                 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
9126                         }
9127                         if (!in_array($color,$aColors)) {
9128                                 $this->Error('Invalid color: '.$color);
9129                         }
9130                         return 'color.'.$color;
9131                 }
9132                 
9133                 /*
9134                 * Adds a javascript form field.
9135                 * @param string $type field type
9136                 * @param string $name field name
9137                 * @param int $x horizontal position
9138                 * @param int $y vertical position
9139                 * @param int $w width
9140                 * @param int $h height
9141                 * @param array $prop array of properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9142                 * @access protected
9143                 * @author Denis Van Nuffelen, Nicola Asuni
9144                 * @since 2.1.002 (2008-02-12)
9145                 */
9146                 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
9147                         if ($this->rtl) {
9148                                 $x = $x - $w;
9149                         }
9150                         // the followind avoid fields duplication after saving the document
9151                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
9152                         $k = $this->k;
9153                         $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
9154                         $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
9155                         while (list($key, $val) = each($prop)) {
9156                                 if (strcmp(substr($key, -5), 'Color') == 0) {
9157                                         $val = $this->_JScolor($val);
9158                                 } else {
9159                                         $val = "'".$val."'";
9160                                 }
9161                                 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
9162                         }
9163                         if ($this->rtl) {
9164                                 $this->x -= $w;
9165                         } else {
9166                                 $this->x += $w;
9167                         }
9168                         $this->javascript .= '}';
9169                 }
9170                 
9171                 /*
9172                 * Creates a text field
9173                 * @param string $name field name
9174                 * @param int $w width
9175                 * @param int $h height
9176                 * @param string $prop properties. The value property allows to set the initial value. The multiline property allows to define the field as multiline. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9177                 * @access public
9178                 * @author Denis Van Nuffelen, Nicola Asuni
9179                 * @since 2.1.002 (2008-02-12)
9180                 */
9181                 public function TextField($name, $w, $h, $prop=array()) {
9182                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
9183                 }
9184                 
9185                 /*
9186                 * Creates a RadioButton field
9187                 * @param string $name field name
9188                 * @param int $w width
9189                 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9190                 * @access public
9191                 * @author Nicola Asuni
9192                 * @since 2.2.003 (2008-03-03)
9193                 */
9194                 public function RadioButton($name, $w, $prop=array()) {
9195                         if (!isset($prop['strokeColor'])) {
9196                                 $prop['strokeColor']='black';
9197                         }
9198                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
9199                 }
9200                 
9201                 /*
9202                 * Creates a List-box field
9203                 * @param string $name field name
9204                 * @param int $w width
9205                 * @param int $h height
9206                 * @param array $values array containing the list of values.
9207                 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9208                 * @access public
9209                 * @author Nicola Asuni
9210                 * @since 2.2.003 (2008-03-03)
9211                 */
9212                 public function ListBox($name, $w, $h, $values, $prop=array()) {
9213                         if (!isset($prop['strokeColor'])) {
9214                                 $prop['strokeColor'] = 'ltGray';
9215                         }
9216                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
9217                         $s = '';
9218                         foreach ($values as $value) {
9219                                 $s .= "'".addslashes($value)."',";
9220                         }
9221                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9222                 }
9223                 
9224                 /*
9225                 * Creates a Combo-box field
9226                 * @param string $name field name
9227                 * @param int $w width
9228                 * @param int $h height
9229                 * @param array $values array containing the list of values.
9230                 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9231                 * @access public
9232                 * @author Denis Van Nuffelen, Nicola Asuni
9233                 * @since 2.1.002 (2008-02-12)
9234                 */
9235                 public function ComboBox($name, $w, $h, $values, $prop=array()) {
9236                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
9237                         $s = '';
9238                         foreach ($values as $value) {
9239                                 $s .= "'".addslashes($value)."',";
9240                         }
9241                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9242                 }
9243                 
9244                 /*
9245                 * Creates a CheckBox field
9246                 * @param string $name field name
9247                 * @param int $w width
9248                 * @param boolean $checked define the initial state (default = false).
9249                 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9250                 * @access public
9251                 * @author Denis Van Nuffelen, Nicola Asuni
9252                 * @since 2.1.002 (2008-02-12)
9253                 */
9254                 public function CheckBox($name, $w, $checked=false, $prop=array()) {
9255                         $prop['value'] = ($checked ? 'Yes' : 'Off');
9256                         if (!isset($prop['strokeColor'])) {
9257                                 $prop['strokeColor'] = 'black';
9258                         }
9259                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
9260                 }
9261                 
9262                 /*
9263                 * Creates a button field
9264                 * @param string $name field name
9265                 * @param int $w width
9266                 * @param int $h height
9267                 * @param string $caption caption.
9268                 * @param string $action action triggered by the button (JavaScript code).
9269                 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
9270                 * @access public
9271                 * @author Denis Van Nuffelen, Nicola Asuni
9272                 * @since 2.1.002 (2008-02-12)
9273                 */
9274                 public function Button($name, $w, $h, $caption, $action, $prop=array()) {
9275                         if (!isset($prop['strokeColor'])) {
9276                                 $prop['strokeColor'] = 'black';
9277                         }
9278                         if (!isset($prop['borderStyle'])) {
9279                                 $prop['borderStyle'] = 'beveled';
9280                         }
9281                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
9282                         $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
9283                         $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
9284                         $this->javascript .= 'f'.$name.".highlight='push';\n";
9285                         $this->javascript .= 'f'.$name.".print=false;\n";
9286                 }
9287                 
9288                 // END JAVASCRIPT - FORMS ------------------------------
9289                 
9290                 /*
9291                 * Enable Write permissions for PDF Reader.
9292                 * WARNING: This works only using the Adobe private key with the setSignature() method.
9293                 * @access protected
9294                 * @author Nicola Asuni
9295                 * @since 2.9.000 (2008-03-26)
9296                 */
9297                 protected function _putuserrights() {
9298                         if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) {
9299                                 return;
9300                         }
9301                         $this->_out('/Perms');
9302                         $this->_out('<<');
9303                         $this->_out('/UR3');
9304                         $this->_out('<<');
9305                         $this->_out('/Type/Sig');
9306                         $this->_out('/Filter/Adobe.PPKLite');
9307                         $this->_out('/SubFilter/adbe.pkcs7.detached');
9308                         $this->_out('/ByteRange[0 ********** ********** **********]');
9309                         $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9310                         if ($this->ur) {
9311                                 $this->_out('/Reference');
9312                                 $this->_out('[');
9313                                 $this->_out('<<');
9314                                 $this->_out('/Type/SigRef');
9315                                 $this->_out('/TransformMethod/UR3');
9316                                 $this->_out('/TransformParams');
9317                                 $this->_out('<<');
9318                                 $this->_out('/Type/TransformParams');
9319                                 $this->_out('/V/2.2');
9320                                 if (!$this->empty_string($this->ur_document)) {
9321                                         $this->_out('/Document['.$this->ur_document.']');
9322                                 }
9323                                 if (!$this->empty_string($this->ur_annots)) {
9324                                         $this->_out('/Annots['.$this->ur_annots.']');
9325                                 }
9326                                 if (!$this->empty_string($this->ur_form)) {
9327                                         $this->_out('/Form['.$this->ur_form.']');
9328                                 }
9329                                 if (!$this->empty_string($this->ur_signature)) {
9330                                         $this->_out('/Signature['.$this->ur_signature.']');
9331                                 }                       
9332                                 $this->_out('>>');
9333                                 $this->_out('>>');
9334                                 $this->_out(']');
9335                         }
9336                         $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9337                         $this->_out('>>');
9338                         $this->_out('>>');
9339                 }
9340                 
9341                 /*
9342                 * Add certification signature (DocMDP)
9343                 * @access protected
9344                 * @author Nicola Asuni
9345                 * @since 4.6.008 (2009-05-07)
9346                 */
9347                 protected function _putcertification() {
9348                         if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) {
9349                                 return;
9350                         }
9351                         $this->_out('/Perms');
9352                         $this->_out('<<');
9353                         $this->_out('/DocMDP');
9354                         $this->_out('<<');
9355                         $this->_out('/Type/Sig');
9356                         $this->_out('/Filter/Adobe.PPKLite');
9357                         $this->_out('/SubFilter/adbe.pkcs7.detached');
9358                         $this->_out('/ByteRange[0 ********** ********** **********]');
9359                         $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9360                         $this->_out('/Reference');
9361                         $this->_out('[');
9362                         $this->_out('<<');
9363                         $this->_out('/Type/SigRef');
9364                         $this->_out('/TransformMethod/DocMDP');
9365                         $this->_out('/TransformParams');
9366                         $this->_out('<<');
9367                         $this->_out('/Type/TransformParams');
9368                         $this->_out('/V/1.2');
9369                         $this->_out('/P '.$this->signature_data['cert_type'].'');
9370                         $this->_out('>>');
9371                         $this->_out('>>');
9372                         $this->_out(']');
9373                         $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9374                         if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
9375                                 $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
9376                         }
9377                         if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
9378                                 $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
9379                         }
9380                         if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
9381                                 $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
9382                         }
9383                         if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
9384                                 $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
9385                         }
9386                         $this->_out('>>');
9387                         $this->_out('>>');
9388                 }
9389                 
9390                 /*
9391                 * Set User's Rights for PDF Reader
9392                 * WARNING: This works only using the Adobe private key with the setSignature() method!.
9393                 * Check the PDF Reference 8.7.1 Transform Methods, 
9394                 * Table 8.105 Entries in the UR transform parameters dictionary
9395                 * @param boolean $enable if true enable user's rights on PDF reader
9396                 * @param string $document Names specifying additional document-wide usage rights for the document. The only defined value is "/FullSave", which permits a user to save the document along with modified form and/or annotation data.
9397                 * @param string $annots Names specifying additional annotation-related usage rights for the document. Valid names in PDF 1.5 and later are /Create/Delete/Modify/Copy/Import/Export, which permit the user to perform the named operation on annotations.
9398                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 
9399                 * @param string $signature Names specifying additional signature-related usage rights for the document. The only defined value is /Modify, which permits a user to apply a digital signature to an existing signature form field or clear a signed signature form field.
9400                 * @access public
9401                 * @author Nicola Asuni
9402                 * @since 2.9.000 (2008-03-26)
9403                 */
9404                 public function setUserRights(
9405                                 $enable=true, 
9406                                 $document='/FullSave',
9407                                 $annots='/Create/Delete/Modify/Copy/Import/Export',
9408                                 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
9409                                 $signature='/Modify') {
9410                         $this->ur = $enable;
9411                         $this->ur_document = $document;
9412                         $this->ur_annots = $annots;
9413                         $this->ur_form = $form;
9414                         $this->ur_signature = $signature;
9415                 }
9416                 
9417                 /*
9418                 * Enable document signature (requires the OpenSSL Library).
9419                 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
9420                 * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
9421                 * @param mixed $private_key private key (string or filename prefixed with 'file://')
9422                 * @param string $private_key_password password
9423                 * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
9424                 * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
9425                 * @parm array $info array of option information: Name, Location, Reason, ContactInfo.
9426                 * @access public
9427                 * @author Nicola Asuni
9428                 * @since 4.6.005 (2009-04-24)
9429                 */
9430                 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
9431                         // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem
9432                         $this->sign = true;
9433                         $this->signature_data = array();
9434                         if (strlen($signing_cert) == 0) {
9435                                 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem';
9436                         }
9437                         if (strlen($private_key) == 0) {
9438                                 $private_key = $signing_cert;
9439                         }
9440                         $this->signature_data['signcert'] = $signing_cert;
9441                         $this->signature_data['privkey'] = $private_key;
9442                         $this->signature_data['password'] = $private_key_password;
9443                         $this->signature_data['extracerts'] = $extracerts;
9444                         $this->signature_data['cert_type'] = $cert_type;
9445                         $this->signature_data['info'] = array();
9446                 }
9447                 
9448                 /*
9449                 * Create a new page group.
9450                 * NOTE: call this function before calling AddPage()
9451                 * @param int $page starting group page (leave empty for next page).
9452                 * @access public
9453                 * @since 3.0.000 (2008-03-27)
9454                 */
9455                 public function startPageGroup($page='') {
9456                         if (empty($page)) {
9457                                 $page = $this->page + 1;
9458                         }
9459                         $this->newpagegroup[$page] = true;
9460                 }
9461
9462                 /**
9463                 * Defines an alias for the total number of pages.
9464                 * It will be substituted as the document is closed.
9465                 * @param string $alias The alias.
9466                 * @access public
9467                 * @since 1.4
9468                 * @see getAliasNbPages(), PageNo(), Footer()
9469                 */
9470                 public function AliasNbPages($alias='{nb}') {
9471                         $this->AliasNbPages = $alias;
9472                 }
9473                 
9474                 /**
9475                  * Returns the string alias used for the total number of pages.
9476          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9477                  * @return string
9478                  * @access public
9479                  * @since 4.0.018 (2008-08-08)
9480                  * @see AliasNbPages(), PageNo(), Footer()
9481                 */
9482                 public function getAliasNbPages() {
9483                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9484                                 return '{'.$this->AliasNbPages.'}';
9485             }
9486                         return $this->AliasNbPages;
9487                 }
9488
9489                 /**
9490                 * Defines an alias for the page number.
9491                 * It will be substituted as the document is closed.
9492                 * @param string $alias The alias.
9493                 * @access public
9494                 * @since 4.5.000 (2009-01-02)
9495                 * @see getAliasNbPages(), PageNo(), Footer()
9496                 */
9497                 public function AliasNumPage($alias='{pnb}') {
9498                         //Define an alias for total number of pages
9499                         $this->AliasNumPage = $alias;
9500                 }
9501                 
9502                 /**
9503                  * Returns the string alias used for the page number.
9504          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9505                  * @return string
9506                  * @access public
9507                  * @since 4.5.000 (2009-01-02)
9508                  * @see AliasNbPages(), PageNo(), Footer()
9509                 */
9510                 public function getAliasNumPage() {
9511                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9512                                 return '{'.$this->AliasNumPage.'}';
9513             }
9514                         return $this->AliasNumPage;
9515                 }
9516                 
9517                 /*
9518                 * Return the current page in the group.
9519                 * @return current page in the group
9520                 * @access public
9521                 * @since 3.0.000 (2008-03-27)
9522                 */
9523                 public function getGroupPageNo() {
9524                         return $this->pagegroups[$this->currpagegroup];
9525                 }
9526
9527                 /**
9528                 * Returns the current group page number formatted as a string.
9529                 * @access public
9530                 * @since 4.3.003 (2008-11-18)
9531                 * @see PaneNo(), formatPageNumber()
9532                 */
9533                 public function getGroupPageNoFormatted() {
9534                         return $this->formatPageNumber($this->getGroupPageNo());
9535         }
9536                 
9537                 /*
9538                  * Return the alias of the current page group
9539          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9540                  * (will be replaced by the total number of pages in this group).
9541                  * @return alias of the current page group
9542                  * @access public
9543                  * @since 3.0.000 (2008-03-27)
9544                 */
9545                 public function getPageGroupAlias() {
9546                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9547                                 return '{'.$this->currpagegroup.'}';
9548             }
9549                         return $this->currpagegroup;
9550                 }
9551                 
9552                 /*
9553                  * Return the alias for the page number on the current page group
9554          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9555                  * (will be replaced by the total number of pages in this group).
9556                  * @return alias of the current page group
9557                  * @access public
9558                  * @since 4.5.000 (2009-01-02)
9559                 */
9560                 public function getPageNumGroupAlias() {
9561                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9562                                 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
9563             }
9564                         return str_replace('{nb', '{pnb', $this->currpagegroup);
9565                 }
9566
9567                 /**
9568                 * Format the page numbers.
9569                 * This method can be overriden for custom formats.
9570                 * @param int $num page number
9571                 * @access protected
9572                 * @since 4.2.005 (2008-11-06)
9573                 */
9574                 protected function formatPageNumber($num) {
9575                         return number_format((float)$num, 0, '', '.');
9576                 }
9577
9578                 /**
9579                 * Format the page numbers on the Table Of Content.
9580                 * This method can be overriden for custom formats.
9581                 * @param int $num page number
9582                 * @access protected
9583                 * @since 4.5.001 (2009-01-04)
9584                 * @see addTOC()
9585                 */
9586                 protected function formatTOCPageNumber($num) {
9587                         return number_format((float)$num, 0, '', '.');
9588                 }
9589
9590         /**
9591                 * Returns the current page number formatted as a string.
9592                 * @access public
9593                 * @since 4.2.005 (2008-11-06)
9594                 * @see PaneNo(), formatPageNumber()
9595                 */
9596                 public function PageNoFormatted() {
9597                         return $this->formatPageNumber($this->PageNo());
9598         }
9599
9600         /*
9601                 * Put visibility settings.
9602                 * @access protected
9603                 * @since 3.0.000 (2008-03-27)
9604                 */
9605                 protected function _putocg() {
9606                         $this->_newobj();
9607                         $this->n_ocg_print = $this->n;
9608                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
9609                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
9610                         $this->_out('endobj');
9611                         $this->_newobj();
9612                         $this->n_ocg_view=$this->n;
9613                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
9614                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
9615                         $this->_out('endobj');
9616                 }
9617                 
9618                 /*
9619                 * Set the visibility of the successive elements.
9620                 * This can be useful, for instance, to put a background 
9621                 * image or color that will show on screen but won't print.
9622                 * @param string $v visibility mode. Legal values are: all, print, screen.
9623                 * @access public
9624                 * @since 3.0.000 (2008-03-27)
9625                 */
9626                 public function setVisibility($v) {
9627                         if ($this->openMarkedContent) {
9628                                 // close existing open marked-content
9629                                 $this->_out('EMC');
9630                                 $this->openMarkedContent = false;
9631                         }
9632                         switch($v) {
9633                                 case 'print': {
9634                                         $this->_out('/OC /OC1 BDC');
9635                                         $this->openMarkedContent = true;
9636                                         break;
9637                                 }
9638                                 case 'screen': {
9639                                         $this->_out('/OC /OC2 BDC');
9640                                         $this->openMarkedContent = true;
9641                                         break;
9642                                 }
9643                                 case 'all': {
9644                                         $this->_out('');
9645                                         break;
9646                                 }
9647                                 default: {
9648                                         $this->Error('Incorrect visibility: '.$v);
9649                                         break;
9650                                 }
9651                         }
9652                         $this->visibility = $v;
9653                 }
9654                 
9655                 /*
9656                 * Add transparency parameters to the current extgstate
9657                 * @param array $params parameters
9658                 * @return the number of extgstates
9659                 * @access protected
9660                 * @since 3.0.000 (2008-03-27)
9661                 */
9662                 protected function addExtGState($parms) {
9663                         $n = count($this->extgstates) + 1;
9664                         $this->extgstates[$n]['parms'] = $parms;
9665                         return $n;
9666                 }
9667                 
9668                 /*
9669                 * Add an extgstate
9670                 * @param array $gs extgstate
9671                 * @access protected
9672                 * @since 3.0.000 (2008-03-27)
9673                 */
9674                 protected function setExtGState($gs) {
9675                         $this->_out(sprintf('/GS%d gs', $gs));
9676                 }
9677                 
9678                 /*
9679                 * Put extgstates for object transparency
9680                 * @param array $gs extgstate
9681                 * @access protected
9682                 * @since 3.0.000 (2008-03-27)
9683                 */
9684                 protected function _putextgstates() {
9685                         $ne = count($this->extgstates);
9686                         for ($i = 1; $i <= $ne; ++$i) {
9687                                 $this->_newobj();
9688                                 $this->extgstates[$i]['n'] = $this->n;
9689                                 $this->_out('<</Type /ExtGState');
9690                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
9691                                         $this->_out('/'.$k.' '.$v);
9692                                 }
9693                                 $this->_out('>>');
9694                                 $this->_out('endobj');
9695                         }
9696                 }
9697                 
9698                 /*
9699                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
9700                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
9701                 * @param string $bm blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
9702                 * @access public
9703                 * @since 3.0.000 (2008-03-27)
9704                 */
9705                 public function setAlpha($alpha, $bm='Normal') {
9706                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
9707                         $this->setExtGState($gs);
9708                 }
9709
9710                 /*
9711                 * Set the default JPEG compression quality (1-100)
9712                 * @param int $quality JPEG quality, integer between 1 and 100
9713                 * @access public
9714                 * @since 3.0.000 (2008-03-27)
9715                 */
9716                 public function setJPEGQuality($quality) {
9717                         if (($quality < 1) OR ($quality > 100)) {
9718                                 $quality = 75;
9719                         }
9720                         $this->jpeg_quality = intval($quality);
9721                 }
9722                 
9723                 /*
9724                 * Set the default number of columns in a row for HTML tables.
9725                 * @param int $cols number of columns
9726                 * @access public
9727                 * @since 3.0.014 (2008-06-04)
9728                 */
9729                 public function setDefaultTableColumns($cols=4) { 
9730                         $this->default_table_columns = intval($cols); 
9731                 }
9732                 
9733                 /*
9734                 * Set the height of cell repect font height.
9735                 * @param int $h cell proportion respect font height (typical value = 1.25).
9736                 * @access public
9737                 * @since 3.0.014 (2008-06-04)
9738                 */
9739                 public function setCellHeightRatio($h) { 
9740                         $this->cell_height_ratio = $h; 
9741                 }
9742                 
9743                 /*
9744                 * return the height of cell repect font height.
9745                 * @access public
9746                 * @since 4.0.012 (2008-07-24)
9747                 */
9748                 public function getCellHeightRatio() { 
9749                         return $this->cell_height_ratio; 
9750                 }
9751                 
9752                 /*
9753                 * Set the PDF version (check PDF reference for valid values).
9754                 * Default value is 1.t
9755                 * @access public
9756                 * @since 3.1.000 (2008-06-09)
9757                 */
9758                 public function setPDFVersion($version='1.7') { 
9759                         $this->PDFVersion = $version;
9760                 }
9761                 
9762                 /*
9763                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
9764                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
9765                 * <ul>
9766                 * <li>HideToolbar boolean (Optional) A flag specifying whether to hide the viewer application's tool bars when the document is active. Default value: false.</li>
9767                 * <li>HideMenubar boolean (Optional) A flag specifying whether to hide the viewer application's menu bar when the document is active. Default value: false.</li>
9768                 * <li>HideWindowUI boolean (Optional) A flag specifying whether to hide user interface elements in the document's window (such as scroll bars and navigation controls), leaving only the document's contents displayed. Default value: false.</li>
9769                 * <li>FitWindow boolean (Optional) A flag specifying whether to resize the document's window to fit the size of the first displayed page. Default value: false.</li>
9770                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
9771                 * <li>DisplayDocTitle boolean (Optional; PDF 1.4) A flag specifying whether the window's title bar should display the document title taken from the Title entry of the document information dictionary (see Section 10.2.1, "Document Information Dictionary"). If false, the title bar should instead display the name of the PDF file containing the document. Default value: false.</li>
9772                 * <li>NonFullScreenPageMode name (Optional) The document's page mode, specifying how to display the document on exiting full-screen mode:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>UseOC Optional content group panel visible</li><ul>This entry is meaningful only if the value of the PageMode entry in the catalog dictionary (see Section 3.6.1, "Document Catalog") is FullScreen; it is ignored otherwise. Default value: UseNone.</li>
9773                 * <li>ViewArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be displayed when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9774                 * <li>ViewClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when viewing the document on the screen. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9775                 * <li>PrintArea name (Optional; PDF 1.4) The name of the page boundary representing the area of a page to be rendered when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9776                 * <li>PrintClip name (Optional; PDF 1.4) The name of the page boundary to which the contents of a page are to be clipped when printing the document. Valid values are (see Section 10.10.1, "Page Boundaries").:<ul><li>MediaBox</li><li>CropBox (default)</li><li>BleedBox</li><li>TrimBox</li><li>ArtBox</li></ul></li>
9777                 * <li>PrintScaling name (Optional; PDF 1.6) The page scaling option to be selected when a print dialog is displayed for this document. Valid values are: <ul><li>None, which indicates that the print dialog should reflect no page scaling</li><li>AppDefault (default), which indicates that applications should use the current print scaling</li><ul></li>
9778                 * <li>Duplex name (Optional; PDF 1.7) The paper handling option to use when printing the file from the print dialog. The following values are valid:<ul><li>Simplex - Print single-sided</li><li>DuplexFlipShortEdge - Duplex and flip on the short edge of the sheet</li><li>DuplexFlipLongEdge - Duplex and flip on the long edge of the sheet</li></ul>Default value: none</li>
9779                 * <li>PickTrayByPDFSize boolean (Optional; PDF 1.7) A flag specifying whether the PDF page size is used to select the input paper tray. This setting influences only the preset values used to populate the print dialog presented by a PDF viewer application. If PickTrayByPDFSize is true, the check box in the print dialog associated with input paper tray is checked. Note: This setting has no effect on Mac OS systems, which do not provide the ability to pick the input tray by size.</li>
9780                 * <li>PrintPageRange array (Optional; PDF 1.7) The page numbers used to initialize the print dialog box when the file is printed. The first page of the PDF file is denoted by 1. Each pair consists of the first and last pages in the sub-range. An odd number of integers causes this entry to be ignored. Negative numbers cause the entire array to be ignored. Default value: as defined by PDF viewer application</li>
9781                 * <li>NumCopies integer (Optional; PDF 1.7) The number of copies to be printed when the print dialog is opened for this file. Supported values are the integers 2 through 5. Values outside this range are ignored. Default value: as defined by PDF viewer application, but typically 1</li>
9782                 * </ul>
9783                 * @param array $preferences array of options.
9784                 * @author Nicola Asuni
9785                 * @access public
9786                 * @since 3.1.000 (2008-06-09)
9787                 */
9788                 public function setViewerPreferences($preferences) { 
9789                         $this->viewer_preferences = $preferences;
9790                 }
9791                 
9792                 /**
9793                 * Paints a linear colour gradient.
9794                 * @param float $x abscissa of the top left corner of the rectangle.
9795                 * @param float $y ordinate of the top left corner of the rectangle.
9796                 * @param float $w width of the rectangle.
9797                 * @param float $h height of the rectangle.
9798                 * @param array $col1 first color (RGB components).
9799                 * @param array $col2 second color (RGB components).
9800                 * @param array $coords array of the form (x1, y1, x2, y2) which defines the gradient vector (see linear_gradient_coords.jpg). The default value is from left to right (x1=0, y1=0, x2=1, y2=0).
9801                 * @author Andreas W�rmser, Nicola Asuni
9802                 * @since 3.1.000 (2008-06-09)
9803                 * @access public
9804                 */
9805                 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
9806                         $this->Clip($x, $y, $w, $h);
9807                         $this->Gradient(2, $col1, $col2, $coords);
9808                 }
9809                 
9810                 /**
9811                 * Paints a radial colour gradient.
9812                 * @param float $x abscissa of the top left corner of the rectangle.
9813                 * @param float $y ordinate of the top left corner of the rectangle.
9814                 * @param float $w width of the rectangle.
9815                 * @param float $h height of the rectangle.
9816                 * @param array $col1 first color (RGB components).
9817                 * @param array $col2 second color (RGB components).
9818                 * @param array $coords array of the form (fx, fy, cx, cy, r) where (fx, fy) is the starting point of the gradient with color1, (cx, cy) is the center of the circle with color2, and r is the radius of the circle (see radial_gradient_coords.jpg). (fx, fy) should be inside the circle, otherwise some areas will not be defined.
9819                 * @author Andreas W�rmser, Nicola Asuni
9820                 * @since 3.1.000 (2008-06-09)
9821                 * @access public
9822                 */
9823                 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
9824                         $this->Clip($x, $y, $w, $h);
9825                         $this->Gradient(3, $col1, $col2, $coords);
9826                 }
9827                 
9828                 /**
9829                 * Paints a coons patch mesh.
9830                 * @param float $x abscissa of the top left corner of the rectangle.
9831                 * @param float $y ordinate of the top left corner of the rectangle.
9832                 * @param float $w width of the rectangle.
9833                 * @param float $h height of the rectangle.
9834                 * @param array $col1 first color (lower left corner) (RGB components).
9835                 * @param array $col2 second color (lower right corner) (RGB components).
9836                 * @param array $col3 third color (upper right corner) (RGB components).
9837                 * @param array $col4 fourth color (upper left corner) (RGB components).
9838                 * @param array $coords <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>
9839                 * @param array $coords_min minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
9840                 * @param array $coords_max maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
9841                 * @author Andreas W�rmser, Nicola Asuni
9842                 * @since 3.1.000 (2008-06-09)
9843                 * @access public
9844                 */
9845                 public function CoonsPatchMesh($x, $y, $w, $h, $col1=array(), $col2=array(), $col3=array(), $col4=array(), $coords=array(0.00,0.0,0.33,0.00,0.67,0.00,1.00,0.00,1.00,0.33,1.00,0.67,1.00,1.00,0.67,1.00,0.33,1.00,0.00,1.00,0.00,0.67,0.00,0.33), $coords_min=0, $coords_max=1) {
9846                         $this->Clip($x, $y, $w, $h);        
9847                         $n = count($this->gradients) + 1;
9848                         $this->gradients[$n]['type'] = 6; //coons patch mesh
9849                         //check the coords array if it is the simple array or the multi patch array
9850                         if (!isset($coords[0]['f'])) {
9851                                 //simple array -> convert to multi patch array
9852                                 if (!isset($col1[1])) {
9853                                         $col1[1] = $col1[2] = $col1[0];
9854                                 }
9855                                 if (!isset($col2[1])) {
9856                                         $col2[1] = $col2[2] = $col2[0];
9857                                 }
9858                                 if (!isset($col3[1])) {
9859                                         $col3[1] = $col3[2] = $col3[0];
9860                                 }
9861                                 if (!isset($col4[1])) {
9862                                         $col4[1] = $col4[2] = $col4[0];
9863                                 }
9864                                 $patch_array[0]['f'] = 0;
9865                                 $patch_array[0]['points'] = $coords;
9866                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
9867                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
9868                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
9869                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
9870                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
9871                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
9872                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
9873                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
9874                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
9875                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
9876                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
9877                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
9878                         } else {
9879                                 //multi patch array
9880                                 $patch_array = $coords;
9881                         }
9882                         $bpcd = 65535; //16 BitsPerCoordinate
9883                         //build the data stream
9884                         $this->gradients[$n]['stream'] = '';
9885                         $count_patch = count($patch_array);
9886                         for ($i=0; $i < $count_patch; ++$i) {
9887                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
9888                                 $count_points = count($patch_array[$i]['points']);
9889                                 for ($j=0; $j < $count_points; ++$j) {
9890                                         //each point as 16 bit
9891                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
9892                                         if ($patch_array[$i]['points'][$j] < 0) {
9893                                                 $patch_array[$i]['points'][$j] = 0;
9894                                         }
9895                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
9896                                                 $patch_array[$i]['points'][$j] = $bpcd;
9897                                         }
9898                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
9899                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
9900                                 }
9901                                 $count_cols = count($patch_array[$i]['colors']);
9902                                 for ($j=0; $j < $count_cols; ++$j) {
9903                                         //each color component as 8 bit
9904                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
9905                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
9906                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
9907                                 }
9908                         }
9909                         //paint the gradient
9910                         $this->_out('/Sh'.$n.' sh');
9911                         //restore previous Graphic State
9912                         $this->_out('Q');
9913                 }
9914                 
9915                 /**
9916                 * Set a rectangular clipping area.
9917                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
9918                 * @param float $y ordinate of the top left corner of the rectangle.
9919                 * @param float $w width of the rectangle.
9920                 * @param float $h height of the rectangle.
9921                 * @author Andreas W�rmser, Nicola Asuni
9922                 * @since 3.1.000 (2008-06-09)
9923                 * @access protected
9924                 */
9925                 protected function Clip($x, $y, $w, $h) {
9926                         if ($this->rtl) {
9927                                 $x = $this->w - $x - $w;
9928                         }
9929                         //save current Graphic State
9930                         $s = 'q';
9931                         //set clipping area
9932                         $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
9933                         //set up transformation matrix for gradient
9934                         $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
9935                         $this->_out($s);
9936                 }
9937                                 
9938                 /**
9939                 * Output gradient.
9940                 * @param int $type type of gradient.
9941                 * @param array $col1 first color (RGB components).
9942                 * @param array $col2 second color (RGB components).
9943                 * @param array $coords array of coordinates.
9944                 * @author Andreas W�rmser, Nicola Asuni
9945                 * @since 3.1.000 (2008-06-09)
9946                 * @access protected
9947                 */
9948                 protected function Gradient($type, $col1, $col2, $coords) {
9949                         $n = count($this->gradients) + 1;
9950                         $this->gradients[$n]['type'] = $type;
9951                         if (!isset($col1[1])) {
9952                                 $col1[1]=$col1[2]=$col1[0];
9953                         }
9954                         $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
9955                         if (!isset($col2[1])) {
9956                                 $col2[1] = $col2[2] = $col2[0];
9957                         }
9958                         $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
9959                         $this->gradients[$n]['coords'] = $coords;
9960                         //paint the gradient
9961                         $this->_out('/Sh'.$n.' sh');
9962                         //restore previous Graphic State
9963                         $this->_out('Q');
9964                 }
9965                 
9966                 /**
9967                 * Output shaders.
9968                 * @author Andreas W�rmser, Nicola Asuni
9969                 * @since 3.1.000 (2008-06-09)
9970                 * @access protected
9971                 */
9972                 function _putshaders() {
9973                         foreach ($this->gradients as $id => $grad) {  
9974                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
9975                                         $this->_newobj();
9976                                         $this->_out('<<');
9977                                         $this->_out('/FunctionType 2');
9978                                         $this->_out('/Domain [0.0 1.0]');
9979                                         $this->_out('/C0 ['.$grad['col1'].']');
9980                                         $this->_out('/C1 ['.$grad['col2'].']');
9981                                         $this->_out('/N 1');
9982                                         $this->_out('>>');
9983                                         $this->_out('endobj');
9984                                         $f1 = $this->n;
9985                                 }
9986                                 $this->_newobj();
9987                                 $this->_out('<<');
9988                                 $this->_out('/ShadingType '.$grad['type']);
9989                                 $this->_out('/ColorSpace /DeviceRGB');
9990                                 if ($grad['type'] == 2) {
9991                                         $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
9992                                         $this->_out('/Function '.$f1.' 0 R');
9993                                         $this->_out('/Extend [true true] ');
9994                                         $this->_out('>>');
9995                                 } elseif ($grad['type'] == 3) {
9996                                         //x0, y0, r0, x1, y1, r1
9997                                         //at this this time radius of inner circle is 0
9998                                         $this->_out(sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]));
9999                                         $this->_out('/Function '.$f1.' 0 R');
10000                                         $this->_out('/Extend [true true] ');
10001                                         $this->_out('>>');
10002                                 } elseif ($grad['type'] == 6) {
10003                                         $this->_out('/BitsPerCoordinate 16');
10004                                         $this->_out('/BitsPerComponent 8');
10005                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
10006                                         $this->_out('/BitsPerFlag 8');
10007                                         $this->_out('/Length '.strlen($grad['stream']));
10008                                         $this->_out('>>');
10009                                         $this->_putstream($grad['stream']);
10010                                 }
10011                                 $this->_out('endobj');
10012                                 $this->gradients[$id]['id'] = $this->n;
10013                         }
10014                 }
10015
10016                 /**
10017                 * Output an arc
10018                 * @author Maxime Delorme, Nicola Asuni
10019                 * @since 3.1.000 (2008-06-09)
10020                 * @access protected
10021                 */
10022                 protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
10023                         $h = $this->h;
10024                         $this->_out(sprintf('%.2F %.2F %.2F %.2F %.2F %.2F c', $x1*$this->k, ($h-$y1)*$this->k, $x2*$this->k, ($h-$y2)*$this->k, $x3*$this->k, ($h-$y3)*$this->k));
10025                 }
10026                 
10027                 /**
10028                 * Draw the sector of a circle.
10029                 * It can be used for instance to render pie charts.
10030                 * @param float $xc abscissa of the center.
10031                 * @param float $yc ordinate of the center.
10032                 * @param float $r radius.
10033                 * @param float $a start angle (in degrees).
10034                 * @param float $b end angle (in degrees).
10035                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
10036                 * @param float $cw: indicates whether to go clockwise (default: true).
10037                 * @param float $o: origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.
10038                 * @author Maxime Delorme, Nicola Asuni
10039                 * @since 3.1.000 (2008-06-09)
10040                 * @access public
10041                 */
10042                 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
10043                         if ($this->rtl) {
10044                                 $xc = $this->w - $xc;
10045                         }
10046                         if ($cw) {
10047                                 $d = $b;
10048                                 $b = $o - $a;
10049                                 $a = $o - $d;
10050                         } else {
10051                                 $b += $o;
10052                                 $a += $o;
10053                         }
10054                         $a = ($a % 360) + 360;
10055                         $b = ($b % 360) + 360;
10056                         if ($a > $b) {
10057                                 $b +=360;
10058                         }
10059                         $b = $b / 360 * 2 * M_PI;
10060                         $a = $a / 360 * 2 * M_PI;
10061                         $d = $b - $a;
10062                         if ($d == 0 ) {
10063                                 $d = 2 * M_PI;
10064                         }
10065                         $k = $this->k;
10066                         $hp = $this->h;
10067                         if ($style=='F') {
10068                                 $op = 'f';
10069                         } elseif ($style=='FD' or $style=='DF') {
10070                                 $op = 'b';
10071                         } else {
10072                                 $op = 's';
10073                         }
10074                         if (sin($d/2)) {
10075                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
10076                         }
10077                         //first put the center
10078                         $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
10079                         //put the first point
10080                         $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
10081                         //draw the arc
10082                         if ($d < (M_PI/2)) {
10083                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10084                         } else {
10085                                 $b = $a + $d/4;
10086                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
10087                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10088                                 $a = $b;
10089                                 $b = $a + $d/4;
10090                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10091                                 $a = $b;
10092                                 $b = $a + $d/4;
10093                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b) );
10094                                 $a = $b;
10095                                 $b = $a + $d/4;
10096                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10097                         }
10098                         //terminate drawing
10099                         $this->_out($op);
10100                 }
10101                 
10102                 /**
10103                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
10104                 * Only vector drawing is supported, not text or bitmap. 
10105                 * Although the script was successfully tested with various AI format versions, best results are probably achieved with files that were exported in the AI3 format (tested with Illustrator CS2, Freehand MX and Photoshop CS2).
10106                 * @param string $file Name of the file containing the image.
10107                 * @param float $x Abscissa of the upper-left corner.
10108                 * @param float $y Ordinate of the upper-left corner.
10109                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
10110                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
10111                 * @param mixed $link URL or identifier returned by AddLink().
10112                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
10113                 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
10114                 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
10115                 * @param mixed $border Indicates if borders must be drawn around the image. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
10116                 * @author Valentin Schmidt, Nicola Asuni
10117                 * @since 3.1.000 (2008-06-09)
10118                 * @access public
10119                 */
10120                 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
10121                         if ($x === '') {
10122                                 $x = $this->x;
10123                         }
10124                         if ($y === '') {
10125                                 $y = $this->y;
10126                         }
10127                         $k = $this->k;
10128                         $data = file_get_contents($file);
10129                         if ($data === false) {
10130                                 $this->Error('EPS file not found: '.$file);
10131                         }
10132                         $regs = array();
10133                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
10134                         preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
10135                         if (count($regs) > 1) {
10136                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
10137                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
10138                                         $versexp = explode(' ', $version_str);
10139                                         $version = (float)array_pop($versexp);
10140                                         if ($version >= 9) {
10141                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
10142                                         }
10143                                 }
10144                         }
10145                         // strip binary bytes in front of PS-header
10146                         $start = strpos($data, '%!PS-Adobe');
10147                         if ($start > 0) {
10148                                 $data = substr($data, $start);
10149                         }
10150                         // find BoundingBox params
10151                         preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
10152                         if (count($regs) > 1) {
10153                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
10154                         } else {
10155                                 $this->Error('No BoundingBox found in EPS file: '.$file);
10156                         }
10157                         $start = strpos($data, '%%EndSetup');
10158                         if ($start === false) {
10159                                 $start = strpos($data, '%%EndProlog');
10160                         }
10161                         if ($start === false) {
10162                                 $start = strpos($data, '%%BoundingBox');
10163                         }
10164                         $data = substr($data, $start);
10165                         $end = strpos($data, '%%PageTrailer');
10166                         if ($end===false) {
10167                                 $end = strpos($data, 'showpage');
10168                         }
10169                         if ($end) {
10170                                 $data = substr($data, 0, $end);
10171                         }
10172                         if ($w > 0) {
10173                                 $scale_x = $w / (($x2 - $x1) / $k);
10174                                 if ($h > 0) {
10175                                         $scale_y = $h / (($y2 - $y1) / $k);
10176                                 } else {
10177                                         $scale_y = $scale_x;
10178                                         $h = ($y2 - $y1) / $k * $scale_y;
10179                                 }
10180                         } else {
10181                                 if ($h > 0) {
10182                                         $scale_y = $h / (($y2 - $y1) / $k);
10183                                         $scale_x = $scale_y;
10184                                         $w = ($x2-$x1) / $k * $scale_x;
10185                                 } else {
10186                                         $w = ($x2 - $x1) / $k;
10187                                         $h = ($y2 - $y1) / $k;
10188                                 }
10189                         }
10190                         // Check whether we need a new page first as this does not fit
10191                         if ($this->checkPageBreak($h, $y)) {
10192                                 $y = $this->GetY() + $this->cMargin;
10193                         }
10194                         // set bottomcoordinates
10195                         $this->img_rb_y = $y + $h;
10196                         // set alignment
10197                         if ($this->rtl) {
10198                                 if ($palign == 'L') {
10199                                         $ximg = $this->lMargin;
10200                                         // set right side coordinate
10201                                         $this->img_rb_x = $ximg + $w;
10202                                 } elseif ($palign == 'C') {
10203                                         $ximg = ($this->w - $x - $w) / 2;
10204                                         // set right side coordinate
10205                                         $this->img_rb_x = $ximg + $w;
10206                                 } else {
10207                                         $ximg = $this->w - $x - $w;
10208                                         // set left side coordinate
10209                                         $this->img_rb_x = $ximg;
10210                                 }
10211                         } else {
10212                                 if ($palign == 'R') {
10213                                         $ximg = $this->w - $this->rMargin - $w;
10214                                         // set left side coordinate
10215                                         $this->img_rb_x = $ximg;
10216                                 } elseif ($palign == 'C') {
10217                                         $ximg = ($this->w - $x - $w) / 2;
10218                                         // set right side coordinate
10219                                         $this->img_rb_x = $ximg + $w;
10220                                 } else {
10221                                         $ximg = $x;
10222                                         // set right side coordinate
10223                                         $this->img_rb_x = $ximg + $w;
10224                                 }
10225                         }
10226                         if ($useBoundingBox) {
10227                                 $dx = $ximg * $k - $x1;
10228                                 $dy = $y * $k - $y1;
10229                         } else {
10230                                 $dx = $ximg * $k;
10231                                 $dy = $y * $k;
10232                         }
10233                         // save the current graphic state
10234                         $this->_out('q'.$this->epsmarker);
10235                         // translate
10236                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
10237                         // scale
10238                         if (isset($scale_x)) {
10239                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
10240                         }
10241                         // handle pc/unix/mac line endings
10242                         preg_match('/[\r\n]+/s', $data, $regs);
10243                         $lines = explode($regs[0], $data);
10244                         $u=0;
10245                         $cnt = count($lines);
10246                         for ($i=0; $i < $cnt; ++$i) {
10247                                 $line = $lines[$i];
10248                                 if (($line == '') OR ($line{0} == '%')) {
10249                                         continue;
10250                                 }
10251                                 $len = strlen($line);
10252                                 $chunks = explode(' ', $line);
10253                                 $cmd = array_pop($chunks);
10254                                 // RGB
10255                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
10256                                         $b = array_pop($chunks); 
10257                                         $g = array_pop($chunks); 
10258                                         $r = array_pop($chunks);
10259                                         $this->_out(''.$r.' '.$g.' '.$b.' '.($cmd=='Xa'?'rg':'RG')); //substr($line, 0, -2).'rg' -> in EPS (AI8): c m y k r g b rg!
10260                                         continue;
10261                                 }
10262                                 switch ($cmd) {
10263                                         case 'm':
10264                                         case 'l':
10265                                         case 'v':
10266                                         case 'y':
10267                                         case 'c':
10268                                         case 'k':
10269                                         case 'K':
10270                                         case 'g':
10271                                         case 'G':
10272                                         case 's':
10273                                         case 'S':
10274                                         case 'J':
10275                                         case 'j':
10276                                         case 'w':
10277                                         case 'M':
10278                                         case 'd':
10279                                         case 'n':
10280                                         case 'v': {
10281                                                 $this->_out($line);
10282                                                 break;
10283                                         }
10284                                         case 'x': {// custom fill color
10285                                                 list($c,$m,$y,$k) = $chunks;
10286                                                 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
10287                                                 break;
10288                                         }
10289                                         case 'X': { // custom stroke color
10290                                                 list($c,$m,$y,$k) = $chunks;
10291                                                 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
10292                                                 break;
10293                                         }
10294                                         case 'Y':
10295                                         case 'N':
10296                                         case 'V':
10297                                         case 'L':
10298                                         case 'C': {
10299                                                 $line{$len-1} = strtolower($cmd);
10300                                                 $this->_out($line);
10301                                                 break;
10302                                         }
10303                                         case 'b':
10304                                         case 'B': {
10305                                                 $this->_out($cmd . '*');
10306                                                 break;
10307                                         }
10308                                         case 'f':
10309                                         case 'F': {
10310                                                 if ($u > 0) {
10311                                                         $isU = false;
10312                                                         $max = min($i+5, $cnt);
10313                                                         for ($j=$i+1; $j < $max; ++$j)
10314                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
10315                                                         if ($isU) {
10316                                                                 $this->_out('f*');
10317                                                         }
10318                                                 } else {
10319                                                         $this->_out('f*');
10320                                                 }
10321                                                 break;
10322                                         }
10323                                         case '*u': {
10324                                                 ++$u;
10325                                                 break;
10326                                         }
10327                                         case '*U': {
10328                                                 --$u;
10329                                                 break;
10330                                         }
10331                                 }
10332                         }
10333                         // restore previous graphic state
10334                         $this->_out($this->epsmarker.'Q');
10335                         if (!empty($border)) {
10336                                 $bx = $x;
10337                                 $by = $y;
10338                                 $this->x = $x;
10339                                 $this->y = $y;
10340                                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
10341                                 $this->x = $bx;
10342                                 $this->y = $by;
10343                         }
10344                         if ($link) {
10345                                 $this->Link($ximg, $y, $w, $h, $link, 0);
10346                         }
10347                         // set pointer to align the successive text/objects
10348                         switch($align) {
10349                                 case 'T':{
10350                                         $this->y = $y;
10351                                         $this->x = $this->img_rb_x;
10352                                         break;
10353                                 }
10354                                 case 'M':{
10355                                         $this->y = $y + round($h/2);
10356                                         $this->x = $this->img_rb_x;
10357                                         break;
10358                                 }
10359                                 case 'B':{
10360                                         $this->y = $this->img_rb_y;
10361                                         $this->x = $this->img_rb_x;
10362                                         break;
10363                                 }
10364                                 case 'N':{
10365                                         $this->SetY($this->img_rb_y);
10366                                         break;
10367                                 }
10368                                 default:{
10369                                         break;
10370                                 }
10371                         }
10372                         $this->endlinex = $this->img_rb_x;
10373                 }
10374                 
10375                 /**
10376                  * Set document barcode.
10377                  * @param string $bc barcode
10378                  * @access public
10379                  */
10380                 public function setBarcode($bc='') {
10381                         $this->barcode = $bc;
10382                 }
10383                 
10384                 /**
10385                  * Get current barcode.
10386                  * @return string
10387                  * @access public
10388                  * @since 4.0.012 (2008-07-24)
10389                  */
10390                 public function getBarcode() {
10391                         return $this->barcode;
10392                 }
10393                 
10394                 /**
10395                  * Print a Linear Barcode.
10396                  * @param string $code code to print
10397                  * @param string $type type of barcode.
10398                  * @param int $x x position in user units
10399                  * @param int $y y position in user units
10400                  * @param int $w width in user units
10401                  * @param int $h height in user units
10402                  * @param float $xres width of the smallest bar in user units
10403                  * @param array $style array of options:<ul><li>string $style['position'] barcode position inside the specified width: L = left (default for LTR); C = center; R = right (default for RTL); S = stretch</li><li>boolean $style['border'] if true prints a border around the barcode</li><li>int $style['padding'] padding to leave around the barcode in user units</li><li>array $style['fgcolor'] color array for bars and text</li><li>mixed $style['bgcolor'] color array for background or false for transparent</li><li>boolean $style["text"] boolean if true prints text below the barcode</li><li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li><li>int $style['stretchtext']: 0 = disabled; 1 = horizontal scaling only if necessary; 2 = forced horizontal scaling; 3 = character spacing only if necessary; 4 = forced character spacing</li></ul>
10404                  * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
10405                  * @author Nicola Asuni
10406                  * @since 3.1.000 (2008-06-09)
10407                  * @access public
10408                  */
10409                 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
10410                         if ($this->empty_string($code)) {
10411                                 return;
10412                         }
10413                         require_once(dirname(__FILE__).'/barcodes.php');
10414                         // save current graphic settings
10415                         $gvars = $this->getGraphicVars();
10416                         // create new barcode object
10417                         $barcodeobj = new TCPDFBarcode($code, $type);
10418                         $arrcode = $barcodeobj->getBarcodeArray();
10419                         if ($arrcode === false) {
10420                                 $this->Error('Error in 1D barcode string');
10421                         }
10422                         // set default values
10423                         if (!isset($style['position'])) {
10424                                 if ($this->rtl) {
10425                                         $style['position'] = 'R';
10426                                 } else {
10427                                         $style['position'] = 'L';
10428                                 }
10429                         }
10430                         if (!isset($style['padding'])) {
10431                                 $style['padding'] = 0;
10432                         }
10433                         if (!isset($style['fgcolor'])) {
10434                                 $style['fgcolor'] = array(0,0,0); // default black
10435                         }
10436                         if (!isset($style['bgcolor'])) {
10437                                 $style['bgcolor'] = false; // default transparent
10438                         }
10439                         if (!isset($style['border'])) {
10440                                 $style['border'] = false;
10441                         }
10442                         if (!isset($style['text'])) {
10443                                 $style['text'] = false;
10444                                 $fontsize = 0;
10445                         }
10446                         if ($style['text'] AND isset($style['font'])) {
10447                                 if (isset($style['fontsize'])) {
10448                                         $fontsize = $style['fontsize'];
10449                                 } else {
10450                                         $fontsize = 0;
10451                                 }
10452                                 $this->SetFont($style['font'], '', $fontsize);
10453                         }
10454                         if (!isset($style['stretchtext'])) {
10455                                 $style['stretchtext'] = 4;
10456                         }
10457                         // set foreground color
10458                         $this->SetDrawColorArray($style['fgcolor']);
10459                         $this->SetTextColorArray($style['fgcolor']);
10460                         if ($this->empty_string($w) OR ($w <= 0)) {
10461                                 if ($this->rtl) {
10462                                         $w = $this->x - $this->lMargin;
10463                                 } else {
10464                                         $w = $this->w - $this->rMargin - $this->x;
10465                                 }
10466                         }
10467                         if ($this->empty_string($x)) {
10468                                 $x = $this->GetX();
10469                         }
10470                         if ($this->rtl) {
10471                                 $x = $this->w - $x;
10472                         }
10473                         if ($this->empty_string($y)) {
10474                                 $y = $this->GetY();
10475                         }
10476                         if ($this->empty_string($xres)) {
10477                                 $xres = 0.4;
10478                         }
10479                         $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
10480                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
10481                         if ($this->empty_string($h) OR ($h <= 0)) {
10482                                 $h = 10 + $extraspace;
10483                         }
10484                         if ($this->checkPageBreak($h)) {
10485                                 $y = $this->y;
10486                         }
10487                         // maximum bar heigth
10488                         $barh = $h - $extraspace;
10489                         switch ($style['position']) {
10490                                 case 'L': { // left
10491                                         if ($this->rtl) {
10492                                                 $xpos = $x - $w;
10493                                         } else {
10494                                                 $xpos = $x;
10495                                         }
10496                                         break;
10497                                 }
10498                                 case 'C': { // center
10499                                         $xdiff = (($w - $fbw) / 2);
10500                                         if ($this->rtl) {
10501                                                 $xpos = $x - $w + $xdiff;
10502                                         } else {
10503                                                 $xpos = $x + $xdiff;
10504                                         }
10505                                         break;
10506                                 }
10507                                 case 'R': { // right
10508                                         if ($this->rtl) {
10509                                                 $xpos = $x - $fbw;
10510                                         } else {
10511                                                 $xpos = $x + $w - $fbw;
10512                                         }
10513                                         break;
10514                                 }
10515                                 case 'S': { // stretch
10516                                         $fbw = $w;
10517                                         $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
10518                                         if ($this->rtl) {
10519                                                 $xpos = $x - $w;
10520                                         } else {
10521                                                 $xpos = $x;
10522                                         }
10523                                         break;
10524                                 }
10525                         }
10526                         $xpos_rect = $xpos;
10527                         $xpos = $xpos_rect + $style['padding'];
10528                         $xpos_text = $xpos;
10529                         // barcode is always printed in LTR direction
10530                         $tempRTL = $this->rtl;
10531                         $this->rtl = false;
10532                         // print background color
10533                         if ($style['bgcolor']) {
10534                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']);
10535                         } elseif ($style['border']) {
10536                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
10537                         }
10538                         // print bars
10539                         if ($arrcode !== false) {
10540                                 foreach ($arrcode['bcode'] as $k => $v) {
10541                                         $bw = ($v['w'] * $xres);
10542                                         if ($v['t']) {
10543                                                 // draw a vertical bar
10544                                                 $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
10545                                                 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
10546                                         }
10547                                         $xpos += $bw;
10548                                 }
10549                         }
10550                         // print text
10551                         if ($style['text']) {
10552                                 // print text
10553                                 $this->x = $xpos_text;
10554                                 $this->y = $y + $style['padding'] + $barh; 
10555                                 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
10556                         }
10557                         // restore original direction
10558                         $this->rtl = $tempRTL;
10559                         // restore previous settings
10560                         $this->setGraphicVars($gvars);
10561                         // set bottomcoordinates
10562                         $this->img_rb_y = $y + $h;
10563                         if ($this->rtl) {
10564                                 // set left side coordinate
10565                                 $this->img_rb_x = ($this->w - $x - $w);
10566                         } else {
10567                                 // set right side coordinate
10568                                 $this->img_rb_x = $x + $w;
10569                         }
10570                         // set pointer to align the successive text/objects
10571                         switch($align) {
10572                                 case 'T':{
10573                                         $this->y = $y;
10574                                         $this->x = $this->img_rb_x;
10575                                         break;
10576                                 }
10577                                 case 'M':{
10578                                         $this->y = $y + round($h/2);
10579                                         $this->x = $this->img_rb_x;
10580                                         break;
10581                                 }
10582                                 case 'B':{
10583                                         $this->y = $this->img_rb_y;
10584                                         $this->x = $this->img_rb_x;
10585                                         break;
10586                                 }
10587                                 case 'N':{
10588                                         $this->SetY($this->img_rb_y);
10589                                         break;
10590                                 }
10591                                 default:{
10592                                         break;
10593                                 }
10594                         }
10595                 }
10596                 
10597                 /**
10598                  * This function is DEPRECATED, please use the new write1DBarcode() function.
10599                  * @param int $x x position in user units
10600                  * @param int $y y position in user units
10601                  * @param int $w width in user units
10602                  * @param int $h height position in user units
10603                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
10604                  * @param string $style barcode style
10605                  * @param string $font font for text
10606                  * @param int $xres x resolution
10607                  * @param string $code code to print
10608                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
10609                  * @access public
10610                  * @see write1DBarcode()
10611                  */
10612                 public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
10613                         // convert old settings for the new write1DBarcode() function.
10614                         $xres = 1 / $xres;
10615                         $newstyle = array(
10616                                 'position' => 'L',
10617                                 'border' => false,
10618                                 'padding' => 0,
10619                                 'fgcolor' => array(0,0,0),
10620                                 'bgcolor' => false,
10621                                 'text' => true,
10622                                 'font' => $font,
10623                                 'fontsize' => 8,
10624                                 'stretchtext' => 4
10625                         );
10626                         if ($style & 1) {
10627                                 $newstyle['border'] = true;
10628                         }
10629                         if ($style & 2) {
10630                                 $newstyle['bgcolor'] = false;
10631                         }
10632                         if ($style & 4) {
10633                                 $newstyle['position'] = 'C';
10634                         } elseif ($style & 8) {
10635                                 $newstyle['position'] = 'L';
10636                         } elseif ($style & 16) {
10637                                 $newstyle['position'] = 'R';
10638                         }
10639                         if ($style & 128) {
10640                                 $newstyle['text'] = true;
10641                         }
10642                         if ($style & 256) {
10643                                 $newstyle['stretchtext'] = 4;
10644                         }
10645                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
10646                 }
10647                 
10648                 /**
10649                  * Print 2D Barcode.
10650                  * @param string $code code to print
10651                  * @param string $type type of barcode.
10652                  * @param int $x x position in user units
10653                  * @param int $y y position in user units
10654                  * @param int $w width in user units
10655                  * @param int $h height in user units
10656                  * @param array $style array of options:<ul><li>boolean $style['border'] if true prints a border around the barcode</li><li>int $style['padding'] padding to leave around the barcode in user units</li><li>array $style['fgcolor'] color array for bars and text</li><li>mixed $style['bgcolor'] color array for background or false for transparent</li></ul>
10657                  * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
10658                  * @author Nicola Asuni
10659                  * @since 4.5.037 (2009-04-07)
10660                  * @access public
10661                  */
10662                 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
10663                         if ($this->empty_string($code)) {
10664                                 return;
10665                         }
10666                         require_once(dirname(__FILE__).'/2dbarcodes.php');
10667                         // save current graphic settings
10668                         $gvars = $this->getGraphicVars();
10669                         // create new barcode object
10670                         $barcodeobj = new TCPDF2DBarcode($code, $type);
10671                         $arrcode = $barcodeobj->getBarcodeArray();
10672                         if ($arrcode === false) {
10673                                 $this->Error('Error in 2D barcode string');
10674                         }
10675                         // set default values
10676                         if (!isset($style['padding'])) {
10677                                 $style['padding'] = 0;
10678                         }
10679                         if (!isset($style['fgcolor'])) {
10680                                 $style['fgcolor'] = array(0,0,0); // default black
10681                         }
10682                         if (!isset($style['bgcolor'])) {
10683                                 $style['bgcolor'] = false; // default transparent
10684                         }
10685                         if (!isset($style['border'])) {
10686                                 $style['border'] = false;
10687                         }
10688                         // set foreground color
10689                         $this->SetDrawColorArray($style['fgcolor']);
10690                         if ($this->empty_string($x)) {
10691                                 $x = $this->GetX();
10692                         }
10693                         if ($this->rtl) {
10694                                 $x = $this->w - $x;
10695                         }
10696                         if ($this->empty_string($y)) {
10697                                 $y = $this->GetY();
10698                         }
10699                         if ($this->empty_string($w) OR ($w <= 0)) {
10700                                 if ($this->rtl) {
10701                                         $w = $x - $this->lMargin;
10702                                 } else {
10703                                         $w = $this->w - $this->rMargin - $x;
10704                                 }
10705                         }
10706                         if ($this->empty_string($h) OR ($h <= 0)) {
10707                                 // 2d barcodes are square by default
10708                                 $h = $w;
10709                         }
10710                         if ($this->checkPageBreak($h)) {
10711                                 $y = $this->y;
10712                         }
10713                         // calculate barcode size (excluding padding)
10714                         $bw = $w - (2 * $style['padding']);
10715                         $bh = $h - (2 * $style['padding']);
10716                         // calculate starting coordinates
10717                         if ($this->rtl) {
10718                                 $xpos = $x - $w;
10719                         } else {
10720                                 $xpos = $x;
10721                         }
10722                         $xpos += $style['padding'];
10723                         $ypos = $y + $style['padding'];
10724                         // barcode is always printed in LTR direction
10725                         $tempRTL = $this->rtl;
10726                         $this->rtl = false;
10727                         // print background color
10728                         if ($style['bgcolor']) {
10729                                 $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']);
10730                         } elseif ($style['border']) {
10731                                 $this->Rect($x, $y, $w, $h, 'D');
10732                         }
10733                         // print barcode cells
10734                         if ($arrcode !== false) {
10735                                 $rows = $arrcode['num_rows'];
10736                                 $cols = $arrcode['num_cols'];
10737                                 // calculate dimension of single barcode cell
10738                                 $cw = $bw / $cols;
10739                                 $ch = $bh / $rows;
10740                                 // for each row
10741                                 for ($r = 0; $r < $rows; ++$r) {
10742                                         $xr = $xpos;
10743                                         // for each column
10744                                         for ($c = 0; $c < $cols; ++$c) {
10745                                                 if ($arrcode['bcode'][$r][$c] == 1) {
10746                                                         // draw a single barcode cell
10747                                                         $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
10748                                                 }
10749                                                 $xr += $cw;
10750                                         }
10751                                         $ypos += $ch;
10752                                 }
10753                         }
10754                         // restore original direction
10755                         $this->rtl = $tempRTL;
10756                         // restore previous settings
10757                         $this->setGraphicVars($gvars);
10758                         // set bottomcoordinates
10759                         $this->img_rb_y = $y + $h;
10760                         if ($this->rtl) {
10761                                 // set left side coordinate
10762                                 $this->img_rb_x = ($this->w - $x - $w);
10763                         } else {
10764                                 // set right side coordinate
10765                                 $this->img_rb_x = $x + $w;
10766                         }
10767                         // set pointer to align the successive text/objects
10768                         switch($align) {
10769                                 case 'T':{
10770                                         $this->y = $y;
10771                                         $this->x = $this->img_rb_x;
10772                                         break;
10773                                 }
10774                                 case 'M':{
10775                                         $this->y = $y + round($h/2);
10776                                         $this->x = $this->img_rb_x;
10777                                         break;
10778                                 }
10779                                 case 'B':{
10780                                         $this->y = $this->img_rb_y;
10781                                         $this->x = $this->img_rb_x;
10782                                         break;
10783                                 }
10784                                 case 'N':{
10785                                         $this->SetY($this->img_rb_y);
10786                                         break;
10787                                 }
10788                                 default:{
10789                                         break;
10790                                 }
10791                         }
10792                 }
10793                 
10794                 /**
10795                  * Returns an array containing current margins:
10796                  * <ul>
10797                                 <li>$ret['left'] = left  margin</li>
10798                                 <li>$ret['right'] = right margin</li>
10799                                 <li>$ret['top'] = top margin</li>
10800                                 <li>$ret['bottom'] = bottom margin</li>
10801                                 <li>$ret['header'] = header margin</li>
10802                                 <li>$ret['footer'] = footer margin</li>
10803                                 <li>$ret['cell'] = cell margin</li>
10804                  * </ul>
10805                  * @return array containing all margins measures 
10806                  * @access public
10807                  * @since 3.2.000 (2008-06-23)
10808                  */
10809                 public function getMargins() {
10810                         $ret = array(
10811                                 'left' => $this->lMargin,
10812                                 'right' => $this->rMargin,
10813                                 'top' => $this->tMargin,
10814                                 'bottom' => $this->bMargin,
10815                                 'header' => $this->header_margin,
10816                                 'footer' => $this->footer_margin,
10817                                 'cell' => $this->cMargin,
10818                         );
10819                         return $ret;
10820                 }
10821                 
10822                 /**
10823                  * Returns an array containing original margins:
10824                  * <ul>
10825                                 <li>$ret['left'] = left  margin</li>
10826                                 <li>$ret['right'] = right margin</li>
10827                  * </ul>
10828                  * @return array containing all margins measures 
10829                  * @access public
10830                  * @since 4.0.012 (2008-07-24)
10831                  */
10832                 public function getOriginalMargins() {
10833                         $ret = array(
10834                                 'left' => $this->original_lMargin,
10835                                 'right' => $this->original_rMargin
10836                         );
10837                         return $ret;
10838                 }
10839                 
10840                 /**
10841                  * Returns the current font size.
10842                  * @return current font size
10843                  * @access public
10844                  * @since 3.2.000 (2008-06-23)
10845                  */
10846                 public function getFontSize() {
10847                         return $this->FontSize;
10848                 }
10849                 
10850                 /**
10851                  * Returns the current font size in points unit.
10852                  * @return current font size in points unit
10853                  * @access public
10854                  * @since 3.2.000 (2008-06-23)
10855                  */
10856                 public function getFontSizePt() {
10857                         return $this->FontSizePt;
10858                 }
10859
10860                 /**
10861                  * Returns the current font family name.
10862                  * @return string current font family name
10863                  * @access public
10864                  * @since 4.3.008 (2008-12-05)
10865                  */
10866                 public function getFontFamily() {
10867                         return $this->FontFamily;
10868                 }
10869
10870                 /**
10871                  * Returns the current font style.
10872                  * @return string current font style
10873                  * @access public
10874                  * @since 4.3.008 (2008-12-05)
10875                  */
10876                 public function getFontStyle() {
10877                         return $this->FontStyle;
10878                 }
10879                 
10880                 /**
10881                  * Prints a cell (rectangular area) with optional borders, background color and html text string. 
10882                  * The upper-left corner of the cell corresponds to the current position. After the call, the current position moves to the right or to the next line.<br />
10883                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
10884                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
10885                  * @param float $h Cell minimum height. The cell extends automatically if needed.
10886                  * @param float $x upper-left corner X coordinate
10887                  * @param float $y upper-left corner Y coordinate
10888                  * @param string $html html text to print. Default value: empty string.
10889                  * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
10890                  * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL language)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
10891         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
10892                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
10893                  * @param boolean $reseth if true reset the last cell height (default true).
10894                  * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
10895                  * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
10896                  * @access public
10897                  * @uses MultiCell()
10898                  * @see Multicell(), writeHTML()
10899                  */
10900                 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
10901                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
10902                 }
10903                 
10904                 /**
10905                  * Returns the HTML DOM array.
10906                  * <ul><li>$dom[$key]['tag'] = true if tag, false otherwise;</li><li>$dom[$key]['value'] = tag name or text;</li><li>$dom[$key]['opening'] = true if opening tag, false otherwise;</li><li>$dom[$key]['attribute'] = array of attributes (attribute name is the key);</li><li>$dom[$key]['style'] = array of style attributes (attribute name is the key);</li><li>$dom[$key]['parent'] = id of parent element;</li><li>$dom[$key]['fontname'] = font family name;</li><li>$dom[$key]['fontstyle'] = font style;</li><li>$dom[$key]['fontsize'] = font size in points;</li><li>$dom[$key]['bgcolor'] = RGB array of background color;</li><li>$dom[$key]['fgcolor'] = RGB array of foreground color;</li><li>$dom[$key]['width'] = width in pixels;</li><li>$dom[$key]['height'] = height in pixels;</li><li>$dom[$key]['align'] = text alignment;</li><li>$dom[$key]['cols'] = number of colums in table;</li><li>$dom[$key]['rows'] = number of rows in table;</li></ul>
10907                  * @param string $html html code
10908                  * @return array
10909                  * @access protected
10910                  * @since 3.2.000 (2008-06-20)
10911                  */
10912                 protected function getHtmlDomArray($html) {
10913                         // remove all unsupported tags (the line below lists all supported tags)
10914                         $html = strip_tags($html, '<marker/><a><b><blockquote><br><br/><dd><del><div><dl><dt><em><font><h1><h2><h3><h4><h5><h6><hr><i><img><li><ol><p><pre><small><span><strong><sub><sup><table><tcpdf><td><th><thead><tr><tt><u><ul>');
10915                         //replace some blank characters
10916                         $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
10917                         $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
10918                         $html = preg_replace('@(\r\n|\r)@', "\n", $html);
10919                         $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
10920                         $html = strtr($html, $repTable);
10921                         while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html)) {
10922                                 // preserve newlines on <pre> tag
10923                                 $html = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html);
10924                         }
10925                         $html = str_replace("\n", ' ', $html);
10926                         // remove extra spaces from code
10927                         $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
10928                         $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
10929                         $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
10930                         $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
10931                         $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
10932                         $html = preg_replace('/<img/', ' <img', $html);
10933                         $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span></span>', $html);
10934                         $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
10935                         // trim string
10936                         $html = preg_replace('/^[\s]+/', '', $html);
10937                         $html = preg_replace('/[\s]+$/', '', $html);
10938                         // pattern for generic tag
10939                         $tagpattern = '/(<[^>]+>)/';
10940                         // explodes the string
10941                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
10942                         // count elements
10943                         $maxel = count($a);
10944                         $elkey = 0;
10945                         $key = 0;
10946                         // create an array of elements
10947                         $dom = array();
10948                         $dom[$key] = array();
10949                         // set first void element
10950                         $dom[$key]['tag'] = false;
10951                         $dom[$key]['value'] = '';
10952                         $dom[$key]['parent'] = 0;
10953                         $dom[$key]['fontname'] = $this->FontFamily;
10954                         $dom[$key]['fontstyle'] = $this->FontStyle;
10955                         $dom[$key]['fontsize'] = $this->FontSizePt;
10956                         $dom[$key]['bgcolor'] = false;
10957                         $dom[$key]['fgcolor'] = $this->fgcolor;
10958                         $dom[$key]['align'] = '';
10959                         $dom[$key]['listtype'] = '';
10960                         $thead = false; // true when we are inside the THEAD tag
10961                         ++$key;
10962                         $level = array();
10963                         array_push($level, 0); // root
10964                         while ($elkey < $maxel) {
10965                                 $dom[$key] = array();
10966                                 $element = $a[$elkey];
10967                                 $dom[$key]['elkey'] = $elkey;
10968                                 if (preg_match($tagpattern, $element)) {
10969                                         // html tag
10970                                         $element = substr($element, 1, -1);
10971                                         // get tag name
10972                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
10973                                         $tagname = strtolower($tag[1]);
10974                                         // check if we are inside a table header
10975                                         if ($tagname == 'thead') {
10976                                                 if ($element{0} == '/') {
10977                                                         $thead = false;
10978                                                 } else {
10979                                                         $thead = true;
10980                                                 }
10981                                                 ++$elkey;
10982                                                 continue;
10983                                         }
10984                                         $dom[$key]['tag'] = true;
10985                                         $dom[$key]['value'] = $tagname;
10986                                         if ($element{0} == '/') {
10987                                                 // closing html tag
10988                                                 $dom[$key]['opening'] = false;
10989                                                 $dom[$key]['parent'] = end($level);
10990                                                 array_pop($level);
10991                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
10992                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
10993                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
10994                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
10995                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
10996                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
10997                                                 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
10998                                                         $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
10999                                                 }
11000                                                 // set the number of columns in table tag
11001                                                 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
11002                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
11003                                                 }
11004                                                 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11005                                                         $dom[($dom[$key]['parent'])]['content'] = '';
11006                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
11007                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
11008                                                         }
11009                                                         $key = $i;
11010                                                 }
11011                                                 // store header rows on a new table
11012                                                 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) {
11013                                                         if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
11014                                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
11015                                                         }
11016                                                         for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
11017                                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
11018                                                         }
11019                                                 }
11020                                                 if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
11021                                                         $dom[($dom[$key]['parent'])]['thead'] .= '</table>';
11022                                                 }
11023                                         } else {
11024                                                 // opening html tag
11025                                                 $dom[$key]['opening'] = true;
11026                                                 $dom[$key]['parent'] = end($level);
11027                                                 if (substr($element, -1, 1) != '/') {
11028                                                         // not self-closing tag
11029                                                         array_push($level, $key);
11030                                                         $dom[$key]['self'] = false;
11031                                                 } else {
11032                                                         $dom[$key]['self'] = true;
11033                                                 }
11034                                                 // copy some values from parent
11035                                                 $parentkey = 0;
11036                                                 if ($key > 0) {
11037                                                         $parentkey = $dom[$key]['parent'];
11038                                                         $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
11039                                                         $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
11040                                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
11041                                                         $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
11042                                                         $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
11043                                                         $dom[$key]['align'] = $dom[$parentkey]['align'];
11044                                                         $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11045                                                 }
11046                                                 // get attributes
11047                                                 preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
11048                                                 $dom[$key]['attribute'] = array(); // reset attribute array
11049                                                 while (list($id, $name) = each($attr_array[1])) {
11050                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
11051                                                 }
11052                                                 // split style attributes
11053                                                 if (isset($dom[$key]['attribute']['style'])) {
11054                                                         // get style attributes
11055                                                         preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
11056                                                         $dom[$key]['style'] = array(); // reset style attribute array
11057                                                         while (list($id, $name) = each($style_array[1])) {
11058                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
11059                                                         }
11060                                                         // --- get some style attributes ---
11061                                                         if (isset($dom[$key]['style']['font-family'])) {
11062                                                                 // font family
11063                                                                 if (isset($dom[$key]['style']['font-family'])) {
11064                                                                         $fontslist = explode(',', strtolower($dom[$key]['style']['font-family']));
11065                                                                         foreach ($fontslist as $font) {
11066                                                                                 $font = trim(strtolower($font));
11067                                                                                 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11068                                                                                         $dom[$key]['fontname'] = $font;
11069                                                                                         break;
11070                                                                                 }
11071                                                                         }
11072                                                                 }
11073                                                         }
11074                                                         // list-style-type
11075                                                         if (isset($dom[$key]['style']['list-style-type'])) {
11076                                                                 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
11077                                                                 if ($dom[$key]['listtype'] == 'inherit') {
11078                                                                         $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11079                                                                 }
11080                                                         }
11081                                                         // font size
11082                                                         if (isset($dom[$key]['style']['font-size'])) {
11083                                                                 $fsize = trim($dom[$key]['style']['font-size']);
11084                                                                 switch ($fsize) {
11085                                                                         // absolute-size
11086                                                                         case 'xx-small': {
11087                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
11088                                                                                 break;
11089                                                                         }
11090                                                                         case 'x-small': {
11091                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
11092                                                                                 break;
11093                                                                         }
11094                                                                         case 'small': {
11095                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
11096                                                                                 break;
11097                                                                         }
11098                                                                         case 'medium': {
11099                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
11100                                                                                 break;
11101                                                                         }
11102                                                                         case 'large': {
11103                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
11104                                                                                 break;
11105                                                                         }
11106                                                                         case 'x-large': {
11107                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
11108                                                                                 break;
11109                                                                         }
11110                                                                         case 'xx-large': {
11111                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
11112                                                                                 break;
11113                                                                         }
11114                                                                         // relative-size
11115                                                                         case 'smaller': {
11116                                                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
11117                                                                                 break;
11118                                                                         }
11119                                                                         case 'larger': {
11120                                                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
11121                                                                                 break;
11122                                                                         }
11123                                                                         default: {
11124                                                                                 $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
11125                                                                         }
11126                                                                 }
11127                                                         }
11128                                                         // font style
11129                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
11130                                                                 $dom[$key]['fontstyle'] .= 'B';
11131                                                         }
11132                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
11133                                                                 $dom[$key]['fontstyle'] .= '"I';
11134                                                         }
11135                                                         // font color
11136                                                         if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
11137                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
11138                                                         }
11139                                                         // background color
11140                                                         if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
11141                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
11142                                                         }
11143                                                         // text-decoration
11144                                                         if (isset($dom[$key]['style']['text-decoration'])) {
11145                                                                 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
11146                                                                 foreach ($decors as $dec) {
11147                                                                         $dec = trim($dec);
11148                                                                         if (!$this->empty_string($dec)) {
11149                                                                                 if ($dec{0} == 'u') {
11150                                                                                         $dom[$key]['fontstyle'] .= 'U';
11151                                                                                 } elseif ($dec{0} == 'l') {
11152                                                                                         $dom[$key]['fontstyle'] .= 'D';
11153                                                                                 }
11154                                                                         }
11155                                                                 }
11156                                                         }
11157                                                         // check for width attribute
11158                                                         if (isset($dom[$key]['style']['width'])) {
11159                                                                 $dom[$key]['width'] = $dom[$key]['style']['width'];
11160                                                         }
11161                                                         // check for height attribute
11162                                                         if (isset($dom[$key]['style']['height'])) {
11163                                                                 $dom[$key]['height'] = $dom[$key]['style']['height'];
11164                                                         }
11165                                                         // check for text alignment
11166                                                         if (isset($dom[$key]['style']['text-align'])) {
11167                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
11168                                                         }
11169                                                         // check for border attribute
11170                                                         if (isset($dom[$key]['style']['border'])) {
11171                                                                 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
11172                                                         }
11173                                                 }
11174                                                 // check for font tag
11175                                                 if ($dom[$key]['value'] == 'font') {
11176                                                         // font family
11177                                                         if (isset($dom[$key]['attribute']['face'])) {
11178                                                                 $fontslist = explode(',', strtolower($dom[$key]['attribute']['face']));
11179                                                                 foreach ($fontslist as $font) {
11180                                                                         $font = trim(strtolower($font));
11181                                                                         if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11182                                                                                 $dom[$key]['fontname'] = $font;
11183                                                                                 break;
11184                                                                         }
11185                                                                 }
11186                                                         }
11187                                                         // font size
11188                                                         if (isset($dom[$key]['attribute']['size'])) {
11189                                                                 if ($key > 0) {
11190                                                                         if ($dom[$key]['attribute']['size']{0} == '+') {
11191                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
11192                                                                         } elseif ($dom[$key]['attribute']['size']{0} == '-') {
11193                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
11194                                                                         } else {
11195                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11196                                                                         }
11197                                                                 } else {
11198                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11199                                                                 }
11200                                                         }
11201                                                 }
11202                                                 // force natural alignment for lists
11203                                                 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
11204                                                         AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
11205                                                         if ($this->rtl) {
11206                                                                 $dom[$key]['align'] = 'R';
11207                                                         } else {
11208                                                                 $dom[$key]['align'] = 'L';
11209                                                         }
11210                                                 }
11211                                                 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
11212                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
11213                                                 }
11214                                                 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
11215                                                         $dom[$key]['fontstyle'] .= 'B';
11216                                                 }
11217                                                 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
11218                                                         $dom[$key]['fontstyle'] .= 'I';
11219                                                 }
11220                                                 if ($dom[$key]['value'] == 'u') {
11221                                                         $dom[$key]['fontstyle'] .= 'U';
11222                                                 }
11223                                                 if ($dom[$key]['value'] == 'del') {
11224                                                         $dom[$key]['fontstyle'] .= 'D';
11225                                                 }
11226                                                 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
11227                                                         $dom[$key]['fontname'] = $this->default_monospaced_font;
11228                                                 }
11229                                                 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
11230                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
11231                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
11232                                                         $dom[$key]['fontstyle'] .= 'B';
11233                                                 }
11234                                                 if (($dom[$key]['value'] == 'table')) {
11235                                                         $dom[$key]['rows'] = 0; // number of rows
11236                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
11237                                                         $dom[$key]['thead'] = ''; // table header rows
11238                                                 }
11239                                                 if (($dom[$key]['value'] == 'tr')) {
11240                                                         $dom[$key]['cols'] = 0;
11241                                                         // store the number of rows on table element
11242                                                         ++$dom[($dom[$key]['parent'])]['rows'];
11243                                                         // store the TR elements IDs on table element
11244                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
11245                                                         if ($thead) {
11246                                                                 $dom[$key]['thead'] = true;
11247                                                         } else {
11248                                                                 $dom[$key]['thead'] = false;
11249                                                         }
11250                                                 }
11251                                                 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
11252                                                         if (isset($dom[$key]['attribute']['colspan'])) {
11253                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
11254                                                         } else {
11255                                                                 $colspan = 1;
11256                                                         }
11257                                                         $dom[$key]['attribute']['colspan'] = $colspan;
11258                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
11259                                                 }
11260                                                 // set foreground color attribute
11261                                                 if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
11262                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
11263                                                 }
11264                                                 // set background color attribute
11265                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
11266                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
11267                                                 }
11268                                                 // check for width attribute
11269                                                 if (isset($dom[$key]['attribute']['width'])) {
11270                                                         $dom[$key]['width'] = $dom[$key]['attribute']['width'];
11271                                                 }
11272                                                 // check for height attribute
11273                                                 if (isset($dom[$key]['attribute']['height'])) {
11274                                                         $dom[$key]['height'] = $dom[$key]['attribute']['height'];
11275                                                 }
11276                                                 // check for text alignment
11277                                                 if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
11278                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
11279                                                 }
11280                                         } // end opening tag
11281                                 } else {
11282                                         // text
11283                                         $dom[$key]['tag'] = false;
11284                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
11285                                         $dom[$key]['parent'] = end($level);
11286                                 }
11287                                 ++$elkey;
11288                                 ++$key;
11289                         }
11290                         return $dom;
11291                 }
11292                 
11293                 /**
11294                  * Allows to preserve some HTML formatting (limited support).<br />
11295                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
11296                  * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
11297                  * @param string $html text to display
11298                  * @param boolean $ln if true add a new line after text (default = true)
11299                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
11300                  * @param boolean $reseth if true reset the last cell height (default false).
11301                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
11302                  * @param string $align Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
11303                  * @access public
11304                  */
11305                 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
11306                         $gvars = $this->getGraphicVars();
11307                         // store current values
11308                         $prevPage = $this->page;
11309                         $prevlMargin = $this->lMargin;
11310                         $prevrMargin = $this->rMargin;
11311                         $curfontname = $this->FontFamily;
11312                         $curfontstyle = $this->FontStyle;
11313                         $curfontsize = $this->FontSizePt;       
11314                         $this->newline = true;
11315                         $minstartliney = $this->y;
11316                         $yshift = 0;
11317                         $startlinepage = $this->page;
11318                         $newline = true;
11319                         $loop = 0;
11320                         $curpos = 0;
11321                         $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf');
11322                         $this->premode = false;
11323                         if (isset($this->PageAnnots[$this->page])) {
11324                                 $pask = count($this->PageAnnots[$this->page]);
11325                         } else {
11326                                 $pask = 0;
11327                         }
11328                         if (isset($this->footerlen[$this->page])) {
11329                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11330                         } else {
11331                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11332                         }
11333                         $startlinepos = $this->footerpos[$this->page];
11334                         $lalign = $align;
11335                         $plalign = $align;
11336                         if ($this->rtl) {
11337                                 $w = $this->x - $this->lMargin;
11338                         } else {
11339                                 $w = $this->w - $this->rMargin - $this->x;
11340                         }
11341                         $w -= (2 * $this->cMargin);
11342                         if ($cell) {
11343                                 if ($this->rtl) {
11344                                         $this->x -= $this->cMargin;
11345                                 } else {
11346                                         $this->x += $this->cMargin;
11347                                 }
11348                         }
11349                         if ($this->customlistindent >= 0) {
11350                                 $this->listindent = $this->customlistindent;
11351                         } else {
11352                                 $this->listindent = $this->GetStringWidth('0000');
11353                         }
11354                         $this->listnum = 0;
11355                         if (($this->empty_string($this->lasth)) OR ($reseth)) {
11356                                 //set row height
11357                                 $this->lasth = $this->FontSize * $this->cell_height_ratio; 
11358                         }
11359                         $dom = $this->getHtmlDomArray($html);
11360                         $maxel = count($dom);
11361                         $key = 0;
11362                         while ($key < $maxel) {
11363                                 if ($dom[$key]['tag'] OR ($key == 0)) {
11364                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
11365                                                 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
11366                                         }
11367                                         // vertically align image in line
11368                                         if ((!$this->newline)
11369                                                 AND ($dom[$key]['value'] == 'img')
11370                                                 AND (isset($dom[$key]['attribute']['height']))
11371                                                 AND ($dom[$key]['attribute']['height'] > 0)) {
11372                                                 // get image height
11373                                                 $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
11374                                                 if (!$this->InFooter) {
11375                                                         // check for page break
11376                                                         $this->checkPageBreak($imgh);
11377                                                 }
11378                                                 if ($this->page > $startlinepage) {
11379                                                         // fix lines splitted over two pages
11380                                                         if (isset($this->footerlen[$startlinepage])) {
11381                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11382                                                         }
11383                                                         // line to be moved one page forward
11384                                                         $pagebuff = $this->getPageBuffer($startlinepage);
11385                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11386                                                         $tstart = substr($pagebuff, 0, $startlinepos);
11387                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11388                                                         // remove line start from previous page
11389                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11390                                                         $pagebuff = $this->getPageBuffer($this->page);
11391                                                         $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11392                                                         $tend = substr($pagebuff, $this->intmrk[$this->page]);
11393                                                         // add line start to current page
11394                                                         $yshift = $minstartliney - $this->y;
11395                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11396                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11397                                                         // shift the annotations and links
11398                                                         if (isset($this->PageAnnots[$startlinepage])) {
11399                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11400                                                                         if ($pak >= $pask) {
11401                                                                                 $this->PageAnnots[$this->page][] = $pac;
11402                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11403                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11404                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11405                                                                         }
11406                                                                 }
11407                                                         }
11408                                                         $startlinepos = $this->intmrk[$this->page];
11409                                                         $startlinepage = $this->page;
11410                                                         $startliney = $this->y;
11411                                                 }
11412                                                 
11413                                                 $this->y += (($curfontsize / $this->k) - $imgh);
11414                                                 $minstartliney = min($this->y, $minstartliney);
11415                                                 
11416                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
11417                                                 // account for different font size
11418                                                 $pfontname = $curfontname;
11419                                                 $pfontstyle = $curfontstyle;
11420                                                 $pfontsize = $curfontsize;
11421                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
11422                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
11423                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
11424                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
11425                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
11426                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
11427                                                         if (is_numeric($fontsize) AND ($fontsize > 0)
11428                                                                 AND is_numeric($curfontsize) AND ($curfontsize > 0)
11429                                                                 AND ($fontsize != $curfontsize) AND (!$this->newline)
11430                                                                 AND ($key < ($maxel - 1))
11431                                                                 ) {
11432                                                                 if ((!$this->newline) AND ($this->page > $startlinepage)) {
11433                                                                         // fix lines splitted over two pages
11434                                                                         if (isset($this->footerlen[$startlinepage])) {
11435                                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11436                                                                         }
11437                                                                         // line to be moved one page forward
11438                                                                         $pagebuff = $this->getPageBuffer($startlinepage);
11439                                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11440                                                                         $tstart = substr($pagebuff, 0, $startlinepos);
11441                                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11442                                                                         // remove line start from previous page
11443                                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11444                                                                         $pagebuff = $this->getPageBuffer($this->page);
11445                                                                         $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11446                                                                         $tend = substr($pagebuff, $this->intmrk[$this->page]);
11447                                                                         // add line start to current page
11448                                                                         $yshift = $minstartliney - $this->y;
11449                                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11450                                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11451                                                                         // shift the annotations and links
11452                                                                         if (isset($this->PageAnnots[$startlinepage])) {
11453                                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11454                                                                                         if ($pak >= $pask) {
11455                                                                                                 $this->PageAnnots[$this->page][] = $pac;
11456                                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11457                                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11458                                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11459                                                                                         }
11460                                                                                 }
11461                                                                         }
11462                                                                 }
11463                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
11464                                                                 $minstartliney = min($this->y, $minstartliney);
11465                                                         }
11466                                                         $curfontname = $fontname;
11467                                                         $curfontstyle = $fontstyle;
11468                                                         $curfontsize = $fontsize;
11469                                                 }
11470                                         }
11471                                         if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
11472                                                 $plalign = '';
11473                                         }
11474                                         // get current position on page buffer
11475                                         $curpos = $this->pagelen[$startlinepage];
11476                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
11477                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
11478                                                 $wfill = true;
11479                                         } else {
11480                                                 $wfill = $fill | false;
11481                                         }
11482                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
11483                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
11484                                         }
11485                                         if (isset($dom[$key]['align'])) {
11486                                                 $lalign = $dom[$key]['align'];
11487                                         }
11488                                         if ($this->empty_string($lalign)) {
11489                                                 $lalign = $align;
11490                                         }
11491                                 }
11492                                 // align lines
11493                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
11494                                         $newline = true;
11495                                         // we are at the beginning of a new line
11496                                         if (isset($startlinex)) {
11497                                                 $yshift = $minstartliney - $startliney;
11498                                                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
11499                                                         $yshift = 0;
11500                                                 }
11501                                                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11502                                                         // the last line must be shifted to be aligned as requested
11503                                                         $linew = abs($this->endlinex - $startlinex);
11504                                                         $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11505                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11506                                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11507                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11508                                                         } elseif (isset($opentagpos)) {
11509                                                                 $midpos = $opentagpos;
11510                                                         } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11511                                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11512                                                                 $midpos = $this->footerpos[$startlinepage];
11513                                                         } else {
11514                                                                 $midpos = 0;
11515                                                         }
11516                                                         if ($midpos > 0) {
11517                                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11518                                                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11519                                                         } else {
11520                                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11521                                                                 $pend = '';
11522                                                         }
11523                                                         // calculate shifting amount
11524                                                         $tw = $w;
11525                                                         if ($this->lMargin != $prevlMargin) {
11526                                                                 $tw += ($prevlMargin - $this->lMargin);
11527                                                         }
11528                                                         if ($this->rMargin != $prevrMargin) {
11529                                                                 $tw += ($prevrMargin - $this->rMargin);
11530                                                         }
11531                                                         $mdiff = abs($tw - $linew);
11532                                                         $t_x = 0;
11533                                                         if ($plalign == 'C') {
11534                                                                 if ($this->rtl) {
11535                                                                         $t_x = -($mdiff / 2);
11536                                                                 } else {
11537                                                                         $t_x = ($mdiff / 2);
11538                                                                 }
11539                                                         } elseif (($plalign == 'R') AND (!$this->rtl)) {
11540                                                                 // right alignment on LTR document
11541                                                                 $t_x = $mdiff;  
11542                                                         } elseif (($plalign == 'L') AND ($this->rtl)) {
11543                                                                 // left alignment on RTL document
11544                                                                 $t_x = -$mdiff;
11545                                                         } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
11546                                                                 // Justification
11547                                                                 if ($this->rtl OR $this->tmprtl) {
11548                                                                         $t_x = $this->lMargin - $this->endlinex;
11549                                                                 }
11550                                                                 $no = 0;
11551                                                                 $ns = 0;
11552                                                                 $pmidtemp = $pmid;
11553                                                                 // escape special characters
11554                                                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11555                                                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11556                                                                 // search spaces
11557                                                                 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
11558                                                                         $maxkk = count($lnstring[1]) - 1;
11559                                                                         for ($kk=0; $kk <= $maxkk; ++$kk) {
11560                                                                                 // restore special characters
11561                                                                                 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
11562                                                                                 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
11563                                                                                 if ($kk == $maxkk) {
11564                                                                                         if ($this->rtl OR $this->tmprtl) {
11565                                                                                                 $tvalue = ltrim($lnstring[1][$kk]);
11566                                                                                         } else {
11567                                                                                                 $tvalue = rtrim($lnstring[1][$kk]);
11568                                                                                         }
11569                                                                                 } else {
11570                                                                                         $tvalue = $lnstring[1][$kk];
11571                                                                                 }
11572                                                                                 // count spaces on line
11573                                                                                 $no += substr_count($lnstring[1][$kk], chr(32));
11574                                                                                 $ns += substr_count($tvalue, chr(32));
11575                                                                         }
11576                                                                         if ($this->rtl OR $this->tmprtl) {
11577                                                                                 $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
11578                                                                         }
11579                                                                         // calculate additional space to add to each space
11580                                                                         $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k;
11581                                                                         $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k;
11582                                                                         $nsmax = $ns;
11583                                                                         $ns = 0;
11584                                                                         reset($lnstring);
11585                                                                         $offset = 0;
11586                                                                         $strcount = 0;
11587                                                                         $prev_epsposbeg = 0;
11588                                                                         global $spacew;
11589                                                                         while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
11590                                                                                 if ($this->rtl OR $this->tmprtl) {
11591                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
11592                                                                                 } else {
11593                                                                                         $spacew = ($spacewidth * $ns);
11594                                                                                 }
11595                                                                                 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
11596                                                                                 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
11597                                                                                 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
11598                                                                                 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
11599                                                                                         OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
11600                                                                                         // shift EPS images
11601                                                                                         $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
11602                                                                                         $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
11603                                                                                         $pmid_b = substr($pmid, 0, $epsposbeg);
11604                                                                                         $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
11605                                                                                         $pmid_e = substr($pmid, $epsposend);
11606                                                                                         $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
11607                                                                                         $offset = $epsposend;
11608                                                                                         continue;
11609                                                                                 }
11610                                                                                 $prev_epsposbeg = $epsposbeg;
11611                                                                                 $currentxpos = 0;
11612                                                                                 // shift blocks of code
11613                                                                                 switch ($strpiece[2][0]) {
11614                                                                                         case 'Td':
11615                                                                                         case 'cm':
11616                                                                                         case 'm':
11617                                                                                         case 'l': {
11618                                                                                                 // get current X position
11619                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11620                                                                                                 $currentxpos = $xmatches[1];
11621                                                                                                 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
11622                                                                                                         if ($strcount == $maxkk) {
11623                                                                                                                 if ($this->rtl OR $this->tmprtl) {
11624                                                                                                                         $tvalue = $lnstring[1][$strcount];
11625                                                                                                                 } else {
11626                                                                                                                         $tvalue = rtrim($lnstring[1][$strcount]);
11627                                                                                                                 }
11628                                                                                                         } else {
11629                                                                                                                 $tvalue = $lnstring[1][$strcount];
11630                                                                                                         }
11631                                                                                                         $ns += substr_count($tvalue, chr(32));
11632                                                                                                         ++$strcount;
11633                                                                                                 }
11634                                                                                                 if ($this->rtl OR $this->tmprtl) {
11635                                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
11636                                                                                                 }
11637                                                                                                 // justify block
11638                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11639                                                                                                         create_function('$matches', 'global $spacew;
11640                                                                                                         $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11641                                                                                                         return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
11642                                                                                                 break;
11643                                                                                         }
11644                                                                                         case 're': {
11645                                                                                                 // get current X position
11646                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11647                                                                                                 $currentxpos = $xmatches[1];
11648                                                                                                 // justify block
11649                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11650                                                                                                         create_function('$matches', 'global $spacew;
11651                                                                                                         $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11652                                                                                                         return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
11653                                                                                                 break;
11654                                                                                         }
11655                                                                                         case 'c': {
11656                                                                                                 // get current X position
11657                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11658                                                                                                 $currentxpos = $xmatches[1];
11659                                                                                                 // justify block
11660                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11661                                                                                                         create_function('$matches', 'global $spacew;
11662                                                                                                         $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
11663                                                                                                         $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
11664                                                                                                         $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
11665                                                                                                         return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
11666                                                                                                 break;
11667                                                                                         }
11668                                                                                 }
11669                                                                                 // shift the annotations and links
11670                                                                                 if (isset($this->PageAnnots[$this->page])) {
11671                                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11672                                                                                                 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
11673                                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
11674                                                                                                         $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
11675                                                                                                         break;
11676                                                                                                 }
11677                                                                                         }
11678                                                                                 }
11679                                                                         } // end of while
11680                                                                         // remove markers
11681                                                                         $pmid = str_replace('x*#!#*x', '', $pmid);
11682                                                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11683                                                                                 // multibyte characters
11684                                                                                 $spacew = $spacewidthu;
11685                                                                                 $pmidtemp = $pmid;
11686                                                                                 // escape special characters
11687                                                                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11688                                                                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11689                                                                                 $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
11690                                                                                                         create_function('$matches', 'global $spacew;
11691                                                                                                         $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
11692                                                                                                         $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
11693                                                                                                         return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp);
11694                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
11695                                                                                 $endlinepos = strlen($pstart."\n".$pmid."\n");
11696                                                                         } else {
11697                                                                                 // non-unicode (single-byte characters)
11698                                                                                 $rs = sprintf("%.3F Tw", $spacewidth);
11699                                                                                 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
11700                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
11701                                                                                 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
11702                                                                         }
11703                                                                 }
11704                                                         } // end of J
11705                                                         if (($t_x != 0) OR ($yshift < 0)) {
11706                                                                 // shift the line
11707                                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11708                                                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11709                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11710                                                                 // shift the annotations and links
11711                                                                 if (isset($this->PageAnnots[$this->page])) {
11712                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11713                                                                                 if ($pak >= $pask) {
11714                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
11715                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
11716                                                                                 }
11717                                                                         }
11718                                                                 }
11719                                                                 $this->y -= $yshift;
11720                                                         }
11721                                                 }
11722                                         }
11723                                         $this->newline = false;
11724                                         $pbrk = $this->checkPageBreak($this->lasth);
11725                                         $this->SetFont($fontname, $fontstyle, $fontsize);
11726                                         if ($wfill) {
11727                                                 $this->SetFillColorArray($this->bgcolor);
11728                                         }
11729                                         $startlinex = $this->x;
11730                                         $startliney = $this->y;
11731                                         $minstartliney = $this->y;
11732                                         $startlinepage = $this->page;
11733                                         if (isset($endlinepos) AND (!$pbrk)) {
11734                                                 $startlinepos = $endlinepos;
11735                                                 unset($endlinepos);
11736                                         } else {
11737                                                 if (isset($this->footerlen[$this->page])) {
11738                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11739                                                 } else {
11740                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11741                                                 }
11742                                                 $startlinepos = $this->footerpos[$this->page];
11743                                         }
11744                                         $plalign = $lalign;
11745                                         if (isset($this->PageAnnots[$this->page])) {
11746                                                 $pask = count($this->PageAnnots[$this->page]);
11747                                         } else {
11748                                                 $pask = 0;
11749                                         }
11750                                 }
11751                                 if (isset($opentagpos)) {
11752                                         unset($opentagpos);
11753                                 }
11754                                 if ($dom[$key]['tag']) {
11755                                         if ($dom[$key]['opening']) {
11756                                                 if ($dom[$key]['value'] == 'table') {
11757                                                         if ($this->rtl) {
11758                                                                 $wtmp = $this->x - $this->lMargin;
11759                                                         } else {
11760                                                                 $wtmp = $this->w - $this->rMargin - $this->x;
11761                                                         }
11762                                                         $wtmp -= (2 * $this->cMargin);
11763                                                         // calculate cell width
11764                                                         if (isset($dom[$key]['width'])) {
11765                                                                 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11766                                                         } else {
11767                                                                 $table_width = $wtmp;
11768                                                         }
11769                                                 }
11770                                                 // table content is handled in a special way
11771                                                 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11772                                                         $trid = $dom[$key]['parent'];
11773                                                         $table_el = $dom[$trid]['parent'];
11774                                                         if (!isset($dom[$table_el]['cols'])) {
11775                                                                 $dom[$table_el]['cols'] = $trid['cols'];
11776                                                         }
11777                                                         $oldmargin = $this->cMargin;
11778                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
11779                                                                 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
11780                                                         } else {
11781                                                                 $currentcmargin = 0;            
11782                                                         }
11783                                                         $this->cMargin = $currentcmargin;
11784                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
11785                                                                 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
11786                                                         } else {
11787                                                                 $cellspacing = 0;
11788                                                         }
11789                                                         if ($this->rtl) {
11790                                                                 $cellspacingx = -$cellspacing;
11791                                                         } else {
11792                                                                 $cellspacingx = $cellspacing;
11793                                                         }
11794                                                         $colspan = $dom[$key]['attribute']['colspan'];
11795                                                         $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
11796                                                         if (isset($dom[$key]['width'])) {
11797                                                                 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11798                                                         } else {
11799                                                                 $cellw = $wtmp;
11800                                                         }
11801                                                         if (isset($dom[$key]['height'])) {
11802                                                                 // minimum cell height
11803                                                                 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
11804                                                         } else {
11805                                                                 $cellh = 0;
11806                                                         }
11807                                                         $cellw -= $cellspacing;
11808                                                         if (isset($dom[$key]['content'])) {
11809                                                                 $cell_content = $dom[$key]['content'];
11810                                                         } else {
11811                                                                 $cell_content = '&nbsp;';
11812                                                         }
11813                                                         $tagtype = $dom[$key]['value'];
11814                                                         $parentid = $key;
11815                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
11816                                                                 // move $key index forward
11817                                                                 ++$key;
11818                                                         }
11819                                                         if (!isset($dom[$trid]['startpage'])) {
11820                                                                 $dom[$trid]['startpage'] = $this->page;
11821                                                         } else {
11822                                                                 $this->setPage($dom[$trid]['startpage']);
11823                                                         }
11824                                                         if (!isset($dom[$trid]['starty'])) {
11825                                                                 $dom[$trid]['starty'] = $this->y;
11826                                                         } else {
11827                                                                 $this->y = $dom[$trid]['starty'];
11828                                                         }
11829                                                         if (!isset($dom[$trid]['startx'])) {
11830                                                                 $dom[$trid]['startx'] = $this->x;
11831                                                         }
11832                                                         $this->x += ($cellspacingx / 2);                                                
11833                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
11834                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
11835                                                         } else {
11836                                                                 $rowspan = 1;
11837                                                         }
11838                                                         // skip row-spanned cells started on the previous rows
11839                                                         if (isset($dom[$table_el]['rowspans'])) {
11840                                                                 $rsk = 0;
11841                                                                 $rskmax = count($dom[$table_el]['rowspans']);
11842                                                                 while ($rsk < $rskmax) {
11843                                                                         $trwsp = $dom[$table_el]['rowspans'][$rsk];
11844                                                                         $rsstartx = $trwsp['startx'];
11845                                                                         $rsendx = $trwsp['endx'];
11846                                                                         // account for margin changes
11847                                                                         if ($trwsp['startpage'] < $this->page) {
11848                                                                                 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
11849                                                                                         $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
11850                                                                                         $rsstartx -= $dl;
11851                                                                                         $rsendx -= $dl;
11852                                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
11853                                                                                         $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
11854                                                                                         $rsstartx += $dl;
11855                                                                                         $rsendx += $dl;
11856                                                                                 }
11857                                                                         }
11858                                                                         if  (($trwsp['rowspan'] > 0)
11859                                                                                 AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
11860                                                                                 AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
11861                                                                                 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
11862                                                                                 // set the starting X position of the current cell
11863                                                                                 $this->x = $rsendx + $cellspacingx;
11864                                                                                 if (($trwsp['rowspan'] == 1)
11865                                                                                         AND (isset($dom[$trid]['endy']))
11866                                                                                         AND (isset($dom[$trid]['endpage']))
11867                                                                                         AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
11868                                                                                         // set ending Y position for row
11869                                                                                         $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11870                                                                                         $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
11871                                                                                 }
11872                                                                                 $rsk = 0;
11873                                                                         } else {
11874                                                                                 ++$rsk;
11875                                                                         }
11876                                                                 }
11877                                                         }
11878                                                         // add rowspan information to table element
11879                                                         if ($rowspan > 1) {
11880                                                                 if (isset($this->footerlen[$this->page])) {
11881                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11882                                                                 } else {
11883                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11884                                                                 }
11885                                                                 $trintmrkpos = $this->footerpos[$this->page];
11886                                                                 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
11887                                                         }
11888                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
11889                                                         if ($rowspan > 1) {
11890                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
11891                                                         }
11892                                                         // push background colors
11893                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
11894                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
11895                                                         }
11896                                                         $prevLastH = $this->lasth;
11897                                                         // ****** write the cell content ******
11898                                                         $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
11899                                                         $this->lasth = $prevLastH;
11900                                                         $this->cMargin = $oldmargin;
11901                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
11902                                                         // update the end of row position
11903                                                         if ($rowspan <= 1) {
11904                                                                 if (isset($dom[$trid]['endy'])) {
11905                                                                         if ($this->page == $dom[$trid]['endpage']) {
11906                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
11907                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
11908                                                                                 $dom[$trid]['endy'] = $this->y;
11909                                                                         }
11910                                                                 } else {
11911                                                                         $dom[$trid]['endy'] = $this->y;
11912                                                                 }
11913                                                                 if (isset($dom[$trid]['endpage'])) {
11914                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
11915                                                                 } else {
11916                                                                         $dom[$trid]['endpage'] = $this->page;
11917                                                                 }                                                               
11918                                                         } else {
11919                                                                 // account for row-spanned cells
11920                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
11921                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
11922                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
11923                                                         }
11924                                                         if (isset($dom[$table_el]['rowspans'])) {
11925                                                                 // update endy and endpage on rowspanned cells
11926                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
11927                                                                         if ($trwsp['rowspan'] > 0) {
11928                                                                                 if (isset($dom[$trid]['endpage'])) {
11929                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
11930                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11931                                                                                         } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
11932                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
11933                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
11934                                                                                         } else {
11935                                                                                                 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
11936                                                                                         }
11937                                                                                 }
11938                                                                         }
11939                                                                 }
11940                                                         }
11941                                                         $this->x += ($cellspacingx / 2);                                                        
11942                                                 } else {
11943                                                         // opening tag (or self-closing tag)
11944                                                         if (!isset($opentagpos)) {
11945                                                                 if (!$this->InFooter) {
11946                                                                         if (isset($this->footerlen[$this->page])) {
11947                                                                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11948                                                                         } else {
11949                                                                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11950                                                                         }
11951                                                                         $opentagpos = $this->footerpos[$this->page];
11952                                                                 }
11953                                                         }
11954                                                         $this->openHTMLTagHandler($dom, $key, $cell);
11955                                                 }
11956                                         } else {
11957                                                 // closing tag
11958                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
11959                                         }
11960                                 } elseif (strlen($dom[$key]['value']) > 0) {
11961                                         // print list-item
11962                                         if (!$this->empty_string($this->lispacer)) {
11963                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
11964                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
11965                                                 $minstartliney = $this->y;
11966                                                 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
11967                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
11968                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
11969                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
11970                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
11971                                                         $minstartliney = min($this->y, $minstartliney);
11972                                                 }
11973                                         }
11974                                         // text
11975                                         $this->htmlvspace = 0;
11976                                         if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
11977                                                 // reverse spaces order
11978                                                 $len1 = strlen($dom[$key]['value']);
11979                                                 $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
11980                                                 $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
11981                                                 $tmpstr = '';
11982                                                 if ($rsp > 0) {
11983                                                         $tmpstr .= substr($dom[$key]['value'], -$rsp);
11984                                                 }
11985                                                 $tmpstr .= trim($dom[$key]['value']);
11986                                                 if ($lsp > 0) {
11987                                                         $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
11988                                                 }
11989                                                 $dom[$key]['value'] = $tmpstr;
11990                                         }
11991                                         if ($newline) {
11992                                                 if (!$this->premode) {
11993                                                         if (($this->rtl OR $this->tmprtl)) {
11994                                                                 $dom[$key]['value'] = rtrim($dom[$key]['value']);
11995                                                         } else {
11996                                                                 $dom[$key]['value'] = ltrim($dom[$key]['value']);
11997                                                         }
11998                                                 }
11999                                                 $newline = false;
12000                                                 $firstblock = true;
12001                                         } else {
12002                                                 $firstblock = false;
12003                                         }
12004                                         $strrest = '';
12005                                         if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
12006                                                 // HTML <a> Link
12007                                                 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
12008                                         } else {
12009                                                 $ctmpmargin = $this->cMargin;
12010                                                 $this->cMargin = 0;
12011                                                 // ****** write only until the end of the line and get the rest ******
12012                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
12013                                                 $this->cMargin = $ctmpmargin;
12014                                         }
12015                                         if (strlen($strrest) > 0) {
12016                                                 // store the remaining string on the previous $key position
12017                                                 $this->newline = true;
12018                                                 if ($cell) {
12019                                                         if ($this->rtl) {
12020                                                                 $this->x -= $this->cMargin;
12021                                                         } else {
12022                                                                 $this->x += $this->cMargin;
12023                                                         }
12024                                                 }
12025                                                 if ($strrest == $dom[$key]['value']) {
12026                                                         // used to avoid infinite loop
12027                                                         ++$loop;
12028                                                 } else {
12029                                                         $loop = 0;
12030                                                 }
12031                                                 $dom[$key]['value'] = ltrim($strrest);
12032                                                 if ($loop < 3) {
12033                                                         --$key;
12034                                                 }
12035                                         } else {
12036                                                 $loop = 0;
12037                                         }
12038                                 }
12039                                 ++$key;
12040                         } // end for each $key
12041                         // align the last line
12042                         if (isset($startlinex)) {
12043                                 $yshift = $minstartliney - $startliney;
12044                                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
12045                                         $yshift = 0;
12046                                 }
12047                                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
12048                                         // the last line must be shifted to be aligned as requested
12049                                         $linew = abs($this->endlinex - $startlinex);
12050                                         $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
12051                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12052                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12053                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
12054                                         } elseif (isset($opentagpos)) {
12055                                                 $midpos = $opentagpos;
12056                                         } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12057                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12058                                                 $midpos = $this->footerpos[$startlinepage];
12059                                         } else {
12060                                                 $midpos = 0;
12061                                         }
12062                                         if ($midpos > 0) {
12063                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
12064                                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
12065                                         } else {
12066                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
12067                                                 $pend = '';
12068                                         }       
12069                                         // calculate shifting amount
12070                                         $tw = $w;
12071                                         if ($this->lMargin != $prevlMargin) {
12072                                                 $tw += ($prevlMargin - $this->lMargin);
12073                                         }
12074                                         if ($this->rMargin != $prevrMargin) {
12075                                                 $tw += ($prevrMargin - $this->rMargin);
12076                                         }
12077                                         $mdiff = abs($tw - $linew);
12078                                         if ($plalign == 'C') {
12079                                                 if ($this->rtl) {
12080                                                         $t_x = -($mdiff / 2);
12081                                                 } else {
12082                                                         $t_x = ($mdiff / 2);
12083                                                 }
12084                                         } elseif (($plalign == 'R') AND (!$this->rtl)) {
12085                                                 // right alignment on LTR document
12086                                                 $t_x = $mdiff;
12087                                         } elseif (($plalign == 'L') AND ($this->rtl)) {
12088                                                 // left alignment on RTL document
12089                                                 $t_x = -$mdiff;
12090                                         } else {
12091                                                 $t_x = 0;
12092                                         }
12093                                         if (($t_x != 0) OR ($yshift < 0)) {
12094                                                 // shift the line
12095                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
12096                                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
12097                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
12098                                                 // shift the annotations and links
12099                                                 if (isset($this->PageAnnots[$this->page])) {
12100                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
12101                                                                 if ($pak >= $pask) {
12102                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
12103                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
12104                                                                 }
12105                                                         }
12106                                                 }
12107                                                 $this->y -= $yshift;
12108                                         }
12109                                 }
12110                         }
12111                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
12112                                 $this->Ln($this->lasth);
12113                         }
12114                         // restore previous values
12115                         $this->setGraphicVars($gvars);
12116                         if ($this->page > $prevPage) {
12117                                 $this->lMargin = $this->pagedim[$this->page]['olm'];
12118                                 $this->rMargin = $this->pagedim[$this->page]['orm'];
12119                         }
12120                         unset($dom);
12121                 }
12122                 
12123                 /**
12124                  * Process opening tags.
12125                  * @param array $dom html dom array 
12126                  * @param int $key current element id
12127                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12128                  * @access protected
12129                  */
12130                 protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
12131                         $tag = $dom[$key];
12132                         $parent = $dom[($dom[$key]['parent'])];
12133                         $firstorlast = ($key == 1);
12134                         // check for text direction attribute
12135                         if (isset($tag['attribute']['dir'])) {
12136                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
12137                         } else {
12138                                 $this->tmprtl = false;
12139                         }
12140                         //Opening tag
12141                         switch($tag['value']) {
12142                                 case 'table': {
12143                                         $cp = 0;
12144                                         $cs = 0;
12145                                         $dom[$key]['rowspans'] = array();
12146                                         if (!$this->empty_string($dom[$key]['thead'])) {
12147                                                 // set table header
12148                                                 $this->thead = $dom[$key]['thead'];
12149                                         }
12150                                         if (isset($tag['attribute']['cellpadding'])) {
12151                                                 $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
12152                                                 $this->oldcMargin = $this->cMargin;
12153                                                 $this->cMargin = $cp;
12154                                         }
12155                                         if (isset($tag['attribute']['cellspacing'])) {
12156                                                 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
12157                                         }
12158                                         $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
12159                                         break;
12160                                 }
12161                                 case 'tr': {
12162                                         // array of columns positions
12163                                         $dom[$key]['cellpos'] = array();
12164                                         break;
12165                                 }
12166                                 case 'hr': {
12167                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12168                                         $this->htmlvspace = 0;
12169                                         $wtmp = $this->w - $this->lMargin - $this->rMargin;
12170                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
12171                                                 $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
12172                                         } else {
12173                                                 $hrWidth = $wtmp;
12174                                         }
12175                                         $x = $this->GetX();
12176                                         $y = $this->GetY();
12177                                         $prevlinewidth = $this->GetLineWidth();
12178                                         $this->Line($x, $y, $x + $hrWidth, $y);
12179                                         $this->SetLineWidth($prevlinewidth);
12180                                         $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
12181                                         break;
12182                                 }
12183                                 case 'a': {
12184                                         if (array_key_exists('href', $tag['attribute'])) {
12185                                                 $this->HREF['url'] = $tag['attribute']['href'];
12186                                         }
12187                                         $this->HREF['color'] = $this->htmlLinkColorArray;
12188                                         $this->HREF['style'] = $this->htmlLinkFontStyle;
12189                                         if (array_key_exists('style', $tag['attribute'])) {
12190                                                 // get style attributes
12191                                                 preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12192                                                 $astyle = array();
12193                                                 while (list($id, $name) = each($style_array[1])) {
12194                                                         $name = strtolower($name);
12195                                                         $astyle[$name] = trim($style_array[2][$id]);
12196                                                 }
12197                                                 if (isset($astyle['color'])) {
12198                                                         $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
12199                                                 }
12200                                                 if (isset($astyle['text-decoration'])) {
12201                                                         $this->HREF['style'] = '';
12202                                                         $decors = explode(' ', strtolower($astyle['text-decoration']));
12203                                                         foreach ($decors as $dec) {
12204                                                                 $dec = trim($dec);
12205                                                                 if (!$this->empty_string($dec)) {
12206                                                                         if ($dec{0} == 'u') {
12207                                                                                 $this->HREF['style'] .= 'U';
12208                                                                         } elseif ($dec{0} == 'l') {
12209                                                                                 $this->HREF['style'] .= 'D';
12210                                                                         }
12211                                                                 }
12212                                                         }
12213                                                 }
12214                                         }               
12215                                         break;
12216                                 }
12217                                 case 'img': {
12218                                         if (isset($tag['attribute']['src'])) {
12219                                                 // replace relative path with real server path
12220                                                 if ($tag['attribute']['src'][0] == '/') {
12221                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
12222                                                 }
12223                                                 $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
12224                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
12225                                                 if (!isset($tag['attribute']['width'])) {
12226                                                         $tag['attribute']['width'] = 0;
12227                                                 }
12228                                                 if (!isset($tag['attribute']['height'])) {
12229                                                         $tag['attribute']['height'] = 0;
12230                                                 }
12231                                                 //if (!isset($tag['attribute']['align'])) {
12232                                                         // the only alignment supported is "bottom"
12233                                                         // further development is required for other modes.
12234                                                         $tag['attribute']['align'] = 'bottom';
12235                                                 //} 
12236                                                 switch($tag['attribute']['align']) {
12237                                                         case 'top': {
12238                                                                 $align = 'T';
12239                                                                 break;
12240                                                         }
12241                                                         case 'middle': {
12242                                                                 $align = 'M';
12243                                                                 break;
12244                                                         }
12245                                                         case 'bottom': {
12246                                                                 $align = 'B';
12247                                                                 break;
12248                                                         }
12249                                                         default: {
12250                                                                 $align = 'B';
12251                                                                 break;
12252                                                         }
12253                                                 }
12254                                                 $fileinfo = pathinfo($tag['attribute']['src']);
12255                                                 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
12256                                                         $type = strtolower($fileinfo['extension']);
12257                                                 }
12258                                                 $prevy = $this->y;
12259                                                 $xpos = $this->GetX();
12260                                                 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
12261                                                         if ($this->rtl) {
12262                                                                 $xpos += $this->GetStringWidth(' ');
12263                                                         } else {
12264                                                                 $xpos -= $this->GetStringWidth(' ');
12265                                                         }
12266                                                 }
12267                                                 $imglink = '';
12268                                                 if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
12269                                                         $imglink = $this->HREF['url'];
12270                                                         if ($imglink{0} == '#') {
12271                                                                 // convert url to internal link
12272                                                                 $page = intval(substr($imglink, 1));
12273                                                                 $imglink = $this->AddLink();
12274                                                                 $this->SetLink($imglink, 0, $page);
12275                                                         }
12276                                                 }
12277                                                 $border = 0;
12278                                                 if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
12279                                                         // currently only support 1 (frame) or a combination of 'LTRB'
12280                                                         $border = $tag['attribute']['border'];
12281                                                 }
12282                                                 $iw = '';
12283                                                 if (isset($tag['attribute']['width'])) {
12284                                                         $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
12285                                                 }
12286                                                 $ih = '';
12287                                                 if (isset($tag['attribute']['height'])) {
12288                                                         $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
12289                                                 }
12290                                                 if (($type == 'eps') OR ($type == 'ai')) {
12291                                                         $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
12292                                                 } else {
12293                                                         $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
12294                                                 }
12295                                                 switch($align) {
12296                                                         case 'T': {
12297                                                                 $this->y = $prevy;
12298                                                                 break;
12299                                                         }
12300                                                         case 'M': {
12301                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
12302                                                                 break;
12303                                                         }
12304                                                         case 'B': {
12305                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
12306                                                                 break;
12307                                                         }
12308                                                 }
12309                                         }
12310                                         break;
12311                                 }
12312                                 case 'dl': {
12313                                         ++$this->listnum;
12314                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12315                                         break;
12316                                 }
12317                                 case 'dt': {
12318                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12319                                         break;
12320                                 }
12321                                 case 'dd': {
12322                                         if ($this->rtl) {
12323                                                 $this->rMargin += $this->listindent;
12324                                         } else {
12325                                                 $this->lMargin += $this->listindent;
12326                                         }
12327                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12328                                         break;
12329                                 }
12330                                 case 'ul':
12331                                 case 'ol': {
12332                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12333                                         $this->htmlvspace = 0;
12334                                         ++$this->listnum;
12335                                         if ($tag['value'] == 'ol') {
12336                                                 $this->listordered[$this->listnum] = true;
12337                                         } else {
12338                                                 $this->listordered[$this->listnum] = false;
12339                                         }
12340                                         if (isset($tag['attribute']['start'])) {
12341                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
12342                                         } else {
12343                                                 $this->listcount[$this->listnum] = 0;
12344                                         }
12345                                         if ($this->rtl) {
12346                                                 $this->rMargin += $this->listindent;
12347                                         } else {
12348                                                 $this->lMargin += $this->listindent;
12349                                         }
12350                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12351                                         $this->htmlvspace = 0;
12352                                         break;
12353                                 }
12354                                 case 'li': {
12355                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12356                                         if ($this->listordered[$this->listnum]) {
12357                                                 // ordered item
12358                                                 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12359                                                         $this->lispacer = $parent['attribute']['type'];
12360                                                 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12361                                                         $this->lispacer = $parent['listtype'];
12362                                                 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12363                                                         $this->lispacer = $this->lisymbol;
12364                                                 } else {
12365                                                         $this->lispacer = '#';
12366                                                 }
12367                                                 ++$this->listcount[$this->listnum];
12368                                                 if (isset($tag['attribute']['value'])) {
12369                                                         $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
12370                                                 }
12371                                         } else {
12372                                                 // unordered item
12373                                                 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12374                                                         $this->lispacer = $parent['attribute']['type'];
12375                                                 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12376                                                         $this->lispacer = $parent['listtype'];
12377                                                 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12378                                                         $this->lispacer = $this->lisymbol;
12379                                                 } else {
12380                                                         $this->lispacer = '!';
12381                                                 }
12382                                         }
12383                                         break;
12384                                 }
12385                                 case 'blockquote': {
12386                                         if ($this->rtl) {
12387                                                 $this->rMargin += $this->listindent;
12388                                         } else {
12389                                                 $this->lMargin += $this->listindent;
12390                                         }
12391                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12392                                         break;
12393                                 }
12394                                 case 'br': {
12395                                         $this->Ln('', $cell);
12396                                         break;
12397                                 }
12398                                 case 'div': {
12399                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12400                                         break;
12401                                 }
12402                                 case 'p': {
12403                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12404                                         break;
12405                                 }
12406                                 case 'pre': {
12407                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12408                                         $this->premode = true;
12409                                         break;
12410                                 }
12411                                 case 'sup': {
12412                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
12413                                         break;
12414                                 }
12415                                 case 'sub': {
12416                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
12417                                         break;
12418                                 }
12419                                 case 'h1': 
12420                                 case 'h2': 
12421                                 case 'h3': 
12422                                 case 'h4': 
12423                                 case 'h5': 
12424                                 case 'h6': {
12425                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
12426                                         break;
12427                                 }
12428                                 case 'tcpdf': {
12429                                         if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
12430                                                  // Special tag used to call TCPDF methods
12431                         if (isset($tag['attribute']['method'])) {
12432                             $tcpdf_method = $tag['attribute']['method'];
12433                             if (method_exists($this, $tcpdf_method)) {
12434                                 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
12435                                     $params = unserialize(urldecode($tag['attribute']['params']));
12436                                     call_user_func_array(array($this, $tcpdf_method), $params);
12437                                 } else {
12438                                     $this->$tcpdf_method();
12439                                 }
12440                                 $this->newline = true;
12441                             }
12442                         }
12443                     }
12444                                 }
12445                                 default: {
12446                                         break;
12447                                 }
12448                         }
12449                 }
12450                 
12451                 /**
12452                  * Process closing tags.
12453                  * @param array $dom html dom array 
12454                  * @param int $key current element id
12455                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12456                  * @access protected
12457                  */
12458                 protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
12459                         $tag = $dom[$key];
12460                         $parent = $dom[($dom[$key]['parent'])];
12461                         $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
12462                         //Closing tag
12463                         switch($tag['value']) {
12464                                 case 'tr': {
12465                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
12466                                         if(!isset($parent['endy'])) {
12467                                                 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
12468                                                 $parent['endy'] = $this->y;
12469                                         }
12470                                         if(!isset($parent['endpage'])) {
12471                                                 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
12472                                                 $parent['endpage'] = $this->page;
12473                                         }
12474                                         // update row-spanned cells
12475                                         if (isset($dom[$table_el]['rowspans'])) {
12476                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12477                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
12478                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12479                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
12480                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
12481                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
12482                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12483                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12484                                                                 }
12485                                                         }
12486                                                 }
12487                                                 // report new endy and endpage to the rowspanned cells
12488                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12489                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12490                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
12491                                                                 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12492                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
12493                                                                 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12494                                                         }
12495                                                 }
12496                                                 // update remaining rowspanned cells
12497                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12498                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12499                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
12500                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
12501                                                         }
12502                                                 }
12503                                         }
12504                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
12505                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];                                        
12506                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
12507                                                 $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
12508                                                 $this->y += $cellspacing;
12509                                         }                               
12510                                         $this->Ln(0, $cell);
12511                                         $this->x = $parent['startx'];
12512                                         // account for booklet mode
12513                                         if ($this->page > $parent['startpage']) {
12514                                                 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
12515                                                         $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
12516                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
12517                                                         $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
12518                                                 }
12519                                         }
12520                                         break;
12521                                 }
12522                                 case 'table': {
12523                                         // draw borders
12524                                         $table_el = $parent;
12525                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 
12526                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
12527                                                         $border = 1;
12528                                         } else {
12529                                                 $border = 0;
12530                                         }
12531                                         // fix bottom line alignment of last line before page break
12532                                         foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
12533                                                 // update row-spanned cells
12534                                                 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12535                                                         foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12536                                                                 if ($trwsp['trid'] == $trkey) {
12537                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
12538                                                                 }
12539                                                                 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
12540                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
12541                                                                 }
12542                                                         }
12543                                                 }
12544                                                 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
12545                                                         $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
12546                                                         $dom[$prevtrkey]['endy'] = $pgendy;
12547                                                         // update row-spanned cells
12548                                                         if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12549                                                                 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12550                                                                         if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
12551                                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
12552                                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
12553                                                                         }
12554                                                                 }
12555                                                         }
12556                                                 }
12557                                                 $prevtrkey = $trkey;
12558                                                 $table_el = $dom[($dom[$key]['parent'])];
12559                                         }
12560                                         // for each row
12561                                         foreach ($table_el['trids'] as $j => $trkey) {
12562                                                 $parent = $dom[$trkey];
12563                                                 // for each cell on the row
12564                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
12565                                                         if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
12566                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
12567                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
12568                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
12569                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
12570                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
12571                                                         } else {
12572                                                                 $endy = $parent['endy'];
12573                                                                 $startpage = $parent['startpage'];
12574                                                                 $endpage = $parent['endpage'];
12575                                                         }
12576                                                         if ($endpage > $startpage) {
12577                                                                 // design borders around HTML cells.
12578                                                                 for ($page=$startpage; $page <= $endpage; ++$page) {
12579                                                                         $this->setPage($page);
12580                                                                         if ($page == $startpage) {
12581                                                                                 $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
12582                                                                                 $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
12583                                                                                 $cborder = $this->getBorderMode($border, $position='start');
12584                                                                         } elseif ($page == $endpage) {
12585                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of last page
12586                                                                                 $ch = $endy - $this->tMargin;
12587                                                                                 $cborder = $this->getBorderMode($border, $position='end');
12588                                                                         } else {
12589                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of the current page
12590                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
12591                                                                                 $cborder = $this->getBorderMode($border, $position='middle');
12592                                                                         }
12593                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12594                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
12595                                                                                 $fill = true;
12596                                                                         } else {
12597                                                                                 $fill = false;
12598                                                                         }
12599                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
12600                                                                         $this->x = $cellpos['startx'];
12601                                                                         // account for margin changes
12602                                                                         if ($page > $startpage) {
12603                                                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
12604                                                                                         $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
12605                                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
12606                                                                                         $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
12607                                                                                 }
12608                                                                         }
12609                                                                         // design a cell around the text
12610                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
12611                                                                         if ($cborder OR $fill) {
12612                                                                                 $pagebuff = $this->getPageBuffer($this->page);
12613                                                                                 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
12614                                                                                 $pend = substr($pagebuff, $this->intmrk[$this->page]);
12615                                                                                 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12616                                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
12617                                                                         }
12618                                                                 }
12619                                                         } else {
12620                                                                 $this->setPage($startpage);
12621                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12622                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
12623                                                                         $fill = true;
12624                                                                 } else {
12625                                                                         $fill = false;
12626                                                                 }
12627                                                                 $this->x = $cellpos['startx'];
12628                                                                 $this->y = $parent['starty'];
12629                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
12630                                                                 $ch = $endy - $parent['starty'];
12631                                                                 // design a cell around the text
12632                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
12633                                                                 if ($border OR $fill) {
12634                                                                         if (end($this->transfmrk[$this->page]) !== false) {
12635                                                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
12636                                                                                 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
12637                                                                         } elseif ($this->InFooter) {
12638                                                                                 $pagemark = &$this->footerpos[$this->page];
12639                                                                         } else {
12640                                                                                 $pagemark = &$this->intmrk[$this->page];
12641                                                                         }
12642                                                                         $pagebuff = $this->getPageBuffer($this->page);
12643                                                                         $pstart = substr($pagebuff, 0, $pagemark);
12644                                                                         $pend = substr($pagebuff, $pagemark);
12645                                                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12646                                                                         $pagemark += strlen($ccode."\n");
12647                                                                 }                                       
12648                                                         }
12649                                                 }                                       
12650                                                 if (isset($table_el['attribute']['cellspacing'])) {
12651                                                         $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
12652                                                         $this->y += $cellspacing;
12653                                                 }                               
12654                                                 $this->Ln(0, $cell);
12655                                                 $this->x = $parent['startx'];
12656                                                 if ($endpage > $startpage) {
12657                                                         if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
12658                                                                 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
12659                                                         } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
12660                                                                 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
12661                                                         }
12662                                                 }
12663                                         }
12664                                         if (isset($parent['cellpadding'])) {
12665                                                 $this->cMargin = $this->oldcMargin;
12666                                         }
12667                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
12668                                         if (!$this->empty_string($this->theadMargin)) {
12669                                                 // restore top margin
12670                                                 $this->tMargin = $this->theadMargin;
12671                                                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
12672                                         }
12673                                         // reset table header
12674                                         $this->thead = '';
12675                                         $this->theadMargin = '';
12676                                         break;
12677                                 }
12678                                 case 'a': {
12679                                         $this->HREF = '';
12680                                         break;
12681                                 }
12682                                 case 'sup': {
12683                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
12684                                         break;
12685                                 }
12686                                 case 'sub': {
12687                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
12688                                         break;
12689                                 }
12690                                 case 'div': {
12691                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12692                                         break;
12693                                 }
12694                                 case 'blockquote': {
12695                                         if ($this->rtl) {
12696                                                 $this->rMargin -= $this->listindent;
12697                                         } else {
12698                                                 $this->lMargin -= $this->listindent;
12699                                         }
12700                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12701                                         break;
12702                                 }
12703                                 case 'p': {
12704                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12705                                         break;
12706                                 }
12707                                 case 'pre': {
12708                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12709                                         $this->premode = false;
12710                                         break;
12711                                 }
12712                                 case 'dl': {
12713                                         --$this->listnum;
12714                                         if ($this->listnum <= 0) {
12715                                                 $this->listnum = 0;
12716                                                 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12717                                         }
12718                                         break;
12719                                 }
12720                                 case 'dt': {
12721                                         $this->lispacer = '';
12722                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12723                                         break;
12724                                 }
12725                                 case 'dd': {
12726                                         $this->lispacer = '';
12727                                         if ($this->rtl) {
12728                                                 $this->rMargin -= $this->listindent;
12729                                         } else {
12730                                                 $this->lMargin -= $this->listindent;
12731                                         }
12732                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12733                                         break;
12734                                 }
12735                                 case 'ul':
12736                                 case 'ol': {
12737                                         --$this->listnum;
12738                                         $this->lispacer = '';
12739                                         if ($this->rtl) {
12740                                                 $this->rMargin -= $this->listindent;
12741                                         } else {
12742                                                 $this->lMargin -= $this->listindent;
12743                                         }
12744                                         if ($this->listnum <= 0) {
12745                                                 $this->listnum = 0;
12746                                                 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12747                                         }
12748                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
12749                                         break;
12750                                 }
12751                                 case 'li': {
12752                                         $this->lispacer = '';
12753                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12754                                         break;
12755                                 }
12756                                 case 'h1': 
12757                                 case 'h2': 
12758                                 case 'h3': 
12759                                 case 'h4': 
12760                                 case 'h5': 
12761                                 case 'h6': {
12762                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
12763                                         break;
12764                                 }
12765                                 default : {
12766                                         break;
12767                                 }
12768                         }
12769                         $this->tmprtl = false;
12770                 }
12771                 
12772                 /**
12773                  * Add vertical spaces if needed.
12774                  * @param int $n number of spaces to add
12775                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12776                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
12777                  * @param boolean $firstorlast if true do not print additional empty lines.
12778                  * @param string $tag HTML tag to which this space will be applied
12779                  * @param boolean $closing true if this space will be applied to a closing tag, false otherwise
12780                  * @access protected
12781                  */
12782                 protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
12783                         if ($firstorlast) {
12784                                 $this->Ln(0, $cell);
12785                                 $this->htmlvspace = 0;
12786                                 return;
12787                         }
12788                         if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
12789                                 $n = $this->tagvspaces[$tag][intval($closing)]['n'];
12790                         }
12791                         if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
12792                                 $h = $this->tagvspaces[$tag][intval($closing)]['h'];
12793                         }
12794                         if (is_string($h)) {
12795                                 $vsize = $n * $this->lasth;
12796                         } else {
12797                                 $vsize = $n * $h;
12798                         }
12799                         if ($vsize > $this->htmlvspace) {
12800                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
12801                                 $this->htmlvspace = $vsize;
12802                         }
12803                 }
12804                 
12805                 /**
12806                  * Set the default bullet to be used as LI bullet symbol
12807                  * @param string $symbol character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek')
12808                  * @access public
12809                  * @since 4.0.028 (2008-09-26)
12810                  */
12811                 public function setLIsymbol($symbol='!') {
12812                         $symbol = strtolower($symbol);
12813                         switch ($symbol) {
12814                                 case '!' :
12815                                 case '#' :
12816                                 case 'disc' :
12817                                 case 'disc' :
12818                                 case 'circle' :
12819                                 case 'square' :
12820                                 case '1':
12821                                 case 'decimal':
12822                                 case 'decimal-leading-zero':
12823                                 case 'i':
12824                                 case 'lower-roman':
12825                                 case 'I':
12826                                 case 'upper-roman':
12827                                 case 'a':
12828                                 case 'lower-alpha':
12829                                 case 'lower-latin':
12830                                 case 'A':
12831                                 case 'upper-alpha':
12832                                 case 'upper-latin':
12833                                 case 'lower-greek': {
12834                                         $this->lisymbol = $symbol;
12835                                         break;
12836                                 }
12837                                 default : {
12838                                         $this->lisymbol = '';
12839                                 }
12840                         }
12841                 }
12842                 
12843                 /**
12844                 * Set the booklet mode for double-sided pages.
12845                 * @param boolean $booklet true set the booklet mode on, fals eotherwise.
12846                 * @param float $inner Inner page margin.
12847                 * @param float $outer Outer page margin.
12848                 * @access public
12849                 * @since 4.2.000 (2008-10-29)
12850                 */
12851                 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
12852                         $this->booklet = $booklet;
12853                         if ($inner >= 0) {
12854                                 $this->lMargin = $inner;
12855                         }
12856                         if ($outer >= 0) {
12857                                 $this->rMargin = $outer;
12858                         }
12859                 }
12860                 
12861                 /**
12862                 * Swap the left and right margins.
12863                 * @param boolean $reverse if true swap left and right margins.
12864                 * @access protected
12865                 * @since 4.2.000 (2008-10-29)
12866                 */
12867                 protected function swapMargins($reverse=true) {
12868                         if ($reverse) {
12869                                 // swap left and right margins
12870                                 $mtemp = $this->original_lMargin;
12871                                 $this->original_lMargin = $this->original_rMargin;
12872                                 $this->original_rMargin = $mtemp;
12873                                 $deltam = $this->original_lMargin - $this->original_rMargin;
12874                                 $this->lMargin += $deltam;
12875                                 $this->rMargin -= $deltam;
12876                         }
12877                 }
12878
12879                 /**
12880                 * Set the vertical spaces for HTML tags.
12881                 * The array must have the following structure (example):
12882                 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
12883                 * The first array level contains the tag names,
12884                 * the second level contains 0 for opening tags or 1 for closing tags,
12885                 * the third level contains the vertical space unit (h) and the number spaces to add (n).
12886                 * If the h parameter is not specified, default values are used.
12887                 * @param array $tagvs array of tags and relative vertical spaces.
12888                 * @access public
12889                 * @since 4.2.001 (2008-10-30)
12890                 */
12891                 public function setHtmlVSpace($tagvs) {
12892                         $this->tagvspaces = $tagvs;
12893                 }
12894
12895         /**
12896                 * Set custom width for list indentation.
12897                 * @param float $width width of the indentation. Use negative value to disable it.
12898                 * @access public
12899                 * @since 4.2.007 (2008-11-12)
12900                 */
12901                 public function setListIndentWidth($width) {
12902                         return $this->customlistindent = floatval($width);
12903         }
12904
12905         /**
12906                 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
12907                 * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page.
12908                 * @access public
12909                 * @since 4.2.010 (2008-11-14)
12910                 */
12911                 public function setOpenCell($isopen) {
12912                         $this->opencell = $isopen;
12913         }
12914
12915         /**
12916                 * Set the color and font style for HTML links.
12917                 * @param array $color RGB array of colors
12918                 * @param string $fontstyle additional font styles to add
12919                 * @access public
12920                 * @since 4.4.003 (2008-12-09)
12921                 */
12922                 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
12923                         $this->htmlLinkColorArray = $color;
12924                         $this->htmlLinkFontStyle = $fontstyle;
12925         }
12926
12927         /**
12928                 * convert html string containing value and unit of measure to user's units or points.
12929                 * @param string $htmlval string containing values and unit
12930                 * @param string $refsize reference value in points
12931                 * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
12932                 * @param boolean $point if true returns points, otherwise returns value in user's units
12933                 * @return float value in user's unit or point if $points=true
12934                 * @access public
12935                 * @since 4.4.004 (2008-12-10)
12936                 */
12937         public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
12938                         $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
12939                         $retval = 0;
12940                         $value = 0;
12941                         $unit = 'px';
12942                         $k = $this->k;
12943                         if ($points) {
12944                                 $k = 1;
12945                         }
12946                         if (in_array($defaultunit, $supportedunits)) {
12947                                 $unit = $defaultunit;
12948                         }
12949                         if (is_numeric($htmlval)) {
12950                                 $value = floatval($htmlval);
12951                         } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
12952                                 $value = floatval($mnum[1]);
12953                                 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
12954                                         if (in_array($munit[1], $supportedunits)) {
12955                                                 $unit = $munit[1];
12956                                         }
12957                                 }
12958                         }
12959                         switch ($unit) {
12960                                 // percentage
12961                                 case '%': {
12962                                         $retval = (($value * $refsize) / 100);
12963                                         break;
12964                                 }
12965                                 // relative-size
12966                                 case 'em': {
12967                                         $retval = ($value * $refsize);
12968                                         break;
12969                                 }
12970                                 case 'ex': {
12971                                         $retval = $value * ($refsize / 2);
12972                                         break;
12973                                 }
12974                                 // absolute-size
12975                                 case 'in': {
12976                                         $retval = ($value * $this->dpi) / $k;
12977                                         break;
12978                                 }
12979                                 case 'cm': {
12980                                         $retval = ($value / 2.54 * $this->dpi) / $k;
12981                                         break;
12982                                 }
12983                                 case 'mm': {
12984                                         $retval = ($value / 25.4 * $this->dpi) / $k;
12985                                         break;
12986                                 }
12987                                 case 'pc': {
12988                                         // one pica is 12 points
12989                                         $retval = ($value * 12) / $k;
12990                                         break;
12991                                 }
12992                                 case 'pt': {
12993                                         $retval = $value / $k;
12994                                         break;
12995                                 }
12996                                 case 'px': {
12997                                         $retval = $this->pixelsToUnits($value);
12998                                         break;
12999                                 }
13000                         }
13001                         return $retval;
13002                 }
13003
13004                 /**
13005                 * Returns the Roman representation of an integer number
13006                 * @param int number to convert
13007                 * @return string roman representation of the specified number
13008                 * @access public
13009                 * @since 4.4.004 (2008-12-10)
13010                 */
13011                 public function intToRoman($number) {
13012                         $roman = '';
13013                         while ($number >= 1000) {
13014                                 $roman .= 'M';
13015                                 $number -= 1000;
13016                         }
13017                         while ($number >= 900) {
13018                                 $roman .= 'CM';
13019                                 $number -= 900;
13020                         }
13021                         while ($number >= 500) {
13022                                 $roman .= 'D';
13023                                 $number -= 500;
13024                         }
13025                         while ($number >= 400) {
13026                                 $roman .= 'CD';
13027                                 $number -= 400;
13028                         }
13029                         while ($number >= 100) {
13030                                 $roman .= 'C';
13031                                 $number -= 100;
13032                         }
13033                         while ($number >= 90) {
13034                         $roman .= 'XC';
13035                         $number -= 90;
13036                         }
13037                         while ($number >= 50) {
13038                                 $roman .= 'L';
13039                                 $number -= 50;
13040                         }
13041                         while ($number >= 40) {
13042                                 $roman .= 'XL';
13043                                 $number -= 40;
13044                         }
13045                         while ($number >= 10) {
13046                         $roman .= 'X';
13047                         $number -= 10;
13048                         }
13049                         while ($number >= 9) {
13050                                 $roman .= 'IX';
13051                                 $number -= 9;
13052                         }
13053                         while ($number >= 5) {
13054                                 $roman .= 'V';
13055                                 $number -= 5;
13056                         }
13057                         while ($number >= 4) {
13058                         $roman .= 'IV';
13059                         $number -= 4;
13060                         }
13061                         while ($number >= 1) {
13062                                 $roman .= 'I';
13063                                 --$number;
13064                         }
13065                         return $roman;
13066                 }
13067
13068                 /**
13069                 * Output an HTML list bullet or ordered item symbol
13070                 * @param int $listdepth list nesting level
13071                 * @param string $listtype type of list
13072                 * @param float $size current font size
13073                 * @access protected
13074                 * @since 4.4.004 (2008-12-10)
13075                 */
13076                 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
13077                     $size /= $this->k;
13078                     $fill = '';
13079                     $color = $this->fgcolor;
13080                     $width = 0;
13081                     $textitem = '';
13082                     $tmpx = $this->x;           
13083                         $lspace = $this->GetStringWidth('  ');
13084                         if ($listtype == '!') {
13085                                 // set default list type for unordered list
13086                                 $deftypes = array('disc', 'circle', 'square');
13087                                 $listtype = $deftypes[($listdepth - 1) % 3];
13088                         } elseif ($listtype == '#') {
13089                                 // set default list type for ordered list
13090                                 $listtype = 'decimal';
13091                         }
13092                 switch ($listtype) {
13093                         // unordered types
13094                                 case 'none': {
13095                                         break;
13096                                 }
13097                                 case 'disc': {
13098                                         $fill = 'F';
13099                                 }
13100                                 case 'circle': {
13101                                         $fill .= 'D';
13102                                         $r = $size / 6;
13103                                         $lspace += (2 * $r);
13104                                         if ($this->rtl) {
13105                                                 $this->x = $this->w - $this->x - $lspace;
13106                                         } else {
13107                                                 $this->x -= $lspace;
13108                                         }
13109                                         $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
13110                                         break;
13111                                 }
13112                                 case 'square': {
13113                                         $l = $size / 3;
13114                                         $lspace += $l;
13115                                         if ($this->rtl) {
13116                                                 $this->x = $this->w - $this->x - $lspace;
13117                                         } else {
13118                                                 $this->x -= $lspace;
13119                                         }
13120                                         $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
13121                                         break;
13122                                 }
13123                                 // ordered types
13124
13125                                 // $this->listcount[$this->listnum];
13126                                 // $textitem
13127                                 case '1':
13128                                 case 'decimal': {
13129                                         $textitem = $this->listcount[$this->listnum];
13130                                         break;
13131                                 }
13132                                 case 'decimal-leading-zero': {
13133                                         $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
13134                                         break;
13135                                 }
13136                                 case 'i':
13137                                 case 'lower-roman': {
13138                                         $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
13139                                         break;
13140                                 }
13141                                 case 'I':
13142                                 case 'upper-roman': {
13143                                         $textitem = $this->intToRoman($this->listcount[$this->listnum]);
13144                                         break;
13145                                 }
13146                                 case 'a':
13147                                 case 'lower-alpha':
13148                                 case 'lower-latin': {
13149                                         $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
13150                                         break;
13151                                 }
13152                                 case 'A':
13153                                 case 'upper-alpha':
13154                                 case 'upper-latin': {
13155                                         $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
13156                                         break;
13157                                 }
13158                                 case 'lower-greek': {
13159                                         $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
13160                                         break;
13161                                 }
13162                                 /*
13163                                 // Types to be implemented (special handling)
13164                                 case 'hebrew': {
13165                                         break;
13166                                 }
13167                                 case 'armenian': {
13168                                         break;
13169                                 }
13170                                 case 'georgian': {
13171                                         break;
13172                                 }
13173                                 case 'cjk-ideographic': {
13174                                         break;
13175                                 }
13176                                 case 'hiragana': {
13177                                         break;
13178                                 }
13179                                 case 'katakana': {
13180                                         break;
13181                                 }
13182                                 case 'hiragana-iroha': {
13183                                         break;
13184                                 }
13185                                 case 'katakana-iroha': {
13186                                         break;
13187                                 }
13188                                 */
13189                                 default: {
13190                                         $textitem = $this->listcount[$this->listnum];
13191                                 }
13192                         }
13193                         if (!$this->empty_string($textitem)) {
13194                                 // print ordered item
13195                                 if ($this->rtl) {
13196                                         $textitem = '.'.$textitem;
13197                                 } else {
13198                                         $textitem = $textitem.'.';
13199                                 }
13200                                 $lspace += $this->GetStringWidth($textitem);
13201                                 if ($this->rtl) {
13202                                         $this->x += $lspace;
13203                                 } else {
13204                                         $this->x -= $lspace;
13205                                 }
13206                                 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
13207                         }
13208                         $this->x = $tmpx;
13209                         $this->lispacer = '';
13210                 }
13211
13212         /**
13213                 * Returns current graphic variables as array.
13214                 * @return array graphic variables
13215                 * @access protected
13216                 * @since 4.2.010 (2008-11-14)
13217                 */
13218                 protected function getGraphicVars() {
13219                         $grapvars = array(
13220                                 'FontFamily' => $this->FontFamily,
13221                                 'FontStyle' => $this->FontStyle,
13222                                 'FontSizePt' => $this->FontSizePt,
13223                                 'rMargin' => $this->rMargin,
13224                                 'lMargin' => $this->lMargin,
13225                                 'cMargin' => $this->cMargin,
13226                                 'LineWidth' => $this->LineWidth,
13227                                 'linestyleWidth' => $this->linestyleWidth,
13228                                 'linestyleCap' => $this->linestyleCap,
13229                                 'linestyleJoin' => $this->linestyleJoin,
13230                                 'linestyleDash' => $this->linestyleDash,
13231                                 'DrawColor' => $this->DrawColor,
13232                                 'FillColor' => $this->FillColor,
13233                                 'TextColor' => $this->TextColor,
13234                                 'ColorFlag' => $this->ColorFlag,
13235                                 'bgcolor' => $this->bgcolor,
13236                                 'fgcolor' => $this->fgcolor,
13237                                 'htmlvspace' => $this->htmlvspace,
13238                                 'lasth' => $this->lasth
13239                                 );
13240                         return $grapvars;
13241                 }
13242
13243         /**
13244                 * Set graphic variables.
13245                 * @param $gvars array graphic variables
13246                 * @access protected
13247                 * @since 4.2.010 (2008-11-14)
13248                 */
13249                 protected function setGraphicVars($gvars) {
13250                         $this->FontFamily = $gvars['FontFamily'];
13251                         $this->FontStyle = $gvars['FontStyle'];
13252                         $this->FontSizePt = $gvars['FontSizePt'];
13253                         $this->rMargin = $gvars['rMargin'];
13254                         $this->lMargin = $gvars['lMargin'];
13255                         $this->cMargin = $gvars['cMargin'];
13256                         $this->LineWidth = $gvars['LineWidth'];
13257                         $this->linestyleWidth = $gvars['linestyleWidth'];
13258                         $this->linestyleCap = $gvars['linestyleCap'];
13259                         $this->linestyleJoin = $gvars['linestyleJoin'];
13260                         $this->linestyleDash = $gvars['linestyleDash'];
13261                         $this->DrawColor = $gvars['DrawColor'];
13262                         $this->FillColor = $gvars['FillColor'];
13263                         $this->TextColor = $gvars['TextColor'];
13264                         $this->ColorFlag = $gvars['ColorFlag'];
13265                         $this->bgcolor = $gvars['bgcolor'];
13266                         $this->fgcolor = $gvars['fgcolor'];
13267                         $this->htmlvspace = $gvars['htmlvspace'];
13268                         //$this->lasth = $gvars['lasth'];
13269                         $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
13270                         if (!$this->empty_string($this->FontFamily)) {
13271                                 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
13272                         }
13273                 }
13274
13275                 /**
13276                 * Returns a temporary filename for caching object on filesystem.
13277                 * @param string $prefix prefix to add to filename
13278                 * return string filename.
13279                 * @access protected
13280                 * @since 4.5.000 (2008-12-31)
13281                 */
13282                 protected function getObjFilename($name) {
13283                         return tempnam(K_PATH_CACHE, $name.'_');
13284                 }
13285
13286         /**
13287                 * Writes data to a temporary file on filesystem.
13288                 * @param string $file file name
13289                 * @param mixed $data data to write on file
13290                 * @param boolean $append if true append data, false replace.
13291                 * @access protected
13292                 * @since 4.5.000 (2008-12-31)
13293                 */
13294                 protected function writeDiskCache($filename, $data, $append=false) {
13295                         if ($append) {
13296                                 $fmode = 'ab+';
13297                         } else {
13298                                 $fmode = 'wb+';
13299                         }
13300                         $f = @fopen($filename, $fmode);
13301                         if (!$f) {
13302                                 $this->Error('Unable to write cache file: '.$filename);
13303                         } else {
13304                                 fwrite($f, $data);
13305                                 fclose($f);
13306                         }
13307                         // update file lenght (needed for transactions)
13308                         if (!isset($this->cache_file_lenght['_'.$filename])) {
13309                                 $this->cache_file_lenght['_'.$filename] = strlen($data);
13310                         } else {
13311                                 $this->cache_file_lenght['_'.$filename] += strlen($data);
13312                         }
13313                 }
13314
13315         /**
13316                 * Read data from a temporary file on filesystem.
13317                 * @param string $file file name
13318                 * @return mixed retrieved data
13319                 * @access protected
13320                 * @since 4.5.000 (2008-12-31)
13321                 */
13322                 protected function readDiskCache($filename) {
13323                         return file_get_contents($filename);
13324                 }
13325
13326                 /**
13327                 * Set buffer content (always append data).
13328                 * @param string $data data
13329                 * @access protected
13330                 * @since 4.5.000 (2009-01-02)
13331                 */
13332                 protected function setBuffer($data) {
13333                         $this->bufferlen += strlen($data);
13334                         if ($this->diskcache) {
13335                                 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
13336                                         $this->buffer = $this->getObjFilename('buffer');
13337                                 }
13338                                 $this->writeDiskCache($this->buffer, $data, true);
13339                         } else {
13340                                 $this->buffer .= $data;
13341                         }
13342                 }
13343
13344         /**
13345                 * Get buffer content.
13346                 * @return string buffer content
13347                 * @access protected
13348                 * @since 4.5.000 (2009-01-02)
13349                 */
13350                 protected function getBuffer() {
13351                         if ($this->diskcache) {
13352                                 return $this->readDiskCache($this->buffer);
13353                         } else {
13354                                 return $this->buffer;
13355                         }
13356                 }
13357
13358         /**
13359                 * Set page buffer content.
13360                 * @param int $page page number
13361                 * @param string $data page data
13362                 * @param boolean $append if true append data, false replace.
13363                 * @access protected
13364                 * @since 4.5.000 (2008-12-31)
13365                 */
13366                 protected function setPageBuffer($page, $data, $append=false) {
13367                         if ($this->diskcache) {
13368                                 if (!isset($this->pages[$page])) {
13369                                         $this->pages[$page] = $this->getObjFilename('page'.$page);
13370                                 }
13371                                 $this->writeDiskCache($this->pages[$page], $data, $append);
13372                         } else {
13373                                 if ($append) {
13374                                         $this->pages[$page] .= $data;
13375                                 } else {
13376                                         $this->pages[$page] = $data;
13377                                 }
13378                         }
13379                         if ($append AND isset($this->pagelen[$page])) {
13380                                 $this->pagelen[$page] += strlen($data);
13381                         } else {
13382                                 $this->pagelen[$page] = strlen($data);
13383                         }
13384                 }
13385
13386         /**
13387                 * Get page buffer content.
13388                 * @param int $page page number
13389                 * @return string page buffer content or false in case of error
13390                 * @access protected
13391                 * @since 4.5.000 (2008-12-31)
13392                 */
13393                 protected function getPageBuffer($page) {
13394                         if ($this->diskcache) {
13395                                 return $this->readDiskCache($this->pages[$page]);
13396                         } elseif (isset($this->pages[$page])) {
13397                                 return $this->pages[$page];
13398                         }
13399                         return false;
13400                 }
13401
13402         /**
13403                 * Set image buffer content.
13404                 * @param string $image image key
13405                 * @param array $data image data
13406                 * @access protected
13407                 * @since 4.5.000 (2008-12-31)
13408                 */
13409                 protected function setImageBuffer($image, $data) {
13410                         if ($this->diskcache) {
13411                                 if (!isset($this->images[$image])) {
13412                                         $this->images[$image] = $this->getObjFilename('image'.$image);
13413                                 }
13414                                 $this->writeDiskCache($this->images[$image], serialize($data));
13415                         } else {
13416                                 $this->images[$image] = $data;
13417                         }
13418                         if (!in_array($image, $this->imagekeys)) {
13419                                 $this->imagekeys[] = $image;
13420                         }
13421                         ++$this->numimages;
13422                 }
13423
13424         /**
13425                 * Set image buffer content.
13426                 * @param string $image image key
13427                 * @param string $key image sub-key
13428                 * @param array $data image data
13429                 * @access protected
13430                 * @since 4.5.000 (2008-12-31)
13431                 */
13432                 protected function setImageSubBuffer($image, $key, $data) {
13433                         if (!isset($this->images[$image])) {
13434                                 $this->setImageBuffer($image, array());
13435                         }
13436                         if ($this->diskcache) {
13437                                 $tmpimg = $this->getImageBuffer($image);
13438                                 $tmpimg[$key] = $data;
13439                                 $this->writeDiskCache($this->images[$image], serialize($tmpimg));
13440                         } else {
13441                                 $this->images[$image][$key] = $data;
13442                         }
13443                 }
13444
13445         /**
13446                 * Get page buffer content.
13447                 * @param string $image image key
13448                 * @return string image buffer content or false in case of error
13449                 * @access protected
13450                 * @since 4.5.000 (2008-12-31)
13451                 */
13452                 protected function getImageBuffer($image) {
13453                         if ($this->diskcache AND isset($this->images[$image])) {
13454                                 return unserialize($this->readDiskCache($this->images[$image]));
13455                         } elseif (isset($this->images[$image])) {
13456                                 return $this->images[$image];
13457                         }
13458                         return false;
13459                 }
13460
13461                 /**
13462                 * Set font buffer content.
13463                 * @param string $font font key
13464                 * @param array $data font data
13465                 * @access protected
13466                 * @since 4.5.000 (2009-01-02)
13467                 */
13468                 protected function setFontBuffer($font, $data) {
13469                         if ($this->diskcache) {
13470                                 if (!isset($this->fonts[$font])) {
13471                                         $this->fonts[$font] = $this->getObjFilename('font');
13472                                 }
13473                                 $this->writeDiskCache($this->fonts[$font], serialize($data));
13474                         } else {
13475                                 $this->fonts[$font] = $data;
13476                         }
13477                         if (!in_array($font, $this->fontkeys)) {
13478                                 $this->fontkeys[] = $font;
13479                         }
13480                 }
13481
13482         /**
13483                 * Set font buffer content.
13484                 * @param string $font font key
13485                 * @param string $key font sub-key
13486                 * @param array $data font data
13487                 * @access protected
13488                 * @since 4.5.000 (2009-01-02)
13489                 */
13490                 protected function setFontSubBuffer($font, $key, $data) {
13491                         if (!isset($this->fonts[$font])) {
13492                                 $this->setFontBuffer($font, array());
13493                         }
13494                         if ($this->diskcache) {
13495                                 $tmpfont = $this->getFontBuffer($font);
13496                                 $tmpfont[$key] = $data;
13497                                 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
13498                         } else {
13499                                 $this->fonts[$font][$key] = $data;
13500                         }
13501                 }
13502
13503         /**
13504                 * Get font buffer content.
13505                 * @param string $font font key
13506                 * @return string font buffer content or false in case of error
13507                 * @access protected
13508                 * @since 4.5.000 (2009-01-02)
13509                 */
13510                 protected function getFontBuffer($font) {
13511                         if ($this->diskcache AND isset($this->fonts[$font])) {
13512                                 return unserialize($this->readDiskCache($this->fonts[$font]));
13513                         } elseif (isset($this->fonts[$font])) {
13514                                 return $this->fonts[$font];
13515                         }
13516                         return false;
13517                 }
13518
13519         /**
13520                 * Move a page to a previous position.
13521                 * @param int $frompage number of the source page
13522                 * @param int $topage number of the destination page (must be less than $frompage)
13523                 * @return true in case of success, false in case of error.
13524                 * @access public
13525                 * @since 4.5.000 (2009-01-02)
13526                 */
13527                 public function movePage($frompage, $topage) {
13528                         if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
13529                                 return false;
13530                         }
13531                         if ($frompage == $this->page) {
13532                                 // close the page before moving it
13533                                 $this->endPage();
13534                         }
13535                         // move all page-related states
13536                         $tmppage = $this->pages[$frompage];
13537                         $tmppagedim = $this->pagedim[$frompage];
13538                         $tmppagelen = $this->pagelen[$frompage];
13539                         $tmpintmrk = $this->intmrk[$frompage];
13540                         if (isset($this->footerpos[$frompage])) {
13541                                 $tmpfooterpos = $this->footerpos[$frompage];
13542                         }
13543                         if (isset($this->footerlen[$frompage])) {
13544                                 $tmpfooterlen = $this->footerlen[$frompage];
13545                         }
13546                         if (isset($this->transfmrk[$frompage])) {
13547                                 $tmptransfmrk = $this->transfmrk[$frompage];
13548                         }
13549                         if (isset($this->PageAnnots[$frompage])) {
13550                                 $tmpannots = $this->PageAnnots[$frompage];
13551                         }
13552                         if (isset($this->newpagegroup[$frompage])) {
13553                                 $tmpnewpagegroup = $this->newpagegroup[$frompage];
13554                         }
13555                         for ($i = $frompage; $i > $topage; --$i) {
13556                                 $j = $i - 1;
13557                                 // shift pages down
13558                                 $this->pages[$i] = $this->pages[$j];
13559                                 $this->pagedim[$i] = $this->pagedim[$j];
13560                                 $this->pagelen[$i] = $this->pagelen[$j];
13561                                 $this->intmrk[$i] = $this->intmrk[$j];
13562                                 if (isset($this->footerpos[$j])) {
13563                                         $this->footerpos[$i] = $this->footerpos[$j];
13564                                 } elseif (isset($this->footerpos[$i])) {
13565                                         unset($this->footerpos[$i]);
13566                                 }
13567                                 if (isset($this->footerlen[$j])) {
13568                                         $this->footerlen[$i] = $this->footerlen[$j];
13569                                 } elseif (isset($this->footerlen[$i])) {
13570                                         unset($this->footerlen[$i]);
13571                                 }
13572                                 if (isset($this->transfmrk[$j])) {
13573                                         $this->transfmrk[$i] = $this->transfmrk[$j];
13574                                 } elseif (isset($this->transfmrk[$i])) {
13575                                         unset($this->transfmrk[$i]);
13576                                 }
13577                                 if (isset($this->PageAnnots[$j])) {
13578                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
13579                                 } elseif (isset($this->PageAnnots[$i])) {
13580                                         unset($this->PageAnnots[$i]);
13581                                 }
13582                                 if (isset($this->newpagegroup[$j])) {
13583                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
13584                                 } elseif (isset($this->newpagegroup[$i])) {
13585                                         unset($this->newpagegroup[$i]);
13586                                 }
13587                         }
13588                         $this->pages[$topage] = $tmppage;
13589                         $this->pagedim[$topage] = $tmppagedim;
13590                         $this->pagelen[$topage] = $tmppagelen;
13591                         $this->intmrk[$topage] = $tmpintmrk;
13592                         if (isset($tmpfooterpos)) {
13593                                 $this->footerpos[$topage] = $tmpfooterpos;
13594                         } elseif (isset($this->footerpos[$topage])) {
13595                                 unset($this->footerpos[$topage]);
13596                         }
13597                         if (isset($tmpfooterlen)) {
13598                                 $this->footerlen[$topage] = $tmpfooterlen;
13599                         } elseif (isset($this->footerlen[$topage])) {
13600                                 unset($this->footerlen[$topage]);
13601                         }
13602                         if (isset($tmptransfmrk)) {
13603                                 $this->transfmrk[$topage] = $tmptransfmrk;
13604                         } elseif (isset($this->transfmrk[$topage])) {
13605                                 unset($this->transfmrk[$topage]);
13606                         }
13607                         if (isset($tmpannots)) {
13608                                 $this->PageAnnots[$topage] = $tmpannots;
13609                         } elseif (isset($this->PageAnnots[$topage])) {
13610                                 unset($this->PageAnnots[$topage]);
13611                         }
13612                         if (isset($tmpnewpagegroup)) {
13613                                 $this->newpagegroup[$topage] = $tmpnewpagegroup;
13614                         } elseif (isset($this->newpagegroup[$topage])) {
13615                                 unset($this->newpagegroup[$topage]);
13616                         }
13617                         // adjust outlines
13618                         $tmpoutlines = $this->outlines;
13619                         foreach ($tmpoutlines as $key => $outline) {
13620                                 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
13621                                         $this->outlines[$key]['p'] = $outline['p'] + 1;
13622                                 } elseif ($outline['p'] == $frompage) {
13623                                         $this->outlines[$key]['p'] = $topage;
13624                                 }
13625                         }
13626                         // adjust links
13627                         $tmplinks = $this->links;
13628                         foreach ($tmplinks as $key => $link) {
13629                                 if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
13630                                         $this->links[$key][0] = $link[0] + 1;
13631                                 } elseif ($link[0] == $frompage) {
13632                                         $this->links[$key][0] = $topage;
13633                                 }
13634                         }
13635                         // adjust javascript
13636                         $tmpjavascript = $this->javascript;
13637                         global $jfrompage, $jtopage;
13638                         $jfrompage = $frompage;
13639                         $jtopage = $topage;
13640                         $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13641                                 create_function('$matches', 'global $jfrompage, $jtopage;
13642                                 $pagenum = intval($matches[3]) + 1;
13643                                 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
13644                                         $newpage = ($pagenum + 1);
13645                                 } elseif ($pagenum == $jfrompage) {
13646                                         $newpage = $jtopage;
13647                                 } else {
13648                                         $newpage = $pagenum;
13649                                 }
13650                                 --$newpage;
13651                                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13652                         // return to last page
13653                         $this->lastPage(true);
13654                         return true;
13655                 }
13656
13657         /**
13658                 * Remove the specified page.
13659                 * @param int $page page to remove
13660                 * @return true in case of success, false in case of error.
13661                 * @access public
13662                 * @since 4.6.004 (2009-04-23)
13663                 */
13664                 public function deletePage($page) {
13665                         if ($page > $this->numpages) {
13666                                 return false;
13667                         }
13668                         // delete current page
13669                         unset($this->pages[$page]);
13670                         unset($this->pagedim[$page]);
13671                         unset($this->pagelen[$page]);
13672                         unset($this->intmrk[$page]);
13673                         if (isset($this->footerpos[$page])) {
13674                                 unset($this->footerpos[$page]);
13675                         }
13676                         if (isset($this->footerlen[$page])) {
13677                                 unset($this->footerlen[$page]);
13678                         }
13679                         if (isset($this->transfmrk[$page])) {
13680                                 unset($this->transfmrk[$page]);
13681                         }
13682                         if (isset($this->PageAnnots[$page])) {
13683                                 unset($this->PageAnnots[$page]);
13684                         }
13685                         if (isset($this->newpagegroup[$page])) {
13686                                 unset($this->newpagegroup[$page]);
13687                         }
13688                         if (isset($this->pageopen[$page])) {
13689                                 unset($this->pageopen[$page]);
13690                         }
13691                         // update remaining pages
13692                         for ($i = $page; $i < $this->numpages; ++$i) {
13693                                 $j = $i + 1;
13694                                 // shift pages
13695                                 $this->pages[$i] = $this->pages[$j];
13696                                 $this->pagedim[$i] = $this->pagedim[$j];
13697                                 $this->pagelen[$i] = $this->pagelen[$j];
13698                                 $this->intmrk[$i] = $this->intmrk[$j];
13699                                 if (isset($this->footerpos[$j])) {
13700                                         $this->footerpos[$i] = $this->footerpos[$j];
13701                                 } elseif (isset($this->footerpos[$i])) {
13702                                         unset($this->footerpos[$i]);
13703                                 }
13704                                 if (isset($this->footerlen[$j])) {
13705                                         $this->footerlen[$i] = $this->footerlen[$j];
13706                                 } elseif (isset($this->footerlen[$i])) {
13707                                         unset($this->footerlen[$i]);
13708                                 }
13709                                 if (isset($this->transfmrk[$j])) {
13710                                         $this->transfmrk[$i] = $this->transfmrk[$j];
13711                                 } elseif (isset($this->transfmrk[$i])) {
13712                                         unset($this->transfmrk[$i]);
13713                                 }
13714                                 if (isset($this->PageAnnots[$j])) {
13715                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
13716                                 } elseif (isset($this->PageAnnots[$i])) {
13717                                         unset($this->PageAnnots[$i]);
13718                                 }
13719                                 if (isset($this->newpagegroup[$j])) {
13720                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
13721                                 } elseif (isset($this->newpagegroup[$i])) {
13722                                         unset($this->newpagegroup[$i]);
13723                                 }
13724                                 if (isset($this->pageopen[$j])) {
13725                                         $this->pageopen[$i] = $this->pageopen[$j];
13726                                 } elseif (isset($this->pageopen[$i])) {
13727                                         unset($this->pageopen[$i]);
13728                                 }
13729                         }
13730                         // remove last page
13731                         unset($this->pages[$this->numpages]);
13732                         unset($this->pagedim[$this->numpages]);
13733                         unset($this->pagelen[$this->numpages]);
13734                         unset($this->intmrk[$this->numpages]);
13735                         if (isset($this->footerpos[$this->numpages])) {
13736                                 unset($this->footerpos[$this->numpages]);
13737                         }
13738                         if (isset($this->footerlen[$this->numpages])) {
13739                                 unset($this->footerlen[$this->numpages]);
13740                         }
13741                         if (isset($this->transfmrk[$this->numpages])) {
13742                                 unset($this->transfmrk[$this->numpages]);
13743                         }
13744                         if (isset($this->PageAnnots[$this->numpages])) {
13745                                 unset($this->PageAnnots[$this->numpages]);
13746                         }
13747                         if (isset($this->newpagegroup[$this->numpages])) {
13748                                 unset($this->newpagegroup[$this->numpages]);
13749                         }
13750                         if (isset($this->pageopen[$this->numpages])) {
13751                                 unset($this->pageopen[$this->numpages]);
13752                         }
13753                         --$this->numpages;
13754                         $this->page = $this->numpages;
13755                         // adjust outlines
13756                         $tmpoutlines = $this->outlines;
13757                         foreach ($tmpoutlines as $key => $outline) {
13758                                 if ($outline['p'] > $page) {
13759                                         $this->outlines[$key]['p'] = $outline['p'] - 1;
13760                                 } elseif ($outline['p'] == $page) {
13761                                         unset($this->outlines[$key]);
13762                                 }
13763                         }
13764                         // adjust links
13765                         $tmplinks = $this->links;
13766                         foreach ($tmplinks as $key => $link) {
13767                                 if ($link[0] > $page) {
13768                                         $this->links[$key][0] = $link[0] - 1;
13769                                 } elseif ($link[0] == $page) {
13770                                         unset($this->links[$key]);
13771                                 }
13772                         }
13773                         // adjust javascript
13774                         $tmpjavascript = $this->javascript;
13775                         global $jpage;
13776                         $jpage = $page;
13777                         $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13778                                 create_function('$matches', 'global $jpage;
13779                                 $pagenum = intval($matches[3]) + 1;
13780                                 if ($pagenum >= $jpage) {
13781                                         $newpage = ($pagenum - 1);
13782                                 } elseif ($pagenum == $jpage) {
13783                                         $newpage = 1;
13784                                 } else {
13785                                         $newpage = $pagenum;
13786                                 }
13787                                 --$newpage;
13788                                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13789                         // return to last page
13790                         $this->lastPage(true);
13791                         return true;
13792                 }
13793
13794                 /**
13795                 * Output a Table of Content Index (TOC).
13796                 * You can override this method to achieve different styles.
13797                 * @param int $page page number where this TOC should be inserted (leave empty for current page).
13798                 * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment).
13799                 * @param string $filler string used to fill the space between text and page number.
13800                 * @access public
13801                 * @author Nicola Asuni
13802                 * @since 4.5.000 (2009-01-02)
13803                 */
13804                 public function addTOC($page='', $numbersfont='', $filler='.') {
13805                         $fontsize = $this->FontSizePt;
13806                         $fontfamily = $this->FontFamily;
13807                         $fontstyle = $this->FontStyle;
13808                         $w = $this->w - $this->lMargin - $this->rMargin;
13809                         $spacer = $this->GetStringWidth(' ') * 4;
13810                         $page_first = $this->getPage();
13811                         $lmargin = $this->lMargin;
13812                         $rmargin = $this->rMargin;
13813                         $x_start = $this->GetX();
13814                         if ($this->empty_string($numbersfont)) {
13815                                 $numbersfont = $this->default_monospaced_font;
13816                         }
13817                         if ($this->empty_string($filler)) {
13818                                 $filler = ' ';
13819                         }
13820                         if ($this->empty_string($page)) {
13821                                 $gap = ' ';
13822                         } else {
13823                                 $gap = '';
13824                         }
13825                         foreach ($this->outlines as $key => $outline) {
13826                                 if ($this->rtl) {
13827                                         $aligntext = 'R';
13828                                         $alignnum = 'L';
13829                                 } else {
13830                                         $aligntext = 'L';
13831                                         $alignnum = 'R';
13832                                 }
13833                                 if ($outline['l'] == 0) {
13834                                         $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
13835                                 } else {
13836                                         $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
13837                                 }
13838                                 $indent = ($spacer * $outline['l']);
13839                                 if ($this->rtl) {
13840                                         $this->rMargin += $indent;
13841                                         $this->x -= $indent;
13842                                 } else {
13843                                         $this->lMargin += $indent;
13844                                         $this->x += $indent;
13845                                 }
13846                                 $link = $this->AddLink();
13847                                 $this->SetLink($link, 0, $outline['p']);
13848                                 // write the text
13849                                 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
13850                                 $this->SetFont($numbersfont, $fontstyle, $fontsize);
13851                                 if ($this->empty_string($page)) {
13852                                         $pagenum = $outline['p'];
13853                                 } else {
13854                                         // placemark to be replaced with the correct number
13855                                         $pagenum = '{#'.($outline['p']).'}';
13856                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13857                                                 $pagenum = '{'.$pagenum.'}';
13858                                     }
13859                                 }
13860                                 $numwidth = $this->GetStringWidth($pagenum);
13861                                 if ($this->rtl) {
13862                                         $tw = $this->x - $this->lMargin;
13863                                 } else {
13864                                         $tw = $this->w - $this->rMargin - $this->x;
13865                                 }
13866                                 $fw = $tw - $numwidth - $this->GetStringWidth(' ');
13867                                 $numfills = floor($fw / $this->GetStringWidth($filler));
13868                                 if ($numfills > 0) {
13869                                         $rowfill = str_repeat($filler, $numfills);
13870                                 } else {
13871                                         $rowfill = '';
13872                                 }
13873                                 if ($this->rtl) {
13874                                         $pagenum = $pagenum.$gap.$rowfill.' ';
13875                                 } else {
13876                                         $pagenum = ' '.$rowfill.$gap.$pagenum;
13877                                 }
13878                                 // write the number
13879                                 //$this->SetX($x_start);
13880                                 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
13881                                 $this->SetX($x_start);
13882                                 $this->lMargin = $lmargin;
13883                                 $this->rMargin = $rmargin;
13884                         }
13885                         $page_last = $this->getPage();
13886                         $numpages = $page_last - $page_first + 1;
13887                         if (!$this->empty_string($page)) {
13888                                 for ($p = $page_first; $p <= $page_last; ++$p) {
13889                                         // get page data
13890                                         $temppage = $this->getPageBuffer($p);
13891                                         for ($n = 1; $n <= $this->numpages; ++$n) {
13892                                                 // update page numbers
13893                                                 $k = '{#'.$n.'}';
13894                                                 $ku = '{'.$k.'}';
13895                                                 $alias_a = $this->_escape($k);
13896                                                 $alias_au = $this->_escape('{'.$k.'}');
13897                                                 if ($this->isunicode) {
13898                                                         $alias_b = $this->_escape($this->UTF8ToLatin1($k));
13899                                                         $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
13900                                                         $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
13901                                                         $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
13902                                                 }
13903                                                 if ($n >= $page) {
13904                                                         $np = $n + $numpages;
13905                                                 } else {
13906                                                         $np = $n;
13907                                                 }
13908                                                 $ns = $this->formatTOCPageNumber($np);
13909                                                 $nu = $ns;
13910                                                 $sdiff = strlen($k) - strlen($ns) - 1;
13911                                                 $sdiffu = strlen($ku) - strlen($ns) - 1;
13912                                                 $sfill = str_repeat($filler, $sdiff);
13913                                                 $sfillu = str_repeat($filler, $sdiffu);
13914                                                 if ($this->rtl) {
13915                                                         $ns = $ns.' '.$sfill;
13916                                                         $nu = $nu.' '.$sfillu;
13917                                                 } else {
13918                                                         $ns = $sfill.' '.$ns;
13919                                                         $nu = $sfillu.' '.$nu;
13920                                                 }
13921                                                 $nu = $this->UTF8ToUTF16BE($nu, false);
13922                                                 $temppage = str_replace($alias_au, $nu, $temppage);
13923                                                 if ($this->isunicode) {
13924                                                         $temppage = str_replace($alias_bu, $nu, $temppage);
13925                                                         $temppage = str_replace($alias_cu, $nu, $temppage);
13926                                                         $temppage = str_replace($alias_b, $ns, $temppage);
13927                                                         $temppage = str_replace($alias_c, $ns, $temppage);
13928                                                 }
13929                                                 $temppage = str_replace($alias_a, $ns, $temppage);
13930                                         }
13931                                         // save changes
13932                                         $this->setPageBuffer($p, $temppage);
13933                                 }
13934                                 // move pages
13935                                 for ($i = 0; $i < $numpages; ++$i) {
13936                                         $this->movePage($page_last, $page);
13937                                 }
13938                         }
13939                         $this->SetFont($fontfamily, $fontstyle, $fontsize);
13940                 }
13941
13942                 /**
13943                 * Stores a copy of the current TCPDF object used for undo operation.
13944                 * @access public
13945                 * @since 4.5.029 (2009-03-19)
13946                 */
13947                 public function startTransaction() {
13948                         if (isset($this->objcopy)) {
13949                                 // remove previous copy
13950                                 $this->commitTransaction();
13951                         }
13952                         // clone current object
13953                         $this->objcopy = $this->objclone($this);
13954                 }
13955
13956                 /**
13957                 * Delete the copy of the current TCPDF object used for undo operation.
13958                 * @access public
13959                 * @since 4.5.029 (2009-03-19)
13960                 */
13961                 public function commitTransaction() {
13962                         if (isset($this->objcopy)) {
13963                                 $this->objcopy->_destroy(true, true);
13964                                 unset($this->objcopy);
13965                         }
13966                 }
13967
13968                 /**
13969                 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
13970                 * @return TCPDF object.
13971                 * @access public
13972                 * @since 4.5.029 (2009-03-19)
13973                 */
13974                 public function rollbackTransaction() {
13975                         if (isset($this->objcopy)) {
13976                                 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
13977                                         // truncate files to previous values
13978                                         foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
13979                                                 $file = substr($file, 1);
13980                                                 $handle = fopen($file, 'r+');
13981                                                 ftruncate($handle, $lenght);
13982                                         }
13983                                 }
13984                                 $this->_destroy(true, true);
13985                                 return $this->objcopy;
13986                         }
13987                         return $this;
13988                 }
13989
13990                 /**
13991                 * Creates a copy of a class object
13992                 * @param object $object class object to be cloned
13993                 * @return cloned object
13994                 * @access public
13995                 * @since 4.5.029 (2009-03-19)
13996                 */
13997                 public function objclone($object) {
13998                         return @clone($object);
13999                 }
14000
14001                 /**
14002                 * Determine whether a string is empty.
14003                 * @param srting $str string to be checked
14004                 * @return boolean true if string is empty
14005                 * @access public
14006                 * @since 4.5.044 (2009-04-16)
14007                 */
14008                 public function empty_string($str) {
14009                         return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
14010                 }
14011                 
14012         } // END OF TCPDF CLASS
14013 }
14014 //============================================================+
14015 // END OF FILE
14016 //============================================================+
14017 ?>