]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/tcpdf/tcpdf.php
Release 6.2.0beta4
[Github/sugarcrm.git] / include / tcpdf / tcpdf.php
1 <?php
2
3 /*
4
5 Modification information for LGPL compliance
6
7 r57813 - 2010-08-19 10:34:44 -0700 (Thu, 19 Aug 2010) - kjing - Author: John Mertic <jmertic@sugarcrm.com>
8     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.
9
10 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
11
12 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
13
14 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
15
16 r53409 - 2010-01-03 19:31:15 -0800 (Sun, 03 Jan 2010) - roger - merge -r50376:HEAD from fuji_newtag_tmp
17
18 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
19
20 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
21
22 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:
23 - Changing all ereg function to either preg or simple string based ones
24 - No more references to magic quotes.
25 - Change all the session_unregister() functions to just unset() the correct session variable instead.
26
27 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
28
29 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.
30
31 r47900 - 2009-06-02 11:26:55 -0700 (Tue, 02 Jun 2009) - jenny - Updating with changes from bsoufflet.
32
33 r46662 - 2009-04-29 10:48:07 -0700 (Wed, 29 Apr 2009) - jenny - Invoking native jpg image support which isnt' automatically there.
34
35 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.
36
37 r46451 - 2009-04-23 16:57:40 -0700 (Thu, 23 Apr 2009) - jenny - tcpdf initial checkin.
38
39
40 */
41
42
43 //============================================================+
44 // File name   : tcpdf.php
45 // Begin       : 2002-08-03
46 // Last Update : 2009-05-28
47 // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
48 // Version     : 4.6.013
49 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
50 //      ----------------------------------------------------------------------------
51 //  Copyright (C) 2002-2009  Nicola Asuni - Tecnick.com S.r.l.
52 //      
53 //      This program is free software: you can redistribute it and/or modify
54 //      it under the terms of the GNU Lesser General Public License as published by
55 //      the Free Software Foundation, either version 2.1 of the License, or
56 //      (at your option) any later version.
57 //      
58 //      This program is distributed in the hope that it will be useful,
59 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
60 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
61 //      GNU Lesser General Public License for more details.
62 //      
63 //      You should have received a copy of the GNU Lesser General Public License
64 //      along with this program.  If not, see <http://www.gnu.org/licenses/>.
65 //      
66 //      See LICENSE.TXT file for more information.
67 //  ----------------------------------------------------------------------------
68 //
69 // Description : This is a PHP class for generating PDF documents without 
70 //               requiring external extensions.
71 //
72 // NOTE:
73 // This class was originally derived in 2002 from the Public 
74 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 
75 // but now is almost entirely rewritten.
76 //
77 // Main features:
78 //  * no external libraries are required for the basic functions;
79 //      * supports all ISO page formats;
80 //      * supports custom page formats, margins and units of measure;
81 //      * supports UTF-8 Unicode and Right-To-Left languages;
82 //      * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
83 //      * supports document encryption;
84 //      * includes methods to publish some XHTML code;
85 //      * includes graphic (geometric) and transformation methods;
86 //      * includes Javascript and forms support;
87 //      * 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;
88 //      * includes methods to set Bookmarks and print a Table of Content;
89 //      * includes methods to move and delete pages;
90 //      * includes methods for automatic page header and footer management;
91 //      * supports automatic page break;
92 //      * supports automatic page numbering and page groups;
93 //      * supports automatic line break and text justification;
94 //      * 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)
95 //      * supports stroke and clipping mode for text;
96 //      * supports clipping masks;
97 //      * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
98 //      * supports several annotations, including links, text and file attachments;
99 //      * supports page compression (requires zlib extension);
100 //  * supports text hyphenation.
101 //  * supports transactions to UNDO commands.
102 //
103 // -----------------------------------------------------------
104 // THANKS TO:
105 // 
106 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
107 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
108 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
109 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
110 // dullus for text Justification.
111 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
112 // Patrick Benny for text stretch suggestion on Cell().
113 // Johannes Güntert for JavaScript support.
114 // Denis Van Nuffelen for Dynamic Form.
115 // Jacek Czekaj for multibyte justification
116 // Anthony Ferrara for the reintroduction of legacy image methods.
117 // Sourceforge user 1707880 (hucste) for line-trough mode.
118 // Larry Stanbery for page groups.
119 // Martin Hall-May for transparency.
120 // Aaron C. Spike for Polycurve method.
121 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
122 // Moritz Wagner and Andreas Wurmser for graphic functions.
123 // Andrew Whitehead for core fonts support.
124 // Esteban Joël Marín for OpenType font conversion.
125 // Teus Hagen for several suggestions and fixes.
126 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
127 // Kosmas Papachristos for some CSS improvements.
128 // Marcel Partap for some fixes.
129 // Won Kyu Park for several suggestions, fixes and patches.
130 // Anyone that has reported a bug or sent a suggestion.
131 //============================================================+
132
133 /**
134  * This is a PHP class for generating PDF documents without requiring external extensions.<br>
135  * 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>
136  * <h3>TCPDF main features are:</h3>
137  * <ul>
138 * <li>no external libraries are required for the basic functions;</li>
139 * <li>supports all ISO page formats;</li>
140 * <li>supports custom page formats, margins and units of measure;</li>
141 * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
142 * <li>supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>
143 * <li>supports document encryption;</li>
144 * <li>includes methods to publish some XHTML code;</li>
145 * <li>includes graphic (geometric) and transformation methods;</li>
146 * <li>includes Javascript and forms support;</li>
147 * <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>
148 * <li>includes methods to set Bookmarks and print a Table of Content;</li>
149 * <li>includes methods to move and delete pages;</li>
150 * <li>includes methods for automatic page header and footer management;</li>
151 * <li>supports automatic page break;</li>
152 * <li>supports automatic page numbering and page groups;</li>
153 * <li>supports automatic line break and text justification;</li>
154 * <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>
155 * <li>supports stroke and clipping mode for text;</li>
156 * <li>supports clipping masks;</li>
157 * <li>supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
158 * <li>supports several annotations, including links, text and file attachments;</li>
159 * <li>supports page compression (requires zlib extension);</li>
160 * <li>supports text hyphenation.</li>
161 * <li>supports transactions to UNDO commands.</li>
162  * </ul>
163  * Tools to encode your unicode fonts are on fonts/utils directory.</p>
164  * @package com.tecnick.tcpdf
165  * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
166  * @author Nicola Asuni
167  * @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
168  * @link http://www.tcpdf.org
169  * @license http://www.gnu.org/copyleft/lesser.html LGPL
170  * @version 4.6.013
171  */
172
173 /**
174  * main configuration file
175  */
176 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
177
178 // includes some support files
179
180 /**
181  * unicode data
182  */
183 require_once(dirname(__FILE__).'/unicode_data.php');
184
185 /**
186  * html colors table
187  */
188 require_once(dirname(__FILE__).'/htmlcolors.php');
189
190 if (!class_exists('TCPDF', false)) {
191         /**
192          * define default PDF document producer
193          */ 
194         define('PDF_PRODUCER', 'TCPDF 4.6.013 (http://www.tcpdf.org)');
195         
196         /**
197         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
198         * 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>
199         * @name TCPDF
200         * @package com.tecnick.tcpdf
201         * @version 4.6.013
202         * @author Nicola Asuni - info@tecnick.com
203         * @link http://www.tcpdf.org
204         * @license http://www.gnu.org/copyleft/lesser.html LGPL
205         */
206         class TCPDF {
207                 
208                 // protected or Protected properties
209
210                 /**
211                 * @var current page number
212                 * @access protected
213                 */
214                 protected $page;
215                 
216                 /**
217                 * @var current object number
218                 * @access protected
219                 */
220                 protected $n;
221
222                 /**
223                 * @var array of object offsets
224                 * @access protected
225                 */
226                 protected $offsets;
227
228                 /**
229                 * @var buffer holding in-memory PDF
230                 * @access protected
231                 */
232                 protected $buffer;
233
234                 /**
235                 * @var array containing pages
236                 * @access protected
237                 */
238                 protected $pages = array();
239
240                 /**
241                 * @var current document state
242                 * @access protected
243                 */
244                 protected $state;
245
246                 /**
247                 * @var compression flag
248                 * @access protected
249                 */
250                 protected $compress;
251                 
252                 /**
253                 * @var current page orientation (P = Portrait, L = Landscape)
254                 * @access protected
255                 */
256                 protected $CurOrientation;
257
258                 /**
259                 * @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>
260                 * @access protected
261                 */
262                 protected $pagedim = array();
263
264                 /**
265                 * @var scale factor (number of points in user unit)
266                 * @access protected
267                 */
268                 protected $k;
269
270                 /**
271                 * @var width of page format in points
272                 * @access protected
273                 */
274                 protected $fwPt;
275
276                 /**
277                 * @var height of page format in points
278                 * @access protected
279                 */
280                 protected $fhPt;
281
282                 /**
283                 * @var current width of page in points
284                 * @access protected
285                 */
286                 protected $wPt;
287
288                 /**
289                 * @var current height of page in points
290                 * @access protected
291                 */
292                 protected $hPt;
293
294                 /**
295                 * @var current width of page in user unit
296                 * @access protected
297                 */
298                 protected $w;
299
300                 /**
301                 * @var current height of page in user unit
302                 * @access protected
303                 */
304                 protected $h;
305
306                 /**
307                 * @var left margin
308                 * @access protected
309                 */
310                 protected $lMargin;
311
312                 /**
313                 * @var top margin
314                 * @access protected
315                 */
316                 protected $tMargin;
317
318                 /**
319                 * @var right margin
320                 * @access protected
321                 */
322                 protected $rMargin;
323
324                 /**
325                 * @var page break margin
326                 * @access protected
327                 */
328                 protected $bMargin;
329
330                 /**
331                 * @var cell internal padding
332                 * @access protected
333                 */
334                 //protected
335                 public $cMargin;
336                 
337                 /**
338                 * @var cell internal padding (previous value)
339                 * @access protected
340                 */
341                 protected $oldcMargin;
342
343                 /**
344                 * @var current horizontal position in user unit for cell positioning
345                 * @access protected
346                 */
347                 protected $x;
348
349                 /**
350                 * @var current vertical position in user unit for cell positioning
351                 * @access protected
352                 */
353                 protected $y;
354
355                 /**
356                 * @var height of last cell printed
357                 * @access protected
358                 */
359                 protected $lasth;
360
361                 /**
362                 * @var line width in user unit
363                 * @access protected
364                 */
365                 protected $LineWidth;
366
367                 /**
368                 * @var array of standard font names
369                 * @access protected
370                 */
371                 protected $CoreFonts;
372
373                 /**
374                 * @var array of used fonts
375                 * @access protected
376                 */
377                 protected $fonts = array();
378
379                 /**
380                 * @var array of font files
381                 * @access protected
382                 */
383                 protected $FontFiles = array();
384
385                 /**
386                 * @var array of encoding differences
387                 * @access protected
388                 */
389                 protected $diffs = array();
390
391                 /**
392                 * @var array of used images
393                 * @access protected
394                 */
395                 protected $images = array();
396
397                 /**
398                 * @var array of Annotations in pages
399                 * @access protected
400                 */
401                 protected $PageAnnots = array();
402
403                 /**
404                 * @var array of internal links
405                 * @access protected
406                 */
407                 protected $links = array();
408
409                 /**
410                 * @var current font family
411                 * @access protected
412                 */
413                 protected $FontFamily;
414
415                 /**
416                 * @var current font style
417                 * @access protected
418                 */
419                 protected $FontStyle;
420                 
421                 /**
422                 * @var current font ascent (distance between font top and baseline)
423                 * @access protected
424                 * @since 2.8.000 (2007-03-29)
425                 */
426                 protected $FontAscent;
427                 
428                 /**
429                 * @var current font descent (distance between font bottom and baseline)
430                 * @access protected
431                 * @since 2.8.000 (2007-03-29)
432                 */
433                 protected $FontDescent;
434
435                 /**
436                 * @var underlining flag
437                 * @access protected
438                 */
439                 protected $underline;
440
441                 /**
442                 * @var current font info
443                 * @access protected
444                 */
445                 protected $CurrentFont;
446
447                 /**
448                 * @var current font size in points
449                 * @access protected
450                 */
451                 protected $FontSizePt;
452
453                 /**
454                 * @var current font size in user unit
455                 * @access protected
456                 */
457                 protected $FontSize;
458
459                 /**
460                 * @var commands for drawing color
461                 * @access protected
462                 */
463                 protected $DrawColor;
464
465                 /**
466                 * @var commands for filling color
467                 * @access protected
468                 */
469                 protected $FillColor;
470
471                 /**
472                 * @var commands for text color
473                 * @access protected
474                 */
475                 protected $TextColor;
476
477                 /**
478                 * @var indicates whether fill and text colors are different
479                 * @access protected
480                 */
481                 protected $ColorFlag;
482
483                 /**
484                 * @var automatic page breaking
485                 * @access protected
486                 */
487                 protected $AutoPageBreak;
488
489                 /**
490                 * @var threshold used to trigger page breaks
491                 * @access protected
492                 */
493                 protected $PageBreakTrigger;
494
495                 /**
496                 * @var flag set when processing footer
497                 * @access protected
498                 */
499                 protected $InFooter = false;
500
501                 /**
502                 * @var zoom display mode
503                 * @access protected
504                 */
505                 protected $ZoomMode;
506
507                 /**
508                 * @var layout display mode
509                 * @access protected
510                 */
511                 protected $LayoutMode;
512
513                 /**
514                 * @var title
515                 * @access protected
516                 */
517                 protected $title = '';
518
519                 /**
520                 * @var subject
521                 * @access protected
522                 */
523                 protected $subject = '';
524
525                 /**
526                 * @var author
527                 * @access protected
528                 */
529                 protected $author = '';
530
531                 /**
532                 * @var keywords
533                 * @access protected
534                 */
535                 protected $keywords = '';
536
537                 /**
538                 * @var creator
539                 * @access protected
540                 */
541                 protected $creator = '';
542
543                 /**
544                 * @var alias for total number of pages
545                 * @access protected
546                 */
547                 protected $AliasNbPages = '{nb}';
548                 
549                 /**
550                 * @var alias for page number
551                 * @access protected
552                 */
553                 protected $AliasNumPage = '{pnb}';
554                 
555                 /**
556                 * @var right-bottom corner X coordinate of inserted image
557                 * @since 2002-07-31
558                 * @author Nicola Asuni
559                 * @access protected
560                 */
561                 protected $img_rb_x;
562
563                 /**
564                 * @var right-bottom corner Y coordinate of inserted image
565                 * @since 2002-07-31
566                 * @author Nicola Asuni
567                 * @access protected
568                 */
569                 protected $img_rb_y;
570
571                 /**
572                 * @var adjusting factor to convert pixels to user units.
573                 * @since 2004-06-14
574                 * @author Nicola Asuni
575                 * @access protected
576                 */
577                 protected $imgscale = 1;
578
579                 /**
580                 * @var boolean set to true when the input text is unicode (require unicode fonts)
581                 * @since 2005-01-02
582                 * @author Nicola Asuni
583                 * @access protected
584                 */
585                 protected $isunicode = false;
586
587                 /**
588                 * @var PDF version
589                 * @since 1.5.3
590                 * @access protected
591                 */
592                 protected $PDFVersion = '1.7';
593                 
594                 
595                 // ----------------------
596                 
597                 /**
598                  * @var Minimum distance between header and top page margin.
599                  * @access protected
600                  */
601                 protected $header_margin;
602                 
603                 /**
604                  * @var Minimum distance between footer and bottom page margin.
605                  * @access protected
606                  */
607                 protected $footer_margin;
608                 
609                 /**
610                  * @var original left margin value
611                  * @access protected
612                  * @since 1.53.0.TC013
613                  */
614                 protected $original_lMargin;
615                 
616                 /**
617                  * @var original right margin value
618                  * @access protected
619                  * @since 1.53.0.TC013
620                  */
621                 protected $original_rMargin;
622                         
623                 /**
624                  * @var Header font.
625                  * @access protected
626                  */
627                 protected $header_font;
628                 
629                 /**
630                  * @var Footer font.
631                  * @access protected
632                  */
633                 protected $footer_font;
634                 
635                 /**
636                  * @var Language templates.
637                  * @access protected
638                  */
639                 protected $l;
640                 
641                 /**
642                  * @var Barcode to print on page footer (only if set).
643                  * @access protected
644                  */
645                 protected $barcode = false;
646                 
647                 /**
648                  * @var If true prints header
649                  * @access protected
650                  */
651                 protected $print_header = true;
652                 
653                 /**
654                  * @var If true prints footer.
655                  * @access protected
656                  */
657                 protected $print_footer = true;
658                         
659                 /**
660                  * @var Header image logo.
661                  * @access protected
662                  */
663                 protected $header_logo = '';
664                 
665                 /**
666                  * @var Header image logo width in mm.
667                  * @access protected
668                  */
669                 protected $header_logo_width = 30;
670                 
671                 /**
672                  * @var String to print as title on document header.
673                  * @access protected
674                  */
675                 protected $header_title = '';
676                 
677                 /**
678                  * @var String to print on document header.
679                  * @access protected
680                  */
681                 protected $header_string = '';
682                 
683                 /**
684                  * @var Default number of columns for html table.
685                  * @access protected
686                  */
687                 protected $default_table_columns = 4;
688                 
689                 
690                 // variables for html parser
691                 
692                 /**
693                  * @var HTML PARSER: array to store current link and rendering styles.
694                  * @access protected
695                  */
696                 protected $HREF = array();
697                 
698                 /**
699                  * @var store a list of available fonts on filesystem.
700                  * @access protected
701                  */
702                 protected $fontlist = array();
703                 
704                 /**
705                  * @var current foreground color
706                  * @access protected
707                  */
708                 protected $fgcolor;
709                                                 
710                 /**
711                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
712                  * @access protected
713                  */
714                 protected $listordered = array();
715                 
716                 /**
717                  * @var HTML PARSER: array count list items on nested lists.
718                  * @access protected
719                  */
720                 protected $listcount = array();
721                 
722                 /**
723                  * @var HTML PARSER: current list nesting level.
724                  * @access protected
725                  */
726                 protected $listnum = 0;
727                 
728                 /**
729                  * @var HTML PARSER: indent amount for lists.
730                  * @access protected
731                  */
732                 protected $listindent;
733                 
734                 /**
735                  * @var current background color
736                  * @access protected
737                  */
738                 protected $bgcolor;
739                 
740                 /**
741                  * @var Store temporary font size in points.
742                  * @access protected
743                  */
744                 protected $tempfontsize = 10;
745                 
746                 /**
747                  * @var spacer for LI tags.
748                  * @access protected
749                  */
750                 protected $lispacer = '';
751                 
752                 /**
753                  * @var default encoding
754                  * @access protected
755                  * @since 1.53.0.TC010
756                  */
757                 protected $encoding = 'UTF-8';
758                 
759                 /**
760                  * @var PHP internal encoding
761                  * @access protected
762                  * @since 1.53.0.TC016
763                  */
764                 protected $internal_encoding;
765                 
766                 /**
767                  * @var indicates if the document language is Right-To-Left
768                  * @access protected
769                  * @since 2.0.000
770                  */
771                 protected $rtl = false;
772                 
773                 /**
774                  * @var used to force RTL or LTR string inversion
775                  * @access protected
776                  * @since 2.0.000
777                  */
778                 protected $tmprtl = false;
779                 
780                 // --- Variables used for document encryption:
781                 
782                 /**
783                  * Indicates whether document is protected
784                  * @access protected
785                  * @since 2.0.000 (2008-01-02)
786                  */
787                 protected $encrypted;
788                 
789                 /**
790                  * U entry in pdf document
791                  * @access protected
792                  * @since 2.0.000 (2008-01-02)
793                  */
794                 protected $Uvalue;
795                 
796                 /**
797                  * O entry in pdf document
798                  * @access protected
799                  * @since 2.0.000 (2008-01-02)
800                  */
801                 protected $Ovalue;
802                 
803                 /**
804                  * P entry in pdf document
805                  * @access protected
806                  * @since 2.0.000 (2008-01-02)
807                  */
808                 protected $Pvalue;
809                 
810                 /**
811                  * encryption object id
812                  * @access protected
813                  * @since 2.0.000 (2008-01-02)
814                  */
815                 protected $enc_obj_id;
816                 
817                 /**
818                  * last RC4 key encrypted (cached for optimisation)
819                  * @access protected
820                  * @since 2.0.000 (2008-01-02)
821                  */
822                 protected $last_rc4_key;
823                 
824                 /**
825                  * last RC4 computed key
826                  * @access protected
827                  * @since 2.0.000 (2008-01-02)
828                  */
829                 protected $last_rc4_key_c;
830                 
831                 /**
832                  * RC4 padding
833                  * @access protected
834                  */
835                 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";
836                 
837                 /**
838                  * RC4 encryption key
839                  * @access protected
840                  */
841                 protected $encryption_key;
842                 
843                 // --- bookmark ---
844                 
845                 /**
846                  * Outlines for bookmark
847                  * @access protected
848                  * @since 2.1.002 (2008-02-12)
849                  */
850                 protected $outlines = array();
851                 
852                 /**
853                  * Outline root for bookmark
854                  * @access protected
855                  * @since 2.1.002 (2008-02-12)
856                  */
857                 protected $OutlineRoot;
858                 
859                 
860                 // --- javascript and form ---
861                 
862                 /**
863                  * javascript code
864                  * @access protected
865                  * @since 2.1.002 (2008-02-12)
866                  */
867                 protected $javascript = '';
868                 
869                 /**
870                  * javascript counter
871                  * @access protected
872                  * @since 2.1.002 (2008-02-12)
873                  */
874                 protected $n_js;
875
876                 /**
877                  * line trough state
878                  * @access protected
879                  * @since 2.8.000 (2008-03-19)
880                  */
881                 protected $linethrough;
882
883                 // --- Variables used for User's Rights ---
884                 // See PDF reference chapter 8.7 Digital Signatures
885
886                 /**
887                  * If true enables user's rights on PDF reader
888                  * @access protected
889                  * @since 2.9.000 (2008-03-26)
890                  */
891                 protected $ur;
892
893                 /**
894                  * Names specifying additional document-wide usage rights for the document.
895                  * @access protected
896                  * @since 2.9.000 (2008-03-26)
897                  */
898                 protected $ur_document;
899
900                 /**
901                  * Names specifying additional annotation-related usage rights for the document.
902                  * @access protected
903                  * @since 2.9.000 (2008-03-26)
904                  */
905                 protected $ur_annots;
906
907                 /**
908                  * Names specifying additional form-field-related usage rights for the document.
909                  * @access protected
910                  * @since 2.9.000 (2008-03-26)
911                  */
912                 protected $ur_form;
913
914                 /**
915                  * Names specifying additional signature-related usage rights for the document.
916                  * @access protected
917                  * @since 2.9.000 (2008-03-26)
918                  */
919                 protected $ur_signature;
920
921                 /**
922                  * Dot Per Inch Document Resolution (do not change)
923                  * @access protected
924                  * @since 3.0.000 (2008-03-27)
925                  */
926                 protected $dpi = 72;
927                 
928                 /**
929                  * Array of page numbers were a new page group was started
930                  * @access protected
931                  * @since 3.0.000 (2008-03-27)
932                  */
933                 protected $newpagegroup = array();
934                 
935                 /**
936                  * Contains the number of pages of the groups
937                  * @access protected
938                  * @since 3.0.000 (2008-03-27)
939                  */
940                 protected $pagegroups;
941                 
942                 /**
943                  * Contains the alias of the current page group
944                  * @access protected
945                  * @since 3.0.000 (2008-03-27)
946                  */
947                 protected $currpagegroup; 
948                 
949                 /**
950                  * Restrict the rendering of some elements to screen or printout.
951                  * @access protected
952                  * @since 3.0.000 (2008-03-27)
953                  */
954                 protected $visibility = 'all';
955                 
956                 /**
957                  * Print visibility.
958                  * @access protected
959                  * @since 3.0.000 (2008-03-27)
960                  */
961                 protected $n_ocg_print;
962                 
963                 /**
964                  * View visibility.
965                  * @access protected
966                  * @since 3.0.000 (2008-03-27)
967                  */
968                 protected $n_ocg_view;
969                 
970                 /**
971                  * Array of transparency objects and parameters.
972                  * @access protected
973                  * @since 3.0.000 (2008-03-27)
974                  */
975                 protected $extgstates;
976                 
977                 /**
978                  * Set the default JPEG compression quality (1-100)
979                  * @access protected
980                  * @since 3.0.000 (2008-03-27)
981                  */
982                 protected $jpeg_quality;
983                 
984                 /**
985                  * Default cell height ratio.
986                  * @access protected
987                  * @since 3.0.014 (2008-05-23)
988                  */
989                 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
990                 
991                 /**
992                  * PDF viewer preferences.
993                  * @access protected
994                  * @since 3.1.000 (2008-06-09)
995                  */
996                 protected $viewer_preferences;
997                 
998                 /**
999                  * A name object specifying how the document should be displayed when opened.
1000                  * @access protected
1001                  * @since 3.1.000 (2008-06-09)
1002                  */
1003                 protected $PageMode;
1004                 
1005                 /**
1006                  * Array for storing gradient information.
1007                  * @access protected
1008                  * @since 3.1.000 (2008-06-09)
1009                  */
1010                 protected $gradients = array();
1011                 
1012                 /**
1013                  * Array used to store positions inside the pages buffer.
1014                  * keys are the page numbers
1015                  * @access protected
1016                  * @since 3.2.000 (2008-06-26)
1017                  */
1018                 protected $intmrk = array();
1019                 
1020                 /**
1021                  * Array used to store footer positions of each page.
1022                  * @access protected
1023                  * @since 3.2.000 (2008-07-01)
1024                  */
1025                 protected $footerpos = array();
1026                 
1027                 
1028                 /**
1029                  * Array used to store footer lenght of each page.
1030                  * @access protected
1031                  * @since 4.0.014 (2008-07-29)
1032                  */
1033                 protected $footerlen = array();
1034                 
1035                 /**
1036                  * True if a newline is created.
1037                  * @access protected
1038                  * @since 3.2.000 (2008-07-01)
1039                  */
1040                 protected $newline = true;
1041                 
1042                 /**
1043                  * End position of the latest inserted line
1044                  * @access protected
1045                  * @since 3.2.000 (2008-07-01)
1046                  */
1047                 protected $endlinex = 0;
1048                 
1049                 /**
1050                  * PDF string for last line width
1051                  * @access protected
1052                  * @since 4.0.006 (2008-07-16)
1053                  */
1054                 protected $linestyleWidth = '';
1055                 
1056                 /**
1057                  * PDF string for last line width
1058                  * @access protected
1059                  * @since 4.0.006 (2008-07-16)
1060                  */
1061                 protected $linestyleCap = '0 J';
1062                 
1063                 /**
1064                  * PDF string for last line width
1065                  * @access protected
1066                  * @since 4.0.006 (2008-07-16)
1067                  */
1068                 protected $linestyleJoin = '0 j';
1069                 
1070                 /**
1071                  * PDF string for last line width
1072                  * @access protected
1073                  * @since 4.0.006 (2008-07-16)
1074                  */
1075                 protected $linestyleDash = '[] 0 d';
1076                 
1077                 /**
1078                  * True if marked-content sequence is open
1079                  * @access protected
1080                  * @since 4.0.013 (2008-07-28)
1081                  */
1082                 protected $openMarkedContent = false;
1083                 
1084                 /**
1085                  * Count the latest inserted vertical spaces on HTML
1086                  * @access protected
1087                  * @since 4.0.021 (2008-08-24)
1088                  */
1089                 protected $htmlvspace = 0;
1090                 
1091                 /**
1092                  * Array of Spot colors
1093                  * @access protected
1094                  * @since 4.0.024 (2008-09-12)
1095                  */
1096                 protected $spot_colors = array();
1097                 
1098                 /**
1099                  * Symbol used for HTML unordered list items
1100                  * @access protected
1101                  * @since 4.0.028 (2008-09-26)
1102                  */
1103                 protected $lisymbol = '';
1104                 
1105                 /**
1106                  * String used to mark the beginning and end of EPS image blocks
1107                  * @access protected
1108                  * @since 4.1.000 (2008-10-18)
1109                  */
1110                 protected $epsmarker = 'x#!#EPS#!#x';
1111                 
1112                 /**
1113                  * Array of transformation matrix
1114                  * @access protected
1115                  * @since 4.2.000 (2008-10-29)
1116                  */
1117                 protected $transfmatrix = array();
1118                 
1119                 /**
1120                  * Booklet mode for double-sided pages
1121                  * @access protected
1122                  * @since 4.2.000 (2008-10-29)
1123                  */
1124                 protected $booklet = false;
1125                 
1126                 /**
1127                  * Epsilon value used for float calculations
1128                  * @access protected
1129                  * @since 4.2.000 (2008-10-29)
1130                  */
1131                 protected $feps = 0.001;
1132                 
1133                 /**
1134                  * Array used for custom vertical spaces for HTML tags
1135                  * @access protected
1136                  * @since 4.2.001 (2008-10-30)
1137                  */
1138                 protected $tagvspaces = array();
1139                 
1140                 /**
1141                  * @var HTML PARSER: custom indent amount for lists.
1142                  * Negative value means disabled.
1143                  * @access protected
1144                  * @since 4.2.007 (2008-11-12)
1145                  */
1146                 protected $customlistindent = -1;
1147                 
1148                 /**
1149                  * @var if true keeps the border open for the cell sides that cross the page.
1150                  * @access protected
1151                  * @since 4.2.010 (2008-11-14)
1152                  */
1153                 protected $opencell = true;
1154
1155                 /**
1156                  * @var array of files to embedd
1157                  * @access protected
1158                  * @since 4.4.000 (2008-12-07)
1159                  */
1160                 protected $embeddedfiles = array();
1161
1162                 /**
1163                  * @var boolean true when inside html pre tag
1164                  * @access protected
1165                  * @since 4.4.001 (2008-12-08)
1166                  */
1167                 protected $premode = false;
1168
1169                 /**
1170                  * Array used to store positions of graphics transformation blocks inside the page buffer.
1171                  * keys are the page numbers
1172                  * @access protected
1173                  * @since 4.4.002 (2008-12-09)
1174                  */
1175                 protected $transfmrk = array();
1176
1177                 /**
1178                  * Default color for html links
1179                  * @access protected
1180                  * @since 4.4.003 (2008-12-09)
1181                  */
1182                 protected $htmlLinkColorArray = array(0, 0, 255);
1183
1184                 /**
1185                  * Default font style to add to html links
1186                  * @access protected
1187                  * @since 4.4.003 (2008-12-09)
1188                  */
1189                 protected $htmlLinkFontStyle = 'U';
1190
1191                 /**
1192                  * Counts the number of pages.
1193                  * @access protected
1194                  * @since 4.5.000 (2008-12-31)
1195                  */
1196                 protected $numpages = 0;
1197
1198                 /**
1199                  * Array containing page lenghts in bytes.
1200                  * @access protected
1201                  * @since 4.5.000 (2008-12-31)
1202                  */
1203                 protected $pagelen = array();
1204
1205                 /**
1206                  * Counts the number of pages.
1207                  * @access protected
1208                  * @since 4.5.000 (2008-12-31)
1209                  */
1210                 protected $numimages = 0;
1211
1212                 /**
1213                  * Store the image keys.
1214                  * @access protected
1215                  * @since 4.5.000 (2008-12-31)
1216                  */
1217                 protected $imagekeys = array();
1218
1219                 /**
1220                  * Lenght of the buffer in bytes.
1221                  * @access protected
1222                  * @since 4.5.000 (2008-12-31)
1223                  */
1224                 protected $bufferlen = 0;
1225
1226                 /**
1227                  * If true enables disk caching.
1228                  * @access protected
1229                  * @since 4.5.000 (2008-12-31)
1230                  */
1231                 protected $diskcache = false;
1232
1233                 /**
1234                  * Counts the number of fonts.
1235                  * @access protected
1236                  * @since 4.5.000 (2009-01-02)
1237                  */
1238                 protected $numfonts = 0;
1239
1240                 /**
1241                  * Store the font keys.
1242                  * @access protected
1243                  * @since 4.5.000 (2009-01-02)
1244                  */
1245                 protected $fontkeys = array();
1246
1247                 /**
1248                  * Store the fage status (true when opened, false when closed).
1249                  * @access protected
1250                  * @since 4.5.000 (2009-01-02)
1251                  */
1252                 protected $pageopen = array();
1253                 
1254                 /**
1255                  * Default monospaced font
1256                  * @access protected
1257                  * @since 4.5.025 (2009-03-10)
1258                  */
1259                 protected $default_monospaced_font = 'courier';
1260
1261                 /**
1262                  * Used to store a cloned copy of the current class object
1263                  * @access protected
1264                  * @since 4.5.029 (2009-03-19)
1265                  */
1266                 protected $objcopy;
1267
1268                 /**
1269                  * Array used to store the lenghts of cache files
1270                  * @access protected
1271                  * @since 4.5.029 (2009-03-19)
1272                  */
1273                 protected $cache_file_lenght = array();
1274
1275                 /**
1276                  * Table header content to be repeated on each new page
1277                  * @access protected
1278                  * @since 4.5.030 (2009-03-20)
1279                  */
1280                 protected $thead = '';
1281
1282                 /**
1283                  * Distance between the top of page and end of table headers on a new page.
1284                  * @access protected
1285                  * @since 4.5.030 (2009-03-20)
1286                  */
1287                 protected $theadMargin = '';
1288
1289                 /**
1290                  * Cache array for UTF8StringToArray() method.
1291                  * @access protected
1292                  * @since 4.5.037 (2009-04-07)
1293                  */
1294                 protected $cache_UTF8StringToArray = array();
1295
1296                 /**
1297                  * Maximum size of cache array used for UTF8StringToArray() method.
1298                  * @access protected
1299                  * @since 4.5.037 (2009-04-07)
1300                  */
1301                 protected $cache_maxsize_UTF8StringToArray = 8;
1302
1303                 /**
1304                  * Current size of cache array used for UTF8StringToArray() method.
1305                  * @access protected
1306                  * @since 4.5.037 (2009-04-07)
1307                  */
1308                 protected $cache_size_UTF8StringToArray = 0;
1309
1310                 /**
1311                  * If true enables document signing
1312                  * @access protected
1313                  * @since 4.6.005 (2009-04-24)
1314                  */
1315                 protected $sign = false;
1316
1317                 /**
1318                  * Signature data
1319                  * @access protected
1320                  * @since 4.6.005 (2009-04-24)
1321                  */
1322                 protected $signature_data = array();
1323
1324                 /**
1325                  * Signature max lenght
1326                  * @access protected
1327                  * @since 4.6.005 (2009-04-24)
1328                  */
1329                 protected $signature_max_lenght = 5120;
1330
1331                 /**
1332                  * Regular expression used to find blank characters used for word-wrapping.
1333                  * @access protected
1334                  * @since 4.6.006 (2009-04-28)
1335                  */
1336                 protected $re_spaces = '/[\s\p{Z}\p{Lo}]/';
1337
1338                 //------------------------------------------------------------
1339                 // METHODS
1340                 //------------------------------------------------------------
1341
1342                 /**
1343                  * This is the class constructor. 
1344                  * It allows to set up the page format, the orientation and 
1345                  * the measure unit used in all the methods (except for the font sizes).
1346                  * @since 1.0
1347                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1348                  * @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.
1349                  * @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>
1350                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1351                  * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1352                  * @param String $encoding charset encoding; default is UTF-8
1353                  * @access public
1354                  */
1355                 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1356                         /* Set internal character encoding to ASCII */
1357                         if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1358                                 $this->internal_encoding = mb_internal_encoding();
1359                                 mb_internal_encoding('ASCII');
1360                         }
1361                         // set disk caching
1362                         $this->diskcache = $diskcache ? true : false;
1363                         // set language direction
1364                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1365                         $this->tmprtl = false;
1366                         //Some checks
1367                         $this->_dochecks();
1368                         //Initialization of properties
1369                         $this->isunicode = $unicode;
1370                         $this->page = 0;
1371                         $this->transfmrk[0] = array();
1372                         $this->pagedim = array();
1373                         $this->n = 2;
1374                         $this->buffer = '';
1375                         $this->pages = array();
1376                         $this->state = 0;
1377                         $this->fonts = array();
1378                         $this->FontFiles = array();
1379                         $this->diffs = array();
1380                         $this->images = array();
1381                         $this->links = array();
1382                         $this->gradients = array();
1383                         $this->InFooter = false;
1384                         $this->lasth = 0;
1385                         $this->FontFamily = 'helvetica';
1386                         $this->FontStyle = '';
1387                         $this->FontSizePt = 12;
1388                         $this->underline = false;
1389                         $this->linethrough = false;
1390                         $this->DrawColor = '0 G';
1391                         $this->FillColor = '0 g';
1392                         $this->TextColor = '0 g';
1393                         $this->ColorFlag = false;
1394                         // encryption values
1395                         $this->encrypted = false;
1396                         $this->last_rc4_key = '';
1397                         $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";
1398                         //Standard Unicode fonts
1399                         $this->CoreFonts = array(
1400                                 'courier'=>'Courier',
1401                                 'courierB'=>'Courier-Bold',
1402                                 'courierI'=>'Courier-Oblique',
1403                                 'courierBI'=>'Courier-BoldOblique',
1404                                 'helvetica'=>'Helvetica',
1405                                 'helveticaB'=>'Helvetica-Bold',
1406                                 'helveticaI'=>'Helvetica-Oblique',
1407                                 'helveticaBI'=>'Helvetica-BoldOblique',
1408                                 'times'=>'Times-Roman',
1409                                 'timesB'=>'Times-Bold',
1410                                 'timesI'=>'Times-Italic',
1411                                 'timesBI'=>'Times-BoldItalic',
1412                                 'symbol'=>'Symbol',
1413                                 'zapfdingbats'=>'ZapfDingbats'
1414                         );
1415                         //Set scale factor
1416                         $this->setPageUnit($unit);
1417                         // set page format and orientation
1418                         $this->setPageFormat($format, $orientation);
1419                         //Page margins (1 cm)
1420                         $margin = 28.35 / $this->k;
1421                         $this->SetMargins($margin, $margin);
1422                         //Interior cell margin
1423                         $this->cMargin = $margin / 10;
1424                         //Line width (0.2 mm)
1425                         $this->LineWidth = 0.57 / $this->k;
1426                         $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1427                         $this->linestyleCap = '0 J';
1428                         $this->linestyleJoin = '0 j';
1429                         $this->linestyleDash = '[] 0 d';
1430                         //Automatic page break
1431                         $this->SetAutoPageBreak(true, (2 * $margin));
1432                         //Full width display mode
1433                         $this->SetDisplayMode('fullwidth');
1434                         //Compression
1435                         $this->SetCompression(true);
1436                         //Set default PDF version number
1437                         $this->PDFVersion = '1.7';
1438                         $this->encoding = $encoding;
1439                         $this->HREF = array();
1440                         $this->getFontsList();
1441                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1442                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1443                         $this->extgstates = array();
1444                         // user's rights
1445                         $this->sign = false;
1446                         $this->ur = false;
1447                         $this->ur_document = '/FullSave';
1448                         $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
1449                         $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1450                         $this->ur_signature = '/Modify';                        
1451                         // set default JPEG quality
1452                         $this->jpeg_quality = 75;
1453                         // initialize some settings
1454                         $this->utf8Bidi(array(''), '');
1455                         // set default font
1456                         $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1457                         // check if PCRE Unicode support is enabled
1458                         if (@preg_match('/\pL/u', 'a') == 1) {
1459                                 // PCRE unicode support is turned ON
1460                                 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1461                                 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1462                                 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1463                                 $this->re_spaces = '/[\s\p{Z}\p{Lo}]/';
1464                         } else {
1465                                 // PCRE unicode support is turned OFF
1466                                 $this->re_spaces = '/[\s]/';
1467                         }
1468                 }
1469                 
1470                 /**
1471                  * Default destructor.
1472                  * @access public
1473                  * @since 1.53.0.TC016
1474                  */
1475                 public function __destruct() {
1476                         // restore internal encoding
1477                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1478                                 mb_internal_encoding($this->internal_encoding);
1479                         }
1480                         // unset all class variables
1481                         $this->_destroy(true);
1482                 }
1483                 
1484                 /**
1485                  * Set the units of measure for the document.
1486                  * @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.
1487                  * @access public
1488                  * @since 3.0.015 (2008-06-06)
1489                  */
1490                 public function setPageUnit($unit) {
1491                 //Set scale factor
1492                         switch (strtolower($unit)) {
1493                                 // points
1494                                 case 'px':
1495                                 case 'pt': {
1496                                         $this->k = 1;
1497                                         break;
1498                                 }
1499                                 // millimeters
1500                                 case 'mm': {
1501                                         $this->k = $this->dpi / 25.4;
1502                                         break;
1503                                 }
1504                                 // centimeters
1505                                 case 'cm': {
1506                                         $this->k = $this->dpi / 2.54;
1507                                         break;
1508                                 }
1509                                 // inches
1510                                 case 'in': {
1511                                         $this->k = $this->dpi;
1512                                         break;
1513                                 }
1514                                 // unsupported unit
1515                                 default : {
1516                                         $this->Error('Incorrect unit: '.$unit);
1517                                         break;
1518                                 }
1519                         }
1520                         if (isset($this->CurOrientation)) {
1521                                         $this->setPageOrientation($this->CurOrientation);
1522                         }
1523                 }
1524                 
1525                 /**
1526                 * Set the page format
1527                 * @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>
1528                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1529                 * @access public
1530                 * @since 3.0.015 (2008-06-06)
1531                 */
1532                 public function setPageFormat($format, $orientation='P') {
1533                         //Page format
1534                         if (is_string($format)) {
1535                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1536                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1537                                 switch (strtoupper($format)) {
1538                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1539                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1540                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1541                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1542                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1543                                         case 'A3': {$format = array(841.89,1190.55); break;}
1544                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1545                                         case 'A5': {$format = array(419.53,595.28); break;}
1546                                         case 'A6': {$format = array(297.64,419.53); break;}
1547                                         case 'A7': {$format = array(209.76,297.64); break;}
1548                                         case 'A8': {$format = array(147.40,209.76); break;}
1549                                         case 'A9': {$format = array(104.88,147.40); break;}
1550                                         case 'A10': {$format = array(73.70,104.88); break;}
1551                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1552                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1553                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1554                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1555                                         case 'B4': {$format = array(708.66,1000.63); break;}
1556                                         case 'B5': {$format = array(498.90,708.66); break;}
1557                                         case 'B6': {$format = array(354.33,498.90); break;}
1558                                         case 'B7': {$format = array(249.45,354.33); break;}
1559                                         case 'B8': {$format = array(175.75,249.45); break;}
1560                                         case 'B9': {$format = array(124.72,175.75); break;}
1561                                         case 'B10': {$format = array(87.87,124.72); break;}
1562                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1563                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1564                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1565                                         case 'C3': {$format = array(918.43,1298.27); break;}
1566                                         case 'C4': {$format = array(649.13,918.43); break;}
1567                                         case 'C5': {$format = array(459.21,649.13); break;}
1568                                         case 'C6': {$format = array(323.15,459.21); break;}
1569                                         case 'C7': {$format = array(229.61,323.15); break;}
1570                                         case 'C8': {$format = array(161.57,229.61); break;}
1571                                         case 'C9': {$format = array(113.39,161.57); break;}
1572                                         case 'C10': {$format = array(79.37,113.39); break;}
1573                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1574                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1575                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1576                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1577                                         case 'RA4': {$format = array(609.45,864.57); break;}
1578                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1579                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1580                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1581                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1582                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1583                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1584                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1585                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1586                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1587                                 }
1588                                 $this->fwPt = $format[0];
1589                                 $this->fhPt = $format[1];
1590                         } else {
1591                                 $this->fwPt = $format[0] * $this->k;
1592                                 $this->fhPt = $format[1] * $this->k;
1593                         }
1594                         $this->setPageOrientation($orientation);
1595                 }
1596                 
1597                 /**
1598                 * Set page orientation.
1599                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1600                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1601                 * @param float $bottommargin bottom margin of the page.
1602                 * @access public
1603                 * @since 3.0.015 (2008-06-06)
1604                 */
1605                 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1606                         $orientation = strtoupper($orientation);
1607                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1608                                 $this->CurOrientation = 'P';
1609                                 $this->wPt = $this->fwPt;
1610                                 $this->hPt = $this->fhPt;
1611                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1612                                 $this->CurOrientation = 'L';
1613                                 $this->wPt = $this->fhPt;
1614                                 $this->hPt = $this->fwPt;
1615                         } else {
1616                                 $this->Error('Incorrect orientation: '.$orientation);
1617                         }
1618                         $this->w = $this->wPt / $this->k;
1619                         $this->h = $this->hPt / $this->k;
1620                         if ($this->empty_string($autopagebreak)) {
1621                                 if (isset($this->AutoPageBreak)) {
1622                                         $autopagebreak = $this->AutoPageBreak;
1623                                 } else {
1624                                         $autopagebreak = true;
1625                                 }
1626                         }
1627                         if ($this->empty_string($bottommargin)) {
1628                                 if (isset($this->bMargin)) {
1629                                         $bottommargin = $this->bMargin;
1630                                 } else {
1631                                         // default value = 2 cm
1632                                         $bottommargin = 2 * 28.35 / $this->k;
1633                                 }
1634                         }
1635                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1636                         // store page dimensions
1637                         $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);
1638                 }
1639                                 
1640                 /**
1641                  * Enable or disable Right-To-Left language mode
1642                  * @param Boolean $enable if true enable Right-To-Left language mode.
1643                  * @access public
1644                 * @since 2.0.000 (2008-01-03)
1645                  */
1646                 public function setRTL($enable) {
1647                         $this->rtl = $enable ? true : false;
1648                         $this->tmprtl = false;
1649                 }
1650                 
1651                 /**
1652                  * Return the RTL status
1653                  * @return boolean
1654                  * @access public
1655                  * @since 4.0.012 (2008-07-24)
1656                  */
1657                 public function getRTL() {
1658                         return $this->rtl;
1659                 }
1660                 
1661                 /**
1662                 * Force temporary RTL language direction
1663                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1664                 * @access public
1665                 * @since 2.1.000 (2008-01-09)
1666                 */
1667                 public function setTempRTL($mode) {
1668                         switch ($mode) {
1669                                 case false:
1670                                 case 'L':
1671                                 case 'R': {
1672                                         $this->tmprtl = $mode;
1673                                 }
1674                         }
1675                 }
1676                 
1677                 /**
1678                 * Set the last cell height.
1679                 * @param float $h cell height.
1680                 * @author Nicola Asuni
1681                 * @access public
1682                 * @since 1.53.0.TC034
1683                 */
1684                 public function setLastH($h) {
1685                         $this->lasth = $h;
1686                 }
1687                 
1688                 /**
1689                 * Get the last cell height.
1690                 * @return last cell height
1691                 * @access public
1692                 * @since 4.0.017 (2008-08-05)
1693                 */
1694                 public function getLastH() {
1695                         return $this->lasth;
1696                 }
1697                 
1698                 /**
1699                 * Set the adjusting factor to convert pixels to user units.
1700                 * @param float $scale adjusting factor to convert pixels to user units.
1701                 * @author Nicola Asuni
1702                 * @access public
1703                 * @since 1.5.2
1704                 */
1705                 public function setImageScale($scale) {
1706                         $this->imgscale = $scale;
1707                 }
1708
1709                 /**
1710                 * Returns the adjusting factor to convert pixels to user units.
1711                 * @return float adjusting factor to convert pixels to user units.
1712                 * @author Nicola Asuni
1713                 * @access public
1714                 * @since 1.5.2
1715                 */
1716                 public function getImageScale() {
1717                         return $this->imgscale;
1718                 }
1719                                 
1720                 /**
1721                 * Returns an array of page dimensions:
1722                 * <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>
1723                 * @param int $pagenum page number (empty = current page)
1724                 * @return array of page dimensions.
1725                 * @author Nicola Asuni
1726                 * @access public
1727                 * @since 4.5.027 (2009-03-16)
1728                 */
1729                 public function getPageDimensions($pagenum='') {
1730                         if (empty($pagenum)) {
1731                                 $pagenum = $this->page;
1732                         }
1733                         return $this->pagedim[$pagenum];
1734                 }
1735                 
1736                 /**
1737                 * Returns the page width in units.
1738                 * @param int $pagenum page number (empty = current page)
1739                 * @return int page width.
1740                 * @author Nicola Asuni
1741                 * @access public
1742                 * @since 1.5.2
1743                 * @see getPageDimensions()
1744                 */
1745                 public function getPageWidth($pagenum='') {
1746                         if (empty($pagenum)) {
1747                                 return $this->w;
1748                         }
1749                         return $this->pagedim[$pagenum]['w'];
1750                 }
1751
1752                 /**
1753                 * Returns the page height in units.
1754                 * @param int $pagenum page number (empty = current page)
1755                 * @return int page height.
1756                 * @author Nicola Asuni
1757                 * @access public
1758                 * @since 1.5.2
1759                 * @see getPageDimensions()
1760                 */
1761                 public function getPageHeight($pagenum='') {
1762                         if (empty($pagenum)) {
1763                                 return $this->h;
1764                         }
1765                         return $this->pagedim[$pagenum]['h'];
1766                 }
1767
1768                 /**
1769                 * Returns the page break margin.
1770                 * @param int $pagenum page number (empty = current page)
1771                 * @return int page break margin.
1772                 * @author Nicola Asuni
1773                 * @access public
1774                 * @since 1.5.2
1775                 * @see getPageDimensions()
1776                 */
1777                 public function getBreakMargin($pagenum='') {
1778                         if (empty($pagenum)) {
1779                                 return $this->bMargin;
1780                         }
1781                         return $this->pagedim[$pagenum]['bm'];
1782                 }
1783
1784                 /**
1785                 * Returns the scale factor (number of points in user unit).
1786                 * @return int scale factor.
1787                 * @author Nicola Asuni
1788                 * @access public
1789                 * @since 1.5.2
1790                 */
1791                 public function getScaleFactor() {
1792                         return $this->k;
1793                 }
1794
1795                 /**
1796                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1797                 * @param float $left Left margin.
1798                 * @param float $top Top margin.
1799                 * @param float $right Right margin. Default value is the left one.
1800                 * @access public
1801                 * @since 1.0
1802                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1803                 */
1804                 public function SetMargins($left, $top, $right=-1) {
1805                         //Set left, top and right margins
1806                         $this->lMargin = $left;
1807                         $this->tMargin = $top;
1808                         if ($right == -1) {
1809                                 $right = $left;
1810                         }
1811                         $this->rMargin = $right;
1812                 }
1813
1814                 /**
1815                 * 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.
1816                 * @param float $margin The margin.
1817                 * @access public
1818                 * @since 1.4
1819                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1820                 */
1821                 public function SetLeftMargin($margin) {
1822                         //Set left margin
1823                         $this->lMargin=$margin;
1824                         if (($this->page > 0) AND ($this->x < $margin)) {
1825                                 $this->x = $margin;
1826                         }
1827                 }
1828
1829                 /**
1830                 * Defines the top margin. The method can be called before creating the first page.
1831                 * @param float $margin The margin.
1832                 * @access public
1833                 * @since 1.5
1834                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1835                 */
1836                 public function SetTopMargin($margin) {
1837                         //Set top margin
1838                         $this->tMargin=$margin;
1839                         if (($this->page > 0) AND ($this->y < $margin)) {
1840                                 $this->y = $margin;
1841                         }
1842                 }
1843
1844                 /**
1845                 * Defines the right margin. The method can be called before creating the first page.
1846                 * @param float $margin The margin.
1847                 * @access public
1848                 * @since 1.5
1849                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1850                 */
1851                 public function SetRightMargin($margin) {
1852                         $this->rMargin=$margin;
1853                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1854                                 $this->x = $this->w - $margin;
1855                         }
1856                 }
1857
1858                 /**
1859                 * Set the internal Cell padding.
1860                 * @param float $pad internal padding.
1861                 * @access public
1862                 * @since 2.1.000 (2008-01-09)
1863                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1864                 */
1865                 public function SetCellPadding($pad) {
1866                         $this->cMargin = $pad;
1867                 }
1868
1869                 /**
1870                 * 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.
1871                 * @param boolean $auto Boolean indicating if mode should be on or off.
1872                 * @param float $margin Distance from the bottom of the page.
1873                 * @access public
1874                 * @since 1.0
1875                 * @see Cell(), MultiCell(), AcceptPageBreak()
1876                 */
1877                 public function SetAutoPageBreak($auto, $margin=0) {
1878                         //Set auto page break mode and triggering margin
1879                         $this->AutoPageBreak = $auto;
1880                         $this->bMargin = $margin;
1881                         $this->PageBreakTrigger = $this->h - $margin;
1882                 }
1883
1884                 /**
1885                 * Defines the way the document is to be displayed by the viewer.
1886                 * @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>
1887                 * @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>
1888                 * @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>
1889                 * @access public
1890                 * @since 1.2
1891                 */
1892                 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
1893                         //Set display mode in viewer
1894                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1895                                 $this->ZoomMode = $zoom;
1896                         } else {
1897                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1898                         }
1899                         switch ($layout) {
1900                                 case 'default':
1901                                 case 'single':
1902                                 case 'SinglePage': {
1903                                         $this->LayoutMode = 'SinglePage';
1904                                         break;
1905                                 }
1906                                 case 'continuous':
1907                                 case 'OneColumn': {
1908                                         $this->LayoutMode = 'OneColumn';
1909                                         break;
1910                                 }
1911                                 case 'two':
1912                                 case 'TwoColumnLeft': {
1913                                         $this->LayoutMode = 'TwoColumnLeft';
1914                                         break;
1915                                 }
1916                                 case 'TwoColumnRight': {
1917                                         $this->LayoutMode = 'TwoColumnRight';
1918                                         break;
1919                                 }
1920                                 case 'TwoPageLeft': {
1921                                         $this->LayoutMode = 'TwoPageLeft';
1922                                         break;
1923                                 }
1924                                 case 'TwoPageRight': {
1925                                         $this->LayoutMode = 'TwoPageRight';
1926                                         break;
1927                                 }
1928                                 default: {
1929                                         $this->LayoutMode = 'SinglePage';
1930                                 }
1931                         }
1932                         switch ($mode) {
1933                                 case 'UseNone': {
1934                                         $this->PageMode = 'UseNone';
1935                                         break;
1936                                 }
1937                                 case 'UseOutlines': {
1938                                         $this->PageMode = 'UseOutlines';
1939                                         break;
1940                                 }
1941                                 case 'UseThumbs': {
1942                                         $this->PageMode = 'UseThumbs';
1943                                         break;
1944                                 }
1945                                 case 'FullScreen': {
1946                                         $this->PageMode = 'FullScreen';
1947                                         break;
1948                                 }
1949                                 case 'UseOC': {
1950                                         $this->PageMode = 'UseOC';
1951                                         break;
1952                                 }
1953                                 case '': {
1954                                         $this->PageMode = 'UseAttachments';
1955                                         break;
1956                                 }
1957                                 default: {
1958                                         $this->PageMode = 'UseNone';
1959                                 }
1960                         }
1961                 }
1962
1963                 /**
1964                 * 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.
1965                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1966                 * @param boolean $compress Boolean indicating if compression must be enabled.
1967                 * @access public
1968                 * @since 1.4
1969                 */
1970                 public function SetCompression($compress) {
1971                         //Set page compression
1972                         if (function_exists('gzcompress')) {
1973                                 $this->compress = $compress;
1974                         } else {
1975                                 $this->compress = false;
1976                         }
1977                 }
1978
1979                 /**
1980                 * Defines the title of the document.
1981                 * @param string $title The title.
1982                 * @access public
1983                 * @since 1.2
1984                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1985                 */
1986                 public function SetTitle($title) {
1987                         //Title of document
1988                         $this->title = $title;
1989                 }
1990
1991                 /**
1992                 * Defines the subject of the document.
1993                 * @param string $subject The subject.
1994                 * @access public
1995                 * @since 1.2
1996                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1997                 */
1998                 public function SetSubject($subject) {
1999                         //Subject of document
2000                         $this->subject = $subject;
2001                 }
2002
2003                 /**
2004                 * Defines the author of the document.
2005                 * @param string $author The name of the author.
2006                 * @access public
2007                 * @since 1.2
2008                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2009                 */
2010                 public function SetAuthor($author) {
2011                         //Author of document
2012                         $this->author = $author;
2013                 }
2014
2015                 /**
2016                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2017                 * @param string $keywords The list of keywords.
2018                 * @access public
2019                 * @since 1.2
2020                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2021                 */
2022                 public function SetKeywords($keywords) {
2023                         //Keywords of document
2024                         $this->keywords = $keywords;
2025                 }
2026
2027                 /**
2028                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2029                 * @param string $creator The name of the creator.
2030                 * @access public
2031                 * @since 1.2
2032                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2033                 */
2034                 public function SetCreator($creator) {
2035                         //Creator of document
2036                         $this->creator = $creator;
2037                 }
2038                 
2039                 /**
2040                 * 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.
2041                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
2042                 * @param string $msg The error message
2043                 * @access public
2044                 * @since 1.0
2045                 */
2046                 public function Error($msg) {
2047                         // unset all class variables
2048                         $this->_destroy(true);
2049                         // exit program and print error
2050                         die('<strong>TCPDF ERROR: </strong>'.$msg);
2051                 }
2052
2053                 /**
2054                 * This method begins the generation of the PDF document.
2055                 * It is not necessary to call it explicitly because AddPage() does it automatically.
2056                 * Note: no page is created by this method
2057                 * @access public
2058                 * @since 1.0
2059                 * @see AddPage(), Close()
2060                 */
2061                 public function Open() {
2062                         //Begin document
2063                         $this->state = 1;
2064                 }
2065
2066                 /**
2067                 * Terminates the PDF document.
2068                 * It is not necessary to call this method explicitly because Output() does it automatically.
2069                 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2070                 * @access public
2071                 * @since 1.0
2072                 * @see Open(), Output()
2073                 */
2074                 public function Close() {
2075                         if ($this->state == 3) {
2076                                 return;
2077                         }
2078                         if ($this->page == 0) {
2079                                 $this->AddPage();
2080                         }
2081                         // close page
2082                         $this->endPage();
2083                         // close document
2084                         $this->_enddoc();
2085                         // unset all class variables (except critical ones)
2086                         $this->_destroy(false);
2087                 }
2088                 
2089                 /**
2090                 * Move pointer at the specified document page and update page dimensions.
2091                 * @param int $pnum page number
2092                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2093                 * @access public
2094                 * @since 2.1.000 (2008-01-07)
2095                 * @see getPage(), lastpage(), getNumPages()
2096                 */
2097                 public function setPage($pnum, $resetmargins=false) {
2098                         if ($pnum == $this->page) {
2099                                 return;
2100                         }
2101                         if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2102                                 $this->state = 2;
2103                                 // save current graphic settings
2104                                 //$gvars = $this->getGraphicVars();
2105                                 $oldpage = $this->page;
2106                                 $this->page = $pnum;
2107                                 $this->wPt = $this->pagedim[$this->page]['w'];
2108                                 $this->hPt = $this->pagedim[$this->page]['h'];
2109                                 $this->w = $this->wPt / $this->k;
2110                                 $this->h = $this->hPt / $this->k;
2111                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
2112                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
2113                                 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2114                                 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
2115                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
2116                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
2117                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
2118                                 // restore graphic settings
2119                                 //$this->setGraphicVars($gvars);
2120                                 if ($resetmargins) {
2121                                         $this->lMargin = $this->pagedim[$this->page]['olm'];
2122                                         $this->rMargin = $this->pagedim[$this->page]['orm'];
2123                                         $this->SetY($this->tMargin);
2124                                 } else {
2125                                         // account for booklet mode
2126                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
2127                                                 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
2128                                                 $this->lMargin += $deltam;
2129                                                 $this->rMargin -= $deltam;
2130                                         }
2131                                 }
2132                         } else {
2133                                 $this->Error('Wrong page number on setPage() function.');
2134                         }
2135                 }
2136                 
2137                 /**
2138                 * Reset pointer to the last document page.
2139                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2140                 * @access public
2141                 * @since 2.0.000 (2008-01-04)
2142                 * @see setPage(), getPage(), getNumPages()
2143                 */
2144                 public function lastPage($resetmargins=false) {
2145                         $this->setPage($this->getNumPages(), $resetmargins);
2146                 }
2147                 
2148                 /**
2149                 * Get current document page number.
2150                 * @return int page number
2151                 * @access public
2152                 * @since 2.1.000 (2008-01-07)
2153                 * @see setPage(), lastpage(), getNumPages()
2154                 */
2155                 public function getPage() {
2156                         return $this->page;
2157                 }
2158                 
2159                 
2160                 /**
2161                 * Get the total number of insered pages.
2162                 * @return int number of pages
2163                 * @access public
2164                 * @since 2.1.000 (2008-01-07)
2165                 * @see setPage(), getPage(), lastpage()
2166                 */
2167                 public function getNumPages() {
2168                         return $this->numpages;
2169                 }
2170
2171                 /**
2172                 * 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).
2173                 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
2174                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2175                 * @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>
2176                 * @access public
2177                 * @since 1.0
2178                 * @see startPage(), endPage()
2179                 */
2180                 public function AddPage($orientation='', $format='') {
2181                         if (!isset($this->original_lMargin)) {
2182                                 $this->original_lMargin = $this->lMargin;
2183                         }
2184                         if (!isset($this->original_rMargin)) {
2185                                 $this->original_rMargin = $this->rMargin;
2186                         }
2187                         // terminate previous page
2188                         $this->endPage();
2189                         // start new page
2190                         $this->startPage($orientation, $format);
2191                 }
2192
2193                 /**
2194                 * Terminate the current page
2195                 * @access protected
2196                 * @since 4.2.010 (2008-11-14)
2197                 * @see startPage(), AddPage()
2198                 */
2199                 protected function endPage() {
2200                         // check if page is already closed
2201                         if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
2202                                 return;
2203                         }
2204                         $this->InFooter = true;
2205                         // print page footer
2206                         $this->setFooter();
2207                         // close page
2208                         $this->_endpage();
2209                         // mark page as closed
2210                         $this->pageopen[$this->page] = false;
2211                         $this->InFooter = false;
2212                 }
2213
2214                 /**
2215                 * Starts a new page to the document. The page must be closed using the endPage() function.
2216                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
2217                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2218                 * @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>
2219                 * @access protected
2220                 * @since 4.2.010 (2008-11-14)
2221                 * @see endPage(), AddPage()
2222                 */
2223                 protected function startPage($orientation='', $format='') {
2224                         if ($this->numpages > $this->page) {
2225                                 // this page has been already added
2226                                 $this->setPage($this->page + 1);
2227                                 $this->SetY($this->tMargin);
2228                                 return;
2229                         }
2230                         // start a new page
2231                         if ($this->state == 0) {
2232                                 $this->Open();
2233                         }
2234                         ++$this->numpages;
2235                         $this->swapMargins($this->booklet);
2236                         // save current graphic settings
2237                         $gvars = $this->getGraphicVars();
2238                         // start new page
2239                         $this->_beginpage($orientation, $format);
2240                         // mark page as open
2241                         $this->pageopen[$this->page] = true;
2242                         // restore graphic settings
2243                         $this->setGraphicVars($gvars);
2244                         // mark this point
2245                         $this->setPageMark();
2246                         // print page header
2247                         $this->setHeader();
2248                         // restore graphic settings
2249                         $this->setGraphicVars($gvars);
2250                         // mark this point
2251                         $this->setPageMark();
2252                         // print table header (if any)
2253                         $this->setTableHeader();
2254                 }
2255                         
2256                 /**
2257                  * Set start-writing mark on current page for multicell borders and fills.
2258                  * This function must be called after calling Image() function for a background image.
2259                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
2260                  * @access public
2261                  * @since 4.0.016 (2008-07-30)
2262                  */
2263                 public function setPageMark() {
2264                         $this->intmrk[$this->page] = $this->pagelen[$this->page];
2265                 }
2266                 
2267                 /**
2268                  * Set header data.
2269                  * @param string $ln header image logo
2270                  * @param string $lw header image logo width in mm
2271                  * @param string $ht string to print as title on document header
2272                  * @param string $hs string to print on document header
2273                  * @access public
2274                  */
2275                 public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
2276                         $this->header_logo = $ln;
2277                         $this->header_logo_width = $lw;
2278                         $this->header_title = $ht;
2279                         $this->header_string = $hs;
2280                 }
2281                 
2282                 /**
2283                  * Returns header data:
2284                  * <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>
2285                  * @return array()
2286                  * @access public
2287                  * @since 4.0.012 (2008-07-24)
2288                  */
2289                 public function getHeaderData() {
2290                         $ret = array();
2291                         $ret['logo'] = $this->header_logo;
2292                         $ret['logo_width'] = $this->header_logo_width;
2293                         $ret['title'] = $this->header_title;
2294                         $ret['string'] = $this->header_string;
2295                         return $ret;
2296                 }
2297                 
2298                 /**
2299                  * Set header margin.
2300                  * (minimum distance between header and top page margin)
2301                  * @param int $hm distance in user units
2302                  * @access public
2303                  */
2304                 public function setHeaderMargin($hm=10) {
2305                         $this->header_margin = $hm;
2306                 }
2307                 
2308                 /**
2309                  * Returns header margin in user units.
2310                  * @return float
2311                  * @since 4.0.012 (2008-07-24)
2312                  * @access public
2313                  */
2314                 public function getHeaderMargin() {
2315                         return $this->header_margin;
2316                 }
2317                 
2318                 /**
2319                  * Set footer margin.
2320                  * (minimum distance between footer and bottom page margin)
2321                  * @param int $fm distance in user units
2322                  * @access public
2323                  */
2324                 public function setFooterMargin($fm=10) {
2325                         $this->footer_margin = $fm;
2326                 }
2327                 
2328                 /**
2329                  * Returns footer margin in user units.
2330                  * @return float
2331                  * @since 4.0.012 (2008-07-24)
2332                  * @access public
2333                  */
2334                 public function getFooterMargin() {
2335                         return $this->footer_margin;
2336                 }
2337                 /**
2338                  * Set a flag to print page header.
2339                  * @param boolean $val set to true to print the page header (default), false otherwise. 
2340                  * @access public
2341                  */
2342                 public function setPrintHeader($val=true) {
2343                         $this->print_header = $val;
2344                 }
2345                 
2346                 /**
2347                  * Set a flag to print page footer.
2348                  * @param boolean $value set to true to print the page footer (default), false otherwise. 
2349                  * @access public
2350                  */
2351                 public function setPrintFooter($val=true) {
2352                         $this->print_footer = $val;
2353                 }
2354                 
2355                 /**
2356                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2357                  * @return float 
2358                  * @access public
2359                  */
2360                 public function getImageRBX() {
2361                         return $this->img_rb_x;
2362                 }
2363                 
2364                 /**
2365                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2366                  * @return float 
2367                  * @access public
2368                  */
2369                 public function getImageRBY() {
2370                         return $this->img_rb_y;
2371                 }
2372                 
2373                 /**
2374                  * This method is used to render the page header.
2375                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2376                  * @access public
2377                  */
2378                 public function Header() {
2379                         $ormargins = $this->getOriginalMargins();
2380                         $headerfont = $this->getHeaderFont();
2381                         $headerdata = $this->getHeaderData();
2382                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2383                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2384                                 $imgy = $this->getImageRBY();
2385                         } else {
2386                                 $imgy = $this->GetY();
2387                         }
2388                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2389                         // set starting margin for text data cell
2390                         if ($this->getRTL()) {
2391                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2392                         } else {
2393                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2394                         }
2395                         $this->SetTextColor(0, 0, 0);
2396                         // header title
2397                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2398                         $this->SetX($header_x);                 
2399                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
2400                         // header string
2401                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2402                         $this->SetX($header_x);
2403                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
2404                         // print an ending header line
2405                         $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2406                         $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
2407                         if ($this->getRTL()) {
2408                                 $this->SetX($ormargins['right']);
2409                         } else {
2410                                 $this->SetX($ormargins['left']);
2411                         }
2412                         $this->Cell(0, 0, '', 'T', 0, 'C');
2413                 }
2414                 
2415                 /**
2416                  * This method is used to render the page footer. 
2417                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2418                  * @access public
2419                  */
2420                 public function Footer() {                              
2421                         $cur_y = $this->GetY();
2422                         $ormargins = $this->getOriginalMargins();
2423                         $this->SetTextColor(0, 0, 0);                   
2424                         //set style for cell border
2425                         $line_width = 0.85 / $this->getScaleFactor();
2426                         $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2427                         //print document barcode
2428                         $barcode = $this->getBarcode();
2429                         if (!empty($barcode)) {
2430                                 $this->Ln($line_width);
2431                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2432                                 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');     
2433                         }
2434                         if (empty($this->pagegroups)) {
2435                                 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
2436                         } else {
2437                                 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
2438                         }               
2439                         $this->SetY($cur_y);
2440                         //Print page number
2441                         if ($this->getRTL()) {
2442                                 $this->SetX($ormargins['right']);
2443                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2444                         } else {
2445                                 $this->SetX($ormargins['left']);
2446                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2447                         }
2448                 }
2449                 
2450                 /**
2451                  * This method is used to render the page header. 
2452                  * @access protected
2453                  * @since 4.0.012 (2008-07-24)
2454                  */
2455                 protected function setHeader() {
2456                         if ($this->print_header) {
2457                                 $lasth = $this->lasth;
2458                                 $this->_out('q');
2459                                 $this->rMargin = $this->original_rMargin;
2460                                 $this->lMargin = $this->original_lMargin;
2461                                 $this->cMargin = 0;
2462                                 //set current position
2463                                 if ($this->rtl) {
2464                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2465                                 } else {
2466                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2467                                 }
2468                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2469                                 $this->Header();
2470                                 //restore position
2471                                 if ($this->rtl) {
2472                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2473                                 } else {
2474                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2475                                 }
2476                                 $this->_out('Q');
2477                                 $this->lasth = $lasth;
2478                         }
2479                 }
2480                 
2481                 /**
2482                  * This method is used to render the page footer. 
2483                  * @access protected
2484                  * @since 4.0.012 (2008-07-24)
2485                  */
2486                 protected function setFooter() {
2487                         //Page footer
2488                         // save current graphic settings
2489                         $gvars = $this->getGraphicVars();
2490                         // mark this point
2491                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
2492                         $this->_out("\n");
2493                         if ($this->print_footer) {
2494                                 $lasth = $this->lasth;
2495                                 $this->_out('q');
2496                                 $this->rMargin = $this->original_rMargin;
2497                                 $this->lMargin = $this->original_lMargin;
2498                                 $this->cMargin = 0;
2499                                 //set current position
2500                                 $footer_y = $this->h - $this->footer_margin;
2501                                 if ($this->rtl) {
2502                                         $this->SetXY($this->original_rMargin, $footer_y);
2503                                 } else {
2504                                         $this->SetXY($this->original_lMargin, $footer_y);
2505                                 }
2506                                 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
2507                                 $this->Footer();
2508                                 //restore position
2509                                 if ($this->rtl) {
2510                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2511                                 } else {
2512                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2513                                 }
2514                                 $this->_out('Q');
2515                                 $this->lasth = $lasth;
2516                         }
2517                         // restore graphic settings
2518                         $this->setGraphicVars($gvars);
2519                         // calculate footer lenght
2520                         $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
2521                 }
2522
2523                 /**
2524                  * This method is used to render the table header on new page (if any). 
2525                  * @access protected
2526                  * @since 4.5.030 (2009-03-25)
2527                  */
2528                 protected function setTableHeader() {
2529                         if (!$this->empty_string($this->theadMargin)) {
2530                                 // restore the original top-margin
2531                                 $this->tMargin = $this->theadMargin;
2532                                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
2533                                 $this->y = $this->theadMargin;
2534                         }
2535                         if (!$this->empty_string($this->thead)) {
2536                                 // print table header
2537                                 $this->writeHTML($this->thead, false, false, false, false, '');
2538                                 // set new top margin to skip the table headers
2539                                 if (!isset($this->theadMargin) OR ($this->empty_string($this->theadMargin))) {
2540                                         $this->theadMargin = $this->tMargin;
2541                                 }
2542                                 $this->tMargin = $this->y;
2543                                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2544                         }
2545                 }
2546                 
2547                 /**
2548                 * Returns the current page number.
2549                 * @return int page number
2550                 * @access public
2551                 * @since 1.0
2552                 * @see AliasNbPages(), getAliasNbPages()
2553                 */
2554                 public function PageNo() {
2555                         return $this->page;
2556                 }
2557
2558                 /**
2559                 * Defines a new spot color. 
2560                 * It can be expressed in RGB components or gray scale. 
2561                 * The method can be called before the first page is created and the value is retained from page to page.
2562                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2563                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2564                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2565                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2566                 * @access public
2567                 * @since 4.0.024 (2008-09-12)
2568                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2569                 */
2570                 public function AddSpotColor($name, $c, $m, $y, $k) {
2571                         if (!isset($this->spot_colors[$name])) {
2572                                 $i = 1 + count($this->spot_colors);
2573                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2574                         }
2575                 }
2576
2577                 /**
2578                 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 
2579                 * It can be expressed in RGB components or gray scale. 
2580                 * The method can be called before the first page is created and the value is retained from page to page.
2581                 * @param array $color array of colors
2582                 * @access public
2583                 * @since 3.1.000 (2008-06-11)
2584                 * @see SetDrawColor()
2585                 */
2586                 public function SetDrawColorArray($color) {
2587                         if (isset($color)) {
2588                                 $color = array_values($color);
2589                                 $r = isset($color[0]) ? $color[0] : -1;
2590                                 $g = isset($color[1]) ? $color[1] : -1;
2591                                 $b = isset($color[2]) ? $color[2] : -1;
2592                                 $k = isset($color[3]) ? $color[3] : -1;
2593                                 if ($r >= 0) {
2594                                         $this->SetDrawColor($r, $g, $b, $k);
2595                                 }
2596                         }
2597                 }
2598
2599                 /**
2600                 * 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.
2601                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2602                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2603                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2604                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2605                 * @access public
2606                 * @since 1.3
2607                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2608                 */
2609                 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2610                         // set default values
2611                         if (!is_numeric($col1)) {
2612                                 $col1 = 0;
2613                         }
2614                         if (!is_numeric($col2)) {
2615                                 $col2 = -1;
2616                         }
2617                         if (!is_numeric($col3)) {
2618                                 $col3 = -1;
2619                         }
2620                         if (!is_numeric($col4)) {
2621                                 $col4 = -1;
2622                         }
2623                         //Set color for all stroking operations
2624                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2625                                 // Grey scale
2626                                 $this->DrawColor = sprintf('%.3F G', $col1/255);
2627                         } elseif ($col4 == -1) {
2628                                 // RGB
2629                                 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
2630                         } else {
2631                                 // CMYK
2632                                 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
2633                         }
2634                         if ($this->page > 0) {
2635                                 $this->_out($this->DrawColor);
2636                         }
2637                 }
2638                 
2639                 /**
2640                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2641                 * @param string $name name of the spot color
2642                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2643                 * @access public
2644                 * @since 4.0.024 (2008-09-12)
2645                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2646                 */
2647                 public function SetDrawSpotColor($name, $tint=100) {
2648                         if (!isset($this->spot_colors[$name])) {
2649                                 $this->Error('Undefined spot color: '.$name);
2650                         }
2651                         $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
2652                         if ($this->page > 0) {
2653                                 $this->_out($this->DrawColor);
2654                         }
2655                 }
2656                 
2657                 /**
2658                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 
2659                 * It can be expressed in RGB components or gray scale. 
2660                 * The method can be called before the first page is created and the value is retained from page to page.
2661                 * @param array $color array of colors
2662                 * @access public
2663                 * @since 3.1.000 (2008-6-11)
2664                 * @see SetFillColor()
2665                 */
2666                 public function SetFillColorArray($color) {
2667                         if (isset($color)) {
2668                                 $color = array_values($color);
2669                                 $r = isset($color[0]) ? $color[0] : -1;
2670                                 $g = isset($color[1]) ? $color[1] : -1;
2671                                 $b = isset($color[2]) ? $color[2] : -1;
2672                                 $k = isset($color[3]) ? $color[3] : -1;
2673                                 if ($r >= 0) {
2674                                         $this->SetFillColor($r, $g, $b, $k);
2675                                 }
2676                         }
2677                 }
2678                 
2679                 /**
2680                 * 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.
2681                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2682                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2683                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2684                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2685                 * @access public
2686                 * @since 1.3
2687                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2688                 */
2689                 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2690                         // set default values
2691                         if (!is_numeric($col1)) {
2692                                 $col1 = 0;
2693                         }
2694                         if (!is_numeric($col2)) {
2695                                 $col2 = -1;
2696                         }
2697                         if (!is_numeric($col3)) {
2698                                 $col3 = -1;
2699                         }
2700                         if (!is_numeric($col4)) {
2701                                 $col4 = -1;
2702                         }
2703                         //Set color for all filling operations
2704                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2705                                 // Grey scale
2706                                 $this->FillColor = sprintf('%.3F g', $col1/255);
2707                                 $this->bgcolor = array('G' => $col1);
2708                         } elseif ($col4 == -1) {
2709                                 // RGB
2710                                 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2711                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2712                         } else {
2713                                 // CMYK
2714                                 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2715                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2716                         }
2717                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2718                         if ($this->page > 0) {
2719                                 $this->_out($this->FillColor);
2720                         }
2721                 }
2722                 
2723                 /**
2724                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2725                 * @param string $name name of the spot color
2726                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2727                 * @access public
2728                 * @since 4.0.024 (2008-09-12)
2729                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2730                 */
2731                 public function SetFillSpotColor($name, $tint=100) {
2732                         if (!isset($this->spot_colors[$name])) {
2733                                 $this->Error('Undefined spot color: '.$name);
2734                         }
2735                         $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2736                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2737                         if ($this->page > 0) {
2738                                 $this->_out($this->FillColor);
2739                         }
2740                 }
2741                 
2742                 /**
2743                 * Defines the color used for text. It can be expressed in RGB components or gray scale. 
2744                 * The method can be called before the first page is created and the value is retained from page to page.
2745                 * @param array $color array of colors
2746                 * @access public
2747                 * @since 3.1.000 (2008-6-11)
2748                 * @see SetFillColor()
2749                 */
2750                 public function SetTextColorArray($color) {
2751                         if (isset($color)) {
2752                                 $color = array_values($color);
2753                                 $r = isset($color[0]) ? $color[0] : -1;
2754                                 $g = isset($color[1]) ? $color[1] : -1;
2755                                 $b = isset($color[2]) ? $color[2] : -1;
2756                                 $k = isset($color[3]) ? $color[3] : -1;
2757                                 if ($r >= 0) {
2758                                         $this->SetTextColor($r, $g, $b, $k);
2759                                 }
2760                         }
2761                 }
2762
2763                 /**
2764                 * 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.
2765                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2766                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2767                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2768                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2769                 * @access public
2770                 * @since 1.3
2771                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2772                 */
2773                 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2774                         // set default values
2775                         if (!is_numeric($col1)) {
2776                                 $col1 = 0;
2777                         }
2778                         if (!is_numeric($col2)) {
2779                                 $col2 = -1;
2780                         }
2781                         if (!is_numeric($col3)) {
2782                                 $col3 = -1;
2783                         }
2784                         if (!is_numeric($col4)) {
2785                                 $col4 = -1;
2786                         }
2787                         //Set color for text
2788                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2789                                 // Grey scale
2790                                 $this->TextColor = sprintf('%.3F g', $col1/255);
2791                                 $this->fgcolor = array('G' => $col1);
2792                         } elseif ($col4 == -1) {
2793                                 // RGB
2794                                 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2795                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2796                         } else {
2797                                 // CMYK
2798                                 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2799                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2800                         }
2801                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2802                 }
2803                 
2804                 /**
2805                 * Defines the spot color used for text.
2806                 * @param string $name name of the spot color
2807                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2808                 * @access public
2809                 * @since 4.0.024 (2008-09-12)
2810                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2811                 */
2812                 public function SetTextSpotColor($name, $tint=100) {
2813                         if (!isset($this->spot_colors[$name])) {
2814                                 $this->Error('Undefined spot color: '.$name);
2815                         }
2816                         $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2817                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2818                         if ($this->page > 0) {
2819                                 $this->_out($this->TextColor);
2820                         }
2821                 }
2822
2823                 /**
2824                 * Returns the length of a string in user unit. A font must be selected.<br>
2825                 * @param string $s The string whose length is to be computed
2826                 * @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.
2827                 * @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.
2828                 * @param float $fontsize Font size in points. The default value is the current size.
2829                 * @return int string length
2830                 * @author Nicola Asuni
2831                 * @access public
2832                 * @since 1.2
2833                 */
2834                 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2835                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
2836                 }
2837                 
2838                 /**
2839                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2840                 * @param string $arr The array of chars whose total length is to be computed
2841                 * @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.
2842                 * @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.
2843                 * @param float $fontsize Font size in points. The default value is the current size.
2844                 * @return int string length
2845                 * @author Nicola Asuni
2846                 * @access public
2847                 * @since 2.4.000 (2008-03-06)
2848                 */
2849                 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2850                         // store current values
2851                         if (!$this->empty_string($fontname)) {
2852                                 $prev_FontFamily = $this->FontFamily;
2853                                 $prev_FontStyle = $this->FontStyle;
2854                                 $prev_FontSizePt = $this->FontSizePt;
2855                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2856                         }
2857                         $w = 0;
2858                         foreach ($sa as $char) {
2859                                 $w += $this->GetCharWidth($char);
2860                         }
2861                         // restore previous values
2862                         if (!$this->empty_string($fontname)) {
2863                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2864                         }
2865                         return $w;
2866                 }
2867                 
2868                 /**
2869                 * Returns the length of the char in user unit for the current font.<br>
2870                 * @param int $char The char code whose length is to be returned
2871                 * @return int char width
2872                 * @author Nicola Asuni
2873                 * @access public
2874                 * @since 2.4.000 (2008-03-06)
2875                 */
2876                 public function GetCharWidth($char) {
2877                         if ($char == 173) {
2878                                 // SHY character will not be printed
2879                                 return (0);
2880                         }
2881                         $cw = &$this->CurrentFont['cw'];
2882                         if (isset($cw[$char])) {
2883                                 $w = $cw[$char];
2884                         } elseif (isset($this->CurrentFont['dw'])) {
2885                                 // default width
2886                                 $w = $this->CurrentFont['dw'];
2887                         } elseif (isset($cw[32])) {
2888                                 // default width
2889                                 $dw = $cw[32];
2890                         } else {
2891                                 $w = 600;
2892                         }
2893                         return ($w * $this->FontSize / 1000);
2894                 }
2895                 
2896                 /**
2897                 * Returns the numbero of characters in a string.
2898                 * @param string $s The input string.
2899                 * @return int number of characters
2900                 * @access public
2901                 * @since 2.0.0001 (2008-01-07)
2902                 */
2903                 public function GetNumChars($s) {
2904                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
2905                                 return count($this->UTF8StringToArray($s));
2906                         } 
2907                         return strlen($s);
2908                 }
2909                         
2910                 /**
2911                 * Fill the list of available fonts ($this->fontlist).
2912                 * @access protected
2913                 * @since 4.0.013 (2008-07-28)
2914                 */
2915                 protected function getFontsList() {
2916                         $fontsdir = opendir($this->_getfontpath());
2917                         while (($file = readdir($fontsdir)) !== false) {
2918                                 if (substr($file, -4) == '.php') {
2919                                         array_push($this->fontlist, strtolower(basename($file, '.php')));
2920                                 }
2921                         }
2922                         closedir($fontsdir);
2923                 }
2924                 
2925                 /**
2926                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2927                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 
2928                 * 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.
2929                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2930                 * @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>
2931                 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
2932                 * @return array containing the font data, or false in case of error.
2933                 * @access public
2934                 * @since 1.5
2935                 * @see SetFont()
2936                 */
2937                 public function AddFont($family, $style='', $fontfile='') {
2938                         if ($this->empty_string($family)) {
2939                                 if (!$this->empty_string($this->FontFamily)) {
2940                                         $family = $this->FontFamily;
2941                                 } else {
2942                                         $this->Error('Empty font family');
2943                                 }
2944                         }
2945                         $family = strtolower($family);
2946                         if ((!$this->isunicode) AND ($family == 'arial')) {
2947                                 $family = 'helvetica';
2948                         }
2949                         if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
2950                                 $style = '';
2951                         }
2952                         $tempstyle = strtoupper($style);
2953                         $style = '';
2954                         // underline
2955                         if (strpos($tempstyle, 'U') !== false) {
2956                                 $this->underline = true;
2957                         } else {
2958                                 $this->underline = false;
2959                         }
2960                         // line through (deleted)
2961                         if (strpos($tempstyle, 'D') !== false) {
2962                                 $this->linethrough = true;
2963                         } else {
2964                                 $this->linethrough = false;
2965                         }
2966                         // bold
2967                         if (strpos($tempstyle, 'B') !== false) {
2968                                 $style .= 'B';
2969                         }
2970                         // oblique
2971                         if (strpos($tempstyle, 'I') !== false) {
2972                                 $style .= 'I';
2973                         }
2974                         $bistyle = $style;
2975                         $fontkey = $family.$style;
2976                         $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
2977                         $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
2978                         // check if the font has been already added
2979                         if ($this->getFontBuffer($fontkey) !== false) {
2980                                 return $fontdata;
2981                         }
2982                         if (isset($type)) {
2983                                 unset($type); 
2984                         }
2985                         if (isset($cw)) {
2986                                 unset($cw); 
2987                         }
2988                         // get specified font directory (if any)
2989                         $fontdir = '';
2990                         if (!$this->empty_string($fontfile)) {
2991                                 $fontdir = dirname($fontfile);
2992                                 if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
2993                                         $fontdir = '';
2994                                 } else {
2995                                         $fontdir .= '/';
2996                                 }
2997                         }
2998                         // search and include font file
2999                         if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
3000                                 // build a standard filenames for specified font
3001                                 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
3002                                 $fontfile2 = str_replace(' ', '', $family).'.php';
3003                                 // search files on various directories
3004                                 if (file_exists($fontdir.$fontfile1)) {
3005                                         $fontfile = $fontdir.$fontfile1;
3006                                 } elseif (file_exists($this->_getfontpath().$fontfile1)) {
3007                                         $fontfile = $this->_getfontpath().$fontfile1;
3008                                 } elseif (file_exists($fontfile1)) {
3009                                         $fontfile = $fontfile1;
3010                                 } elseif (file_exists($fontdir.$fontfile2)) {
3011                                         $fontfile = $fontdir.$fontfile2;
3012                                 } elseif (file_exists($this->_getfontpath().$fontfile2)) {
3013                                         $fontfile = $this->_getfontpath().$fontfile2;
3014                                 } else {
3015                                         $fontfile = $fontfile2;
3016                                 }
3017                         }
3018                         // include font file
3019                         if (file_exists($fontfile)) {
3020                                 include($fontfile);
3021                         } else {
3022                                 $this->Error('Could not include font definition file: '.$family.'');
3023                         }
3024                         // check font parameters
3025                         if ((!isset($type)) OR (!isset($cw))) {
3026                                 $this->Error('The font definition file has a bad format: '.$fontfile.'');
3027                         }
3028                         if (!isset($file)) {
3029                                 $file = '';
3030                         }
3031                         if (!isset($enc)) {
3032                                 $enc = '';
3033                         }
3034                         if (!isset($dw) OR $this->empty_string($dw)) {
3035                                 // set default width
3036                                 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
3037                                         $dw = $desc['MissingWidth'];
3038                                 } elseif (isset($cw[32])) {
3039                                         $dw = $cw[32];
3040                                 } else {
3041                                         $dw = 600;
3042                                 }
3043                         }
3044                         ++$this->numfonts;                      
3045                         // register CID font (all styles at once)
3046                         if ($type == 'cidfont0') {
3047                                 $file = ''; // not embedded
3048                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
3049                                 $sname = $name.$styles[$bistyle];
3050                                 if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
3051                                         $desc['StemV'] = 120;
3052                                 }
3053                                 $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));
3054                         } elseif ($type == 'core') {
3055                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw));
3056                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
3057                                 $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));
3058                         } elseif ($type == 'TrueTypeUnicode') {
3059                                 $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));
3060                         } else {
3061                                 $this->Error('Unknow font type: '.$type.'');
3062                         }
3063                         if (isset($diff) AND (!empty($diff))) {
3064                                 //Search existing encodings
3065                                 $d = 0;
3066                                 $nb = count($this->diffs);
3067                                 for ($i=1; $i <= $nb; ++$i) {
3068                                         if ($this->diffs[$i] == $diff) {
3069                                                 $d = $i;
3070                                                 break;
3071                                         }
3072                                 }
3073                                 if ($d == 0) {
3074                                         $d = $nb + 1;
3075                                         $this->diffs[$d] = $diff;
3076                                 }
3077                                 $this->setFontSubBuffer($fontkey, 'diff', $d);
3078                         }
3079                         if (!$this->empty_string($file)) {
3080                                 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
3081                                         $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
3082                                 } elseif ($type != 'core') {
3083                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
3084                                 }
3085                         }
3086                         return $fontdata;
3087                 }
3088
3089                 /**
3090                 * Sets the font used to print character strings. 
3091                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
3092                 * The method can be called before the first page is created and the font is retained from page to page. 
3093                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
3094                 * 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 />
3095                 * @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.
3096                 * @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.
3097                 * @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
3098                 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
3099                 * @access public
3100                 * @since 1.0
3101                 * @see AddFont(), SetFontSize()
3102                 */
3103                 public function SetFont($family, $style='', $size=0, $fontfile='') {
3104                         //Select a font; size given in points
3105                         if ($size == 0) {
3106                                 $size = $this->FontSizePt;
3107                         }
3108                         // try to add font (if not already added)
3109                         $fontdata = $this->AddFont($family, $style, $fontfile);
3110                         $this->FontFamily = $fontdata['family'];
3111                         $this->FontStyle = $fontdata['style'];
3112                         $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
3113                         $this->SetFontSize($size);
3114                 }
3115
3116                 /**
3117                 * Defines the size of the current font.
3118                 * @param float $size The size (in points)
3119                 * @access public
3120                 * @since 1.0
3121                 * @see SetFont()
3122                 */
3123                 public function SetFontSize($size) {
3124                         //Set font size in points
3125                         $this->FontSizePt = $size;
3126                         $this->FontSize = $size / $this->k;
3127                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
3128                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
3129                         } else {
3130                                 $this->FontAscent = 0.8 * $this->FontSize;
3131                         }
3132                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
3133                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
3134                         } else {
3135                                 $this->FontDescent = 0.2 * $this->FontSize;
3136                         }
3137                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
3138                                 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
3139                         }
3140                 }
3141
3142                 /**
3143                 * Defines the default monospaced font.
3144                 * @param string $font Font name.
3145                 * @access public
3146                 * @since 4.5.025
3147                 */
3148                 public function SetDefaultMonospacedFont($font) {
3149                         $this->default_monospaced_font = $font;
3150                 }
3151                 
3152                 /**
3153                 * 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 />
3154                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
3155                 * @access public
3156                 * @since 1.5
3157                 * @see Cell(), Write(), Image(), Link(), SetLink()
3158                 */
3159                 public function AddLink() {
3160                         //Create a new internal link
3161                         $n = count($this->links) + 1;
3162                         $this->links[$n] = array(0, 0);
3163                         return $n;
3164                 }
3165
3166                 /**
3167                 * Defines the page and position a link points to.
3168                 * @param int $link The link identifier returned by AddLink()
3169                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
3170                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
3171                 * @access public
3172                 * @since 1.5
3173                 * @see AddLink()
3174                 */
3175                 public function SetLink($link, $y=0, $page=-1) {
3176                         if ($y == -1) {
3177                                 $y = $this->y;
3178                         }
3179                         if ($page == -1) {
3180                                 $page = $this->page;
3181                         }
3182                         $this->links[$link] = array($page, $y);
3183                 }
3184
3185                 /**
3186                 * Puts a link on a rectangular area of the page.
3187                 * 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.
3188                 * @param float $x Abscissa of the upper-left corner of the rectangle
3189                 * @param float $y Ordinate of the upper-left corner of the rectangle
3190                 * @param float $w Width of the rectangle
3191                 * @param float $h Height of the rectangle
3192                 * @param mixed $link URL or identifier returned by AddLink()
3193                 * @param int $spaces number of spaces on the text to link
3194                 * @access public
3195                 * @since 1.5
3196                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
3197                 */
3198                 public function Link($x, $y, $w, $h, $link, $spaces=0) {
3199                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
3200                 }
3201                 
3202                 /**
3203                 * Puts a markup annotation on a rectangular area of the page.
3204                 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
3205                 * @param float $x Abscissa of the upper-left corner of the rectangle
3206                 * @param float $y Ordinate of the upper-left corner of the rectangle
3207                 * @param float $w Width of the rectangle
3208                 * @param float $h Height of the rectangle
3209                 * @param string $text annotation text or alternate content
3210                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
3211                 * @param int $spaces number of spaces on the text to link
3212                 * @access public
3213                 * @since 4.0.018 (2008-08-06)
3214                 */
3215                 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
3216                         // recalculate coordinates to account for graphic transformations
3217                         if (isset($this->transfmatrix)) {
3218                                 $maxid = count($this->transfmatrix) - 1;
3219                                 for ($i=$maxid; $i >= 0; $i--) {
3220                                         $ctm = $this->transfmatrix[$i];
3221                                         if (isset($ctm['a'])) {
3222                                                 $x = $x * $this->k;
3223                                                 $y = ($this->h - $y) * $this->k;
3224                                                 $w = $w * $this->k;
3225                                                 $h = $h * $this->k;
3226                                                 // top left
3227                                                 $xt = $x;
3228                                                 $yt = $y;
3229                                                 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3230                                                 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3231                                                 // top right
3232                                                 $xt = $x + $w;
3233                                                 $yt = $y;
3234                                                 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3235                                                 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3236                                                 // bottom left
3237                                                 $xt = $x;
3238                                                 $yt = $y - $h;
3239                                                 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3240                                                 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3241                                                 // bottom right
3242                                                 $xt = $x + $w;
3243                                                 $yt = $y - $h;
3244                                                 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3245                                                 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3246                                                 // new coordinates (rectangle area)
3247                                                 $x = min($x1, $x2, $x3, $x4);
3248                                                 $y = max($y1, $y2, $y3, $y4);
3249                                                 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
3250                                                 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
3251                                                 $x = $x / $this->k;
3252                                                 $y = $this->h - ($y / $this->k);
3253                                         }
3254                                 }
3255                         }
3256                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
3257                         if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
3258                                 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + 100000));
3259                         }
3260                 }
3261
3262                 /**
3263                 * Embedd the attached files.
3264                 * @since 4.4.000 (2008-12-07)
3265                 * @access protected
3266                 * @see Annotation()
3267                 */
3268                 protected function _putEmbeddedFiles() {
3269                         reset($this->embeddedfiles);
3270                         foreach ($this->embeddedfiles as $filename => $filedata) {
3271                                 $data = file_get_contents($filedata['file']);
3272                                 $filter = '';
3273                                 if ($this->compress) {
3274                                         $data = gzcompress($data);
3275                                         $filter = ' /Filter /FlateDecode';
3276                                 }
3277                                 $this->offsets[$filedata['n']] = $this->bufferlen;
3278                                 $this->_out($filedata['n'].' 0 obj');
3279                                 $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
3280                                 $this->_putstream($data);
3281                                 $this->_out('endobj');
3282                         }
3283                 }
3284                 
3285                 /**
3286                 * Prints a character string.
3287                 * The origin is on the left of the first charcter, on the baseline.
3288                 * This method allows to place a string precisely on the page.
3289                 * @param float $x Abscissa of the origin
3290                 * @param float $y Ordinate of the origin
3291                 * @param string $txt String to print
3292                 * @param int $stroke outline size in points (0 = disable)
3293                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
3294                 * @access public
3295                 * @since 1.0
3296                 * @deprecated deprecated since version 4.3.005 (2008-11-25)
3297                 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
3298                 */
3299                 public function Text($x, $y, $txt, $stroke=0, $clip=false) {
3300                         //Output a string
3301                         if ($this->rtl) {
3302                                 // bidirectional algorithm (some chars may be changed affecting the line length)
3303                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
3304                                 $l = $this->GetArrStringWidth($s);
3305                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
3306                         } else {
3307                                 $xr = $x;
3308                         }
3309                         $opt = '';
3310                         if (($stroke > 0) AND (!$clip)) {
3311                                 $opt .= '1 Tr '.intval($stroke).' w ';
3312                         } elseif (($stroke > 0) AND $clip) {
3313                                 $opt .= '5 Tr '.intval($stroke).' w ';
3314                         } elseif ($clip) {
3315                                 $opt .= '7 Tr ';
3316                         }
3317                         $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
3318                         if ($this->underline AND ($txt!='')) {
3319                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
3320                         }
3321                         if ($this->linethrough AND ($txt!='')) { 
3322                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 
3323                         }
3324                         if ($this->ColorFlag AND (!$clip)) {
3325                                 $s='q '.$this->TextColor.' '.$s.' Q';
3326                         }
3327                         $this->_out($s);
3328                 }
3329
3330                 /**
3331                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 
3332                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
3333                 * This method is called automatically and should not be called directly by the application.
3334                 * @return boolean
3335                 * @access public
3336                 * @since 1.4
3337                 * @see SetAutoPageBreak()
3338                 */
3339                 public function AcceptPageBreak() {
3340                         return $this->AutoPageBreak;
3341                 }
3342                 
3343                 /**
3344                 * Add page if needed.
3345                 * @param float $h Cell height. Default value: 0.
3346                 * @param mixed $y starting y position, leave empty for current position.
3347                 * @return boolean true in case of page break, false otherwise.
3348                 * @since 3.2.000 (2008-07-01)
3349                 * @access protected
3350                 */
3351                 protected function checkPageBreak($h=0, $y='') {
3352                         if ($this->empty_string($y)) {
3353                                 $y = $this->y;
3354                         }
3355                         if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
3356                                 //Automatic page break
3357                                 $x = $this->x;
3358                                 $this->AddPage($this->CurOrientation);
3359                                 $this->y = $this->tMargin;
3360                                 $oldpage = $this->page - 1;
3361                                 if ($this->rtl) {
3362                                         if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
3363                                                 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
3364                                         } else {
3365                                                 $this->x = $x;
3366                                         }
3367                                 } else {
3368                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3369                                                 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
3370                                         } else {
3371                                                 $this->x = $x;
3372                                         }
3373                                 }
3374                                 return true;
3375                         }
3376                         return false;
3377                 }
3378
3379                 /**
3380                 * 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 />
3381                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3382                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
3383                 * @param float $h Cell height. Default value: 0.
3384                 * @param string $txt String to print. Default value: empty string.
3385                 * @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>
3386                 * @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>
3387                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
3388                 * @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>
3389                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3390                 * @param mixed $link URL or identifier returned by AddLink().
3391                 * @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>
3392                 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3393                 * @access public
3394                 * @since 1.0
3395                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
3396                 */
3397                 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3398                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
3399                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3400                         if ($h < $min_cell_height) {
3401                                 $h = $min_cell_height;
3402                         }
3403                         $this->checkPageBreak($h);
3404                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
3405                 }
3406
3407                 /**
3408                 * Removes SHY characters from text.
3409                 * @param string $txt input string
3410                 * @return string without SHY characters.
3411                 * @access public
3412                 * @since (4.5.019) 2009-02-28
3413                 */
3414                 public function removeSHY($txt='') {
3415                         /*
3416                         * Unicode Data
3417                         * Name : SOFT HYPHEN, commonly abbreviated as SHY
3418                         * HTML Entity (decimal): &#173;
3419                         * HTML Entity (hex): &#xad;
3420                         * HTML Entity (named): &shy;
3421                         * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
3422                         * UTF-8 (hex): 0xC2 0xAD (c2ad)
3423                         * UTF-8 character: chr(194).chr(173)
3424                         */
3425                         $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
3426                         if (!$this->isunicode) {
3427                                 $txt = preg_replace('/([\\xad]{1})/', '', $txt);
3428                         }
3429                         return $txt;
3430                 }
3431                 
3432                 /**
3433                 * 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 />
3434                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3435                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
3436                 * @param float $h Cell height. Default value: 0.
3437                 * @param string $txt String to print. Default value: empty string.
3438                 * @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>
3439                 * @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.
3440                 * @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>
3441                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3442                 * @param mixed $link URL or identifier returned by AddLink().
3443                 * @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>
3444                 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3445                 * @access protected
3446                 * @since 1.0
3447                 * @see Cell()
3448                 */
3449                 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3450                         $txt = $this->removeSHY($txt);
3451                         $rs = ''; //string to be returned
3452                         if (!$ignore_min_height) {
3453                                 $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3454                                 if ($h < $min_cell_height) {
3455                                         $h = $min_cell_height;
3456                                 }
3457                         }
3458                         $k = $this->k;
3459                         if ($this->empty_string($w) OR ($w <= 0)) {
3460                                 if ($this->rtl) {
3461                                         $w = $this->x - $this->lMargin;
3462                                 } else {
3463                                         $w = $this->w - $this->rMargin - $this->x;
3464                                 }
3465                         }
3466                         $s = '';                        
3467                         if (($fill == 1) OR ($border == 1)) {
3468                                 if ($fill == 1) {
3469                                         $op = ($border == 1) ? 'B' : 'f';
3470                                 } else {
3471                                         $op = 'S';
3472                                 }
3473                                 if ($this->rtl) {
3474                                         $xk = (($this->x  - $w) * $k);
3475                                 } else {
3476                                         $xk = ($this->x * $k);
3477                                 }
3478                                 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
3479                         }
3480                         if (is_string($border)) {
3481                                 $lm = ($this->LineWidth / 2);
3482                                 $x = $this->x;
3483                                 $y = $this->y;
3484                                 if (strpos($border,'L') !== false) {
3485                                         if ($this->rtl) {
3486                                                 $xk = ($x - $w) * $k;
3487                                         } else {
3488                                                 $xk = $x * $k;
3489                                         }
3490                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
3491                                 }
3492                                 if (strpos($border,'T') !== false) {
3493                                         if ($this->rtl) {
3494                                                 $xk = ($x - $w + $lm) * $k;
3495                                                 $xwk = ($x - $lm) * $k;
3496                                         } else {
3497                                                 $xk = ($x - $lm) * $k;
3498                                                 $xwk = ($x + $w + $lm) * $k;
3499                                         }
3500                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
3501                                 }
3502                                 if (strpos($border,'R') !== false) {
3503                                         if ($this->rtl) {
3504                                                 $xk = $x * $k;
3505                                         } else {
3506                                                 $xk = ($x + $w) * $k;
3507                                         }
3508                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
3509                                 }
3510                                 if (strpos($border,'B') !== false) {
3511                                         if ($this->rtl) {
3512                                                 $xk = ($x - $w + $lm) * $k;
3513                                                 $xwk = ($x - $lm) * $k;
3514                                         } else {
3515                                                 $xk = ($x - $lm) * $k;
3516                                                 $xwk = ($x + $w + $lm) * $k;
3517                                         }
3518                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
3519                                 }
3520                         }
3521                         if ($txt != '') {
3522                                 // text lenght
3523                                 $width = $this->GetStringWidth($txt);
3524                                 // ratio between cell lenght and text lenght
3525                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
3526                                 
3527                                 // stretch text if required
3528                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
3529                                         if ($stretch > 2) {
3530                                                 // spacing
3531                                                 //Calculate character spacing in points
3532                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
3533                                                 //Set character spacing
3534                                                 $rs .= sprintf('BT %.2F Tc ET ', $char_space);
3535                                         } else {
3536                                                 // scaling
3537                                                 //Calculate horizontal scaling
3538                                                 $horiz_scale = $ratio * 100.0;
3539                                                 //Set horizontal scaling
3540                                                 $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
3541                                         }
3542                                         $align = '';
3543                                         $width = $w - (2 * $this->cMargin);
3544                                 } else {
3545                                         $stretch == 0;
3546                                 }
3547                                 if ($align == 'L') {
3548                                         if ($this->rtl) {
3549                                                 $dx = $w - $width - $this->cMargin;
3550                                         } else {
3551                                                 $dx = $this->cMargin;
3552                                         }
3553                                 } elseif ($align == 'R') {
3554                                         if ($this->rtl) {
3555                                                 $dx = $this->cMargin;
3556                                         } else {
3557                                                 $dx = $w - $width - $this->cMargin;
3558                                         }
3559                                 } elseif ($align == 'C') {
3560                                         $dx = ($w - $width) / 2;
3561                                 } elseif ($align == 'J') {
3562                                         if ($this->rtl) {
3563                                                 $dx = $w - $width - $this->cMargin;
3564                                         } else {
3565                                                 $dx = $this->cMargin;
3566                                         }
3567                                 } else {
3568                                         $dx = $this->cMargin;
3569                                 }
3570                                 if ($this->ColorFlag) {
3571                                         $s .= 'q '.$this->TextColor.' ';
3572                                 }
3573                                 $txt2 = $this->_escapetext($txt);
3574                                 if ($this->rtl) {
3575                                         $xdk = ($this->x - $dx - $width) * $k;
3576                                 } else {
3577                                         $xdk = ($this->x + $dx) * $k;
3578                                 }
3579                                 // Justification
3580                                 if ($align == 'J') {
3581                                         // count number of spaces
3582                                         $ns = substr_count($txt, ' ');
3583                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
3584                                                 // get string width without spaces
3585                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3586                                                 // calculate average space width
3587                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3588                                                 // set word position to be used with TJ operator
3589                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3590                                         } else {
3591                                                 // get string width
3592                                                 $width = $this->GetStringWidth($txt);
3593                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3594                                                 $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
3595                                         }
3596                                 }
3597                                 // calculate approximate position of the font base line
3598                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3599                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3600                                 // print text
3601                                 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3602                                 if ($this->rtl) {
3603                                         $xdx = $this->x - $dx - $width;
3604                                 } else {
3605                                         $xdx = $this->x + $dx;
3606                                 }
3607                                 if ($this->underline)  {
3608                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3609                                 }
3610                                 if ($this->linethrough) { 
3611                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3612                                 }
3613                                 if ($this->ColorFlag) {
3614                                         $s .= ' Q';
3615                                 }
3616                                 if ($link) {
3617                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
3618                                 }
3619                         }
3620                         // output cell
3621                         if ($s) {
3622                                 // output cell
3623                                 $rs .= $s;
3624                                 // reset text stretching
3625                                 if ($stretch > 2) {
3626                                         //Reset character horizontal spacing
3627                                         $rs .= ' BT 0 Tc ET';
3628                                 } elseif ($stretch > 0) {
3629                                         //Reset character horizontal scaling
3630                                         $rs .= ' BT 100 Tz ET';
3631                                 }
3632                         }
3633                         // reset word spacing
3634                         if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
3635                                 $rs .= ' BT 0 Tw ET';
3636                         }
3637                         $this->lasth = $h;
3638                         if ($ln > 0) {
3639                                 //Go to the beginning of the next line
3640                                 $this->y += $h;
3641                                 if ($ln == 1) {
3642                                         if ($this->rtl) {
3643                                                 $this->x = $this->w - $this->rMargin;
3644                                         } else {
3645                                                 $this->x = $this->lMargin;
3646                                         }
3647                                 }
3648                         } else {
3649                                 // go left or right by case
3650                                 if ($this->rtl) {
3651                                         $this->x -= $w;
3652                                 } else {
3653                                         $this->x += $w;
3654                                 }
3655                         }
3656                         $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
3657                         $rs = $gstyles.$rs;
3658                         return $rs;
3659                 }
3660
3661                 /**
3662                 * This method allows printing text with line breaks. 
3663                 * 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 />
3664                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3665                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3666                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3667                 * @param string $txt String to print
3668                 * @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>
3669                 * @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>
3670                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3671                 * @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>
3672                 * @param int $x x position in user units
3673                 * @param int $y y position in user units
3674                 * @param boolean $reseth if true reset the last cell height (default true).
3675                 * @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>
3676                 * @param boolean $ishtml set to true if $txt is HTML content (default = false).
3677                 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
3678                 * @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.
3679                 * @return int Return the number of cells or 1 for html mode.
3680                 * @access public
3681                 * @since 1.3
3682                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3683                 */
3684                 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) {     
3685                         if ($this->empty_string($this->lasth) OR $reseth) {
3686                                 //set row height
3687                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3688                         }
3689                         if (!$this->empty_string($y)) {
3690                                 $this->SetY($y);
3691                         } else {
3692                                 $y = $this->GetY();
3693                         }
3694                         // check for page break
3695                         $this->checkPageBreak($h);
3696                         $y = $this->GetY();
3697                         // get current page number
3698                         $startpage = $this->page;
3699                         if (!$this->empty_string($x)) {
3700                                 $this->SetX($x);
3701                         } else {
3702                                 $x = $this->GetX();
3703                         }
3704                         if ($this->empty_string($w) OR ($w <= 0)) {
3705                                 if ($this->rtl) {
3706                                         $w = $this->x - $this->lMargin;
3707                                 } else {
3708                                         $w = $this->w - $this->rMargin - $this->x;
3709                                 }
3710                         }
3711                         // store original margin values
3712                         $lMargin = $this->lMargin;
3713                         $rMargin = $this->rMargin;
3714                         if ($this->rtl) {
3715                                 $this->SetRightMargin($this->w - $this->x);
3716                                 $this->SetLeftMargin($this->x - $w);
3717                         } else {
3718                                 $this->SetLeftMargin($this->x);
3719                                 $this->SetRightMargin($this->w - $this->x - $w);
3720                         }
3721                         $starty = $this->y;
3722                         if ($autopadding) {
3723                                 // Adjust internal padding
3724                                 if ($this->cMargin < ($this->LineWidth / 2)) {
3725                                         $this->cMargin = ($this->LineWidth / 2);
3726                                 }
3727                                 // Add top space if needed
3728                                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3729                                         $this->y += $this->LineWidth / 2;
3730                                 }
3731                                 // add top padding
3732                                 $this->y += $this->cMargin;
3733                         }
3734                         if ($ishtml) {
3735                                 // ******* Write HTML text
3736                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3737                                 $nl = 1;
3738                         } else {
3739                                 // ******* Write text
3740                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
3741                         }
3742                         if ($autopadding) {
3743                                 // add bottom padding
3744                                 $this->y += $this->cMargin;
3745                                 // Add bottom space if needed
3746                                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3747                                         $this->y += $this->LineWidth / 2;
3748                                 }
3749                         }
3750                         // Get end-of-text Y position
3751                         $currentY = $this->y;
3752                         // get latest page number
3753                         $endpage = $this->page;
3754                         // check if a new page has been created
3755                         if ($endpage > $startpage) {
3756                                 // design borders around HTML cells.
3757                                 for ($page=$startpage; $page <= $endpage; ++$page) {
3758                                         $this->setPage($page);
3759                                         if ($page == $startpage) {
3760                                                 $this->y = $starty; // put cursor at the beginning of cell on the first page
3761                                                 $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
3762                                                 $cborder = $this->getBorderMode($border, $position='start');
3763                                         } elseif ($page == $endpage) {
3764                                                 $this->y = $this->tMargin; // put cursor at the beginning of last page
3765                                                 $h = $currentY - $this->tMargin;
3766                                                 $cborder = $this->getBorderMode($border, $position='end');
3767                                         } else {
3768                                                 $this->y = $this->tMargin; // put cursor at the beginning of the current page
3769                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3770                                                 $cborder = $this->getBorderMode($border, $position='middle');
3771                                         }
3772                                         $nx = $x;
3773                                         // account for margin changes
3774                                         if ($page > $startpage) {
3775                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
3776                                                         $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
3777                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
3778                                                         $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
3779                                                 }
3780                                         }
3781                                         $this->SetX($nx);
3782                                         $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
3783                                         if ($cborder OR $fill) {
3784                                                 $pagebuff = $this->getPageBuffer($this->page);
3785                                                 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
3786                                                 $pend = substr($pagebuff, $this->intmrk[$this->page]);
3787                                                 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3788                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3789                                         }
3790                                 }
3791                         } else {
3792                                 $h = max($h, ($currentY - $y));
3793                                 // put cursor at the beginning of text
3794                                 $this->SetY($y); 
3795                                 $this->SetX($x);
3796                                 // design a cell around the text
3797                                 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
3798                                 if ($border OR $fill) {
3799                                         if (end($this->transfmrk[$this->page]) !== false) {
3800                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
3801                                                 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
3802                                         } elseif ($this->InFooter) {
3803                                                 $pagemark = &$this->footerpos[$this->page];
3804                                         } else {
3805                                                 $pagemark = &$this->intmrk[$this->page];
3806                                         }
3807                                         $pagebuff = $this->getPageBuffer($this->page);
3808                                         $pstart = substr($pagebuff, 0, $pagemark);
3809                                         $pend = substr($pagebuff, $pagemark);
3810                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3811                                         $pagemark += strlen($ccode."\n");
3812                                 }
3813                         }
3814                         // Get end-of-cell Y position
3815                         $currentY = $this->GetY();
3816                         // restore original margin values
3817                         $this->SetLeftMargin($lMargin);
3818                         $this->SetRightMargin($rMargin);
3819                         if ($ln > 0) {
3820                                 //Go to the beginning of the next line
3821                                 $this->SetY($currentY);
3822                                 if ($ln == 2) {
3823                                         $this->SetX($x + $w);
3824                                 }
3825                         } else {
3826                                 // go left or right by case
3827                                 $this->setPage($startpage);
3828                                 $this->y = $y;
3829                                 $this->SetX($x + $w);
3830                         }
3831                         return $nl;
3832                 }
3833
3834                 /**
3835                 * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
3836                 * @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>
3837                 * @param string multicell position: 'start', 'middle', 'end'
3838                 * @return border mode
3839                 * @access protected
3840                 * @since 4.4.002 (2008-12-09)
3841                 */
3842                 protected function getBorderMode($border, $position='start') {
3843                         if ((!$this->opencell) AND ($border == 1)) {
3844                                 return 1;
3845                         }
3846                         $cborder = '';
3847                         switch ($position) {
3848                                 case 'start': {
3849                                         if ($border == 1) {
3850                                                 $cborder = 'LTR';
3851                                         } else {
3852                                                 if (!(false === strpos($border, 'L'))) {
3853                                                         $cborder .= 'L';
3854                                                 }
3855                                                 if (!(false === strpos($border, 'T'))) {
3856                                                         $cborder .= 'T';
3857                                                 }
3858                                                 if (!(false === strpos($border, 'R'))) {
3859                                                         $cborder .= 'R';
3860                                                 }
3861                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3862                                                         $cborder .= 'B';
3863                                                 }
3864                                         }
3865                                         break;
3866                                 }
3867                                 case 'middle': {
3868                                         if ($border == 1) {
3869                                                 $cborder = 'LR';
3870                                         } else {
3871                                                 if (!(false === strpos($border, 'L'))) {
3872                                                         $cborder .= 'L';
3873                                                 }
3874                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3875                                                         $cborder .= 'T';
3876                                                 }
3877                                                 if (!(false === strpos($border, 'R'))) {
3878                                                         $cborder .= 'R';
3879                                                 }
3880                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3881                                                         $cborder .= 'B';
3882                                                 }
3883                                         }
3884                                         break;
3885                                 }
3886                                 case 'end': {
3887                                         if ($border == 1) {
3888                                                 $cborder = 'LRB';
3889                                         } else {
3890                                                 if (!(false === strpos($border, 'L'))) {
3891                                                         $cborder .= 'L';
3892                                                 }
3893                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3894                                                         $cborder .= 'T';
3895                                                 }
3896                                                 if (!(false === strpos($border, 'R'))) {
3897                                                         $cborder .= 'R';
3898                                                 }
3899                                                 if (!(false === strpos($border, 'B'))) {
3900                                                         $cborder .= 'B';
3901                                                 }
3902                                         }
3903                                         break;
3904                                 }
3905                                 default: {
3906                                         $cborder = $border;
3907                                         break;
3908                                 }
3909                         }
3910                         return $cborder;
3911                 }
3912
3913                 /**
3914                 * This method returns the estimated number of lines required to print the text.
3915                 * @param string $txt text to print
3916                 * @param float $w width of cell. If 0, they extend up to the right margin of the page.
3917                 * @return int Return the estimated number of lines.
3918                 * @access public
3919                 * @since 4.5.011
3920                 */
3921                 public function getNumLines($txt, $w=0) {
3922                         $lines = 0;
3923                         if ($this->empty_string($w) OR ($w <= 0)) {
3924                                 if ($this->rtl) {
3925                                         $w = $this->x - $this->lMargin;
3926                                 } else {
3927                                         $w = $this->w - $this->rMargin - $this->x;
3928                                 }
3929                         }
3930                         // max column width
3931                         $wmax = $w - (2 * $this->cMargin);
3932                         // remove carriage returns
3933                         $txt = str_replace("\r", '', $txt);
3934                         // remove last newline (if any)
3935                         if (substr($txt,-1) == "\n") {
3936                                 $txt = substr($txt, 0, -1);
3937                         }
3938                         // divide text in blocks
3939                         $txtblocks = explode("\n", $txt);
3940                         // for each block;
3941                         foreach ($txtblocks as $block) {
3942                                 // estimate the number of lines
3943                                 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
3944                         }
3945                         return $lines;
3946                 }
3947                         
3948                 /**
3949                 * This method prints text from the current position.<br />
3950                 * @param float $h Line height
3951                 * @param string $txt String to print
3952                 * @param mixed $link URL or identifier returned by AddLink()
3953                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3954                 * @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>
3955                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3956                 * @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>
3957                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3958                 * @param boolean $firstblock if true the string is the starting of a line.
3959                 * @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.
3960                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3961                 * @access public
3962                 * @since 1.5
3963                 */
3964                 public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
3965                         if (strlen($txt) == 0) {
3966                                 $txt = ' ';
3967                         }
3968                         // remove carriage returns
3969                         $s = str_replace("\r", '', $txt);
3970                         // check if string contains arabic text
3971                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3972                                 $arabic = true;
3973                         } else {
3974                                 $arabic = false;
3975                         }
3976                         // check if string contains RTL text
3977                         if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
3978                                 $rtlmode = true;
3979                         } else {
3980                                 $rtlmode = false;
3981                         }
3982                         // get a char width
3983                         $chrwidth = $this->GetCharWidth('.');
3984                         // get array of unicode values
3985                         $chars = $this->UTF8StringToArray($s);
3986                         // get array of chars
3987                         $uchars = $this->UTF8ArrayToUniArray($chars);
3988                         // get the number of characters
3989                         $nb = count($chars);
3990                         // replacement for SHY character (minus symbol)
3991                         $shy_replacement = 45;
3992                         $shy_replacement_char = $this->unichr($shy_replacement);
3993                         // widht for SHY replacement
3994                         $shy_replacement_width = $this->GetCharWidth($shy_replacement);
3995                         // store current position
3996                         $prevx = $this->x;
3997                         $prevy = $this->y;
3998                         // max Y
3999                         $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
4000                         // calculate remaining line width ($w)
4001                         if ($this->rtl) {
4002                                 $w = $this->x - $this->lMargin;
4003                         } else {
4004                                 $w = $this->w - $this->rMargin - $this->x;
4005                         }
4006                         // max column width
4007                         $wmax = $w - (2 * $this->cMargin);
4008                         if ($chrwidth > $wmax) {
4009                                 // a single character do not fit on column
4010                                 return '';
4011                         }
4012                         $i = 0; // character position
4013                         $j = 0; // current starting position
4014                         $sep = -1; // position of the last blank space
4015                         $shy = false; // true if the last blank is a soft hypen (SHY)
4016                         $l = 0; // current string lenght
4017                         $nl = 0; //number of lines
4018                         $linebreak = false;
4019                         // for each character
4020                         while ($i < $nb) {
4021                                 if (($maxh > 0) AND ($this->y >= $maxy) ) {
4022                                         $firstline = true;
4023                                 }
4024                                 //Get the current character
4025                                 $c = $chars[$i];
4026                                 if ($c == 10) { // 10 = "\n" = new line
4027                                         //Explicit line break
4028                                         if ($align == 'J') {
4029                                                 if ($this->rtl) {
4030                                                         $talign = 'R';
4031                                                 } else {
4032                                                         $talign = 'L';
4033                                                 }
4034                                         } else {
4035                                                 $talign = $align;
4036                                         }
4037                                         $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4038                                         if ($firstline) {
4039                                                 $startx = $this->x;
4040                                                 $tmparr = array_slice($chars, $j, $i);
4041                                                 if ($rtlmode) {
4042                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4043                                                 }
4044                                                 $linew = $this->GetArrStringWidth($tmparr);
4045                                                 unset($tmparr);
4046                                                 if ($this->rtl) {
4047                                                         $this->endlinex = $startx - $linew;
4048                                                 } else {
4049                                                         $this->endlinex = $startx + $linew;
4050                                                 }
4051                                                 $w = $linew;
4052                                                 $tmpcmargin = $this->cMargin;
4053                                                 if ($maxh == 0) {
4054                                                         $this->cMargin = 0;
4055                                                 }
4056                                         }
4057                                         $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
4058                                         unset($tmpstr);
4059                                         if ($firstline) {
4060                                                 $this->cMargin = $tmpcmargin;
4061                                                 return ($this->UniArrSubString($uchars, $i));
4062                                         }
4063                                         ++$nl;
4064                                         $j = $i + 1;
4065                                         $l = 0;
4066                                         $sep = -1;
4067                                         $shy = false;
4068                                         // account for margin changes
4069                                         if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4070                                                 // AcceptPageBreak() may be overriden on extended classed to include margin changes
4071                                                 $this->AcceptPageBreak();
4072                                         }
4073                                         $w = $this->getRemainingWidth();
4074                                         $wmax = $w - (2 * $this->cMargin);
4075                                 } else {
4076                                         // 160 is the non-breaking space.
4077                                         // 173 is SHY (Soft Hypen).
4078                                         // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
4079                                         // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
4080                                         // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
4081                                         if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
4082                                                 // update last blank space position
4083                                                 $sep = $i;
4084                                                 // check if is a SHY
4085                                                 if ($c == 173) {
4086                                                         $shy = true;
4087                                                 } else {
4088                                                         $shy = false;
4089                                                 }
4090                                         }
4091                                         // update string length
4092                                         if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
4093                                                 // with bidirectional algorithm some chars may be changed affecting the line length
4094                                                 // *** very slow ***
4095                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
4096                                         } else {
4097                                                 $l += $this->GetCharWidth($c);
4098                                         }
4099                                         if (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax)) ) {
4100                                                 // we have reached the end of column
4101                                                 if ($sep == -1) {
4102                                                         // check if the line was already started
4103                                                         if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
4104                                                                 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
4105                                                                 // print a void cell and go to next line
4106                                                                 $this->Cell($w, $h, '', 0, 1);
4107                                                                 $linebreak = true;
4108                                                                 if ($firstline) {
4109                                                                         return ($this->UniArrSubString($uchars, $j));
4110                                                                 }
4111                                                         } else {
4112                                                                 // truncate the word because do not fit on column
4113                                                                 $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4114                                                                 if ($firstline) {
4115                                                                         $startx = $this->x;
4116                                                                         $tmparr = array_slice($chars, $j, $i);
4117                                                                         if ($rtlmode) {
4118                                                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4119                                                                         }
4120                                                                         $linew = $this->GetArrStringWidth($tmparr);
4121                                                                         unset($tmparr);
4122                                                                         if ($this->rtl) {
4123                                                                                 $this->endlinex = $startx - $linew;
4124                                                                         } else {
4125                                                                                 $this->endlinex = $startx + $linew;
4126                                                                         }
4127                                                                         $w = $linew;
4128                                                                         $tmpcmargin = $this->cMargin;
4129                                                                         if ($maxh == 0) {
4130                                                                                 $this->cMargin = 0;
4131                                                                         }
4132                                                                 }
4133                                                                 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
4134                                                                 unset($tmpstr);
4135                                                                 if ($firstline) {
4136                                                                         $this->cMargin = $tmpcmargin;
4137                                                                         return ($this->UniArrSubString($uchars, $i));
4138                                                                 }
4139                                                                 $j = $i;
4140                                                                 --$i;
4141                                                         }       
4142                                                 } else {
4143                                                         // word wrapping
4144                                                         if ($this->rtl AND (!$firstblock)) {
4145                                                                 $endspace = 1;
4146                                                         } else {
4147                                                                 $endspace = 0;
4148                                                         }
4149                                                         if ($shy) {
4150                                                                 // add hypen (minus symbol) at the end of the line
4151                                                                 $shy_width = $shy_replacement_width;
4152                                                                 if ($this->rtl) {
4153                                                                         $shy_char_left = $shy_replacement_char;
4154                                                                         $shy_char_right = '';
4155                                                                 } else {
4156                                                                         $shy_char_left = '';
4157                                                                         $shy_char_right = $shy_replacement_char;
4158                                                                 }
4159                                                         } else {
4160                                                                 $shy_width = 0;
4161                                                                 $shy_char_left = '';
4162                                                                 $shy_char_right = '';
4163                                                         }
4164                                                         $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
4165                                                         if ($firstline) {
4166                                                                 $startx = $this->x;
4167                                                                 $tmparr = array_slice($chars, $j, ($sep + $endspace));
4168                                                                 if ($rtlmode) {
4169                                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4170                                                                 }
4171                                                                 $linew = $this->GetArrStringWidth($tmparr);
4172                                                                 unset($tmparr);
4173                                                                 if ($this->rtl) {
4174                                                                         $this->endlinex = $startx - $linew - $shy_width;
4175                                                                 } else {
4176                                                                         $this->endlinex = $startx + $linew + $shy_width;
4177                                                                 }
4178                                                                 $w = $linew;
4179                                                                 $tmpcmargin = $this->cMargin;
4180                                                                 if ($maxh == 0) {
4181                                                                         $this->cMargin = 0;
4182                                                                 }
4183                                                         }
4184                                                         // print the line
4185                                                         $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
4186                                                         unset($tmpstr);
4187                                                         if ($firstline) {
4188                                                                 // return the remaining text
4189                                                                 $this->cMargin = $tmpcmargin;
4190                                                                 return ($this->UniArrSubString($uchars, ($sep + $endspace)));
4191                                                         }
4192                                                         $i = $sep;
4193                                                         $sep = -1;
4194                                                         $shy = false;
4195                                                         $j = ($i+1);
4196                                                 }
4197                                                 // account for margin changes
4198                                                 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4199                                                         // AcceptPageBreak() may be overriden on extended classed to include margin changes
4200                                                         $this->AcceptPageBreak();
4201                                                 }
4202                                                 $w = $this->getRemainingWidth();
4203                                                 $wmax = $w - (2 * $this->cMargin);
4204                                                 if ($linebreak) {
4205                                                         $linebreak = false;
4206                                                 } else {
4207                                                         ++$nl;
4208                                                         $l = 0;
4209                                                 }
4210                                         }
4211                                 }
4212                                 ++$i;
4213                         } // end while i < nb
4214                         // print last substring (if any)
4215                         if ($l > 0) {
4216                                 switch ($align) {
4217                                         case 'J':
4218                                         case 'C': {
4219                                                 $w = $w;
4220                                                 break;
4221                                         }
4222                                         case 'L': {
4223                                                 if ($this->rtl) {
4224                                                         $w = $w;
4225                                                 } else {
4226                                                         $w = $l;
4227                                                 }
4228                                                 break;
4229                                         }
4230                                         case 'R': {
4231                                                 if ($this->rtl) {
4232                                                         $w = $l;
4233                                                 } else {
4234                                                         $w = $w;
4235                                                 }
4236                                                 break;
4237                                         }
4238                                         default: {
4239                                                 $w = $l;
4240                                                 break;
4241                                         }
4242                                 }
4243                                 $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
4244                                 if ($firstline) {
4245                                         $startx = $this->x;
4246                                         $tmparr = array_slice($chars, $j, $nb);
4247                                         if ($rtlmode) {
4248                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4249                                         }
4250                                         $linew = $this->GetArrStringWidth($tmparr);
4251                                         unset($tmparr);
4252                                         if ($this->rtl) {
4253                                                 $this->endlinex = $startx - $linew;
4254                                         } else {
4255                                                 $this->endlinex = $startx + $linew;
4256                                         }
4257                                         $w = $linew;
4258                                         $tmpcmargin = $this->cMargin;
4259                                         if ($maxh == 0) {
4260                                                 $this->cMargin = 0;
4261                                         }
4262                                 }
4263                                 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
4264                                 unset($tmpstr);
4265                                 if ($firstline) {
4266                                         $this->cMargin = $tmpcmargin;
4267                                         return ($this->UniArrSubString($uchars, $nb));
4268                                 }
4269                                 ++$nl;
4270                         }
4271                         if ($firstline) {
4272                                 return '';
4273                         }
4274                         return $nl;
4275                 }
4276                                 
4277                 /**
4278                 * Returns the remaining width between the current position and margins.
4279                 * @return int Return the remaining width
4280                 * @access protected
4281                 */
4282                 protected function getRemainingWidth() {
4283                         if ($this->rtl) {
4284                                 return ($this->x - $this->lMargin);
4285                         } else {
4286                                 return ($this->w - $this->rMargin - $this->x);
4287                         }
4288                 }
4289
4290                 /**
4291                 * Extract a slice of the $strarr array and return it as string.
4292                 * @param string $strarr The input array of characters.
4293                 * @param int $start the starting element of $strarr.
4294                 * @param int $end first element that will not be returned.
4295                 * @return Return part of a string
4296                 * @access public
4297                 */
4298                 public function UTF8ArrSubString($strarr, $start='', $end='') {
4299                         if (strlen($start) == 0) {
4300                                 $start = 0;
4301                         }
4302                         if (strlen($end) == 0) {
4303                                 $end = count($strarr);
4304                         }
4305                         $string = '';
4306                         for ($i=$start; $i < $end; ++$i) {
4307                                 $string .= $this->unichr($strarr[$i]);
4308                         }
4309                         return $string;
4310                 }
4311
4312                 /**
4313                 * Extract a slice of the $uniarr array and return it as string.
4314                 * @param string $uniarr The input array of characters.
4315                 * @param int $start the starting element of $strarr.
4316                 * @param int $end first element that will not be returned.
4317                 * @return Return part of a string
4318                 * @access public
4319                 * @since 4.5.037 (2009-04-07)
4320                 */
4321                 public function UniArrSubString($uniarr, $start='', $end='') {
4322                         if (strlen($start) == 0) {
4323                                 $start = 0;
4324                         }
4325                         if (strlen($end) == 0) {
4326                                 $end = count($uniarr);
4327                         }
4328                         $string = '';
4329                         for ($i=$start; $i < $end; ++$i) {
4330                                 $string .= $uniarr[$i];
4331                         }
4332                         return $string;
4333                 }
4334
4335                 /**
4336                 * Convert an array of UTF8 values to array of unicode characters
4337                 * @param string $ta The input array of UTF8 values.
4338                 * @return Return array of unicode characters
4339                 * @access public
4340                 * @since 4.5.037 (2009-04-07)
4341                 */
4342                 public function UTF8ArrayToUniArray($ta) {
4343                         return array_map(array($this, 'unichr'), $ta);
4344                 }
4345                 
4346                 /**
4347                 * Returns the unicode caracter specified by UTF-8 code
4348                 * @param int $c UTF-8 code
4349                 * @return Returns the specified character.
4350                 * @author Miguel Perez, Nicola Asuni
4351                 * @access public
4352                 * @since 2.3.000 (2008-03-05)
4353                 */
4354                 public function unichr($c) {
4355                         if (!$this->isunicode) {
4356                                 return chr($c);
4357                         } elseif ($c <= 0x7F) {
4358                                 // one byte
4359                                 return chr($c);
4360                         } elseif ($c <= 0x7FF) {
4361                                 // two bytes
4362                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
4363                         } elseif ($c <= 0xFFFF) {
4364                                 // three bytes
4365                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4366                         } elseif ($c <= 0x10FFFF) {
4367                                 // four bytes
4368                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4369                         } else {
4370                                 return '';
4371                         }
4372                 }
4373                 
4374                 /**
4375                 * Puts an image in the page. 
4376                 * The upper-left corner must be given. 
4377                 * The dimensions can be specified in different ways:<ul>
4378                 * <li>explicit width and height (expressed in user unit)</li>
4379                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
4380                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
4381                 * 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;
4382                 * The format can be specified explicitly or inferred from the file extension.<br />
4383                 * It is possible to put a link on the image.<br />
4384                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
4385                 * @param string $file Name of the file containing the image.
4386                 * @param float $x Abscissa of the upper-left corner.
4387                 * @param float $y Ordinate of the upper-left corner.
4388                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
4389                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
4390                 * @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.
4391                 * @param mixed $link URL or identifier returned by AddLink().
4392                 * @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>
4393                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
4394                 * @param int $dpi dot-per-inch resolution used on resize
4395                 * @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>
4396                 * @param boolean $ismask true if this image is a mask, false otherwise
4397                 * @param mixed $imgmask image object returned by this function or false
4398                 * @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>
4399                 * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box.
4400                 * @return image information
4401                 * @access public
4402                 * @since 1.1
4403                 */
4404                 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) {
4405                         if ($x === '') {
4406                                 $x = $this->x;
4407                         }
4408                         if ($y === '') {
4409                                 $y = $this->y;
4410                         }
4411                         // get image dimensions
4412                         $imsize = @getimagesize($file);
4413                         if ($imsize === FALSE) {
4414                                 // encode spaces on filename
4415                                 $file = str_replace(' ', '%20', $file);
4416                                 $imsize = @getimagesize($file);
4417                                 if ($imsize === FALSE) {
4418                                         $this->Error('[Image] No such file or directory in '.$file);
4419                                 }
4420                         }
4421                         // get original image width and height in pixels
4422                         list($pixw, $pixh) = $imsize;
4423                         // calculate image width and height on document
4424                         if (($w <= 0) AND ($h <= 0)) {
4425                                 // convert image size to document unit
4426                                 $w = $this->pixelsToUnits($pixw);
4427                                 $h = $this->pixelsToUnits($pixh);
4428                         } elseif ($w <= 0) {
4429                                 $w = $h * $pixw / $pixh;
4430                         } elseif ($h <= 0) {
4431                                 $h = $w * $pixh / $pixw;
4432                         } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
4433                                 // scale image dimensions proportionally to fit within the ($w, $h) box
4434                                 if ((($w * $pixh) / ($h * $pixw)) < 1) {
4435                                         $h = $w * $pixh / $pixw;
4436                                 } else {
4437                                         $w = $h * $pixw / $pixh;
4438                                 }
4439                         }
4440                         // calculate new minimum dimensions in pixels
4441                         $neww = round($w * $this->k * $dpi / $this->dpi);
4442                         $newh = round($h * $this->k * $dpi / $this->dpi);
4443                         // check if resize is necessary (resize is used only to reduce the image)
4444                         if (($neww * $newh) >= ($pixw * $pixh)) {
4445                                 $resize = false;
4446                         }
4447                         // check if image has been already added on document
4448                         if (!in_array($file, $this->imagekeys)) {
4449                                 //First use of image, get info
4450                                 if ($type == '') {
4451                                         $fileinfo = pathinfo($file);
4452                                         if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
4453                                                 $type = $fileinfo['extension'];
4454                                         } else {
4455                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
4456                                         }
4457                                 }
4458                                 $type = strtolower($type);
4459                                 if ($type == 'jpg') {
4460                                         $type = 'jpeg';
4461                                 }
4462                                 $mqr = get_magic_quotes_runtime();
4463                                 //set_magic_quotes_runtime(0);
4464                                 // Specific image handlers
4465                                 $mtd = '_parse'.$type;
4466                                 // GD image handler function
4467                                 $gdfunction = 'imagecreatefrom'.$type;
4468                                 $info = false;
4469                                 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
4470                                         // TCPDF image functions
4471                                         $info = $this->$mtd($file);
4472                                         if ($info == 'pngalpha') {
4473                                                 return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
4474                                         }
4475                                 } 
4476                                 if (!$info) {
4477                                         if (function_exists($gdfunction)) {
4478                                                 // GD library
4479                                                 $img = $gdfunction($file);
4480                                                 if ($resize) {
4481                                                         $imgr = imagecreatetruecolor($neww, $newh);
4482                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 
4483                                                         $info = $this->_toJPEG($imgr);
4484                                                 } else {
4485                                                         $info = $this->_toJPEG($img);
4486                                                 }
4487                                         } elseif (extension_loaded('imagick')) {
4488                                                 // ImageMagick library
4489                                                 $img = new Imagick();
4490                                                 $img->readImage($file);
4491                                                 if ($resize) {
4492                                                         $img->resizeImage($neww, $newh, 10, 1, false);
4493                                                 }
4494                                                 $img->setCompressionQuality($this->jpeg_quality);
4495                                                 $img->setImageFormat('jpeg');
4496                                                 $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4497                                                 $img->writeImage($tempname);
4498                                                 $info = $this->_parsejpeg($tempname);
4499                                                 unlink($tempname);
4500                                                 $img->destroy();
4501                                         } 
4502                                         else if ($type == 'jpeg') {
4503                                                 $info = $this->_parsejpeg($file);
4504                                         }                                       
4505                                         else {
4506                                                 return;
4507                                         }
4508                                 }
4509                                 if ($info === false) {
4510                                         //If false, we cannot process image
4511                                         return;
4512                                 }
4513                                 //set_magic_quotes_runtime($mqr);
4514                                 if ($ismask) {
4515                                         // force grayscale
4516                                         $info['cs'] = 'DeviceGray';
4517                                 }
4518                                 $info['i'] = $this->numimages + 1;
4519                                 if ($imgmask !== false) {
4520                                         $info['masked'] = $imgmask;
4521                                 }
4522                                 // add image to document
4523                                 $this->setImageBuffer($file, $info);
4524                         } else {
4525                                 $info = $this->getImageBuffer($file);
4526                         }
4527                         // Check whether we need a new page first as this does not fit
4528                         if ($this->checkPageBreak($h, $y)) {
4529                                 $y = $this->GetY() + $this->cMargin;
4530                         }
4531                         // set bottomcoordinates
4532                         $this->img_rb_y = $y + $h;
4533                         // set alignment
4534                         if ($this->rtl) {
4535                                 if ($palign == 'L') {
4536                                         $ximg = $this->lMargin;
4537                                         // set right side coordinate
4538                                         $this->img_rb_x = $ximg + $w;
4539                                 } elseif ($palign == 'C') {
4540                                         $ximg = ($this->w - $x - $w) / 2;
4541                                         // set right side coordinate
4542                                         $this->img_rb_x = $ximg + $w;
4543                                 } else {
4544                                         $ximg = $this->w - $x - $w;
4545                                         // set left side coordinate
4546                                         $this->img_rb_x = $ximg;
4547                                 }
4548                         } else {
4549                                 if ($palign == 'R') {
4550                                         $ximg = $this->w - $this->rMargin - $w;
4551                                         // set left side coordinate
4552                                         $this->img_rb_x = $ximg;
4553                                 } elseif ($palign == 'C') {
4554                                         $ximg = ($this->w - $x - $w) / 2;
4555                                         // set right side coordinate
4556                                         $this->img_rb_x = $ximg + $w;
4557                                 } else {
4558                                         $ximg = $x;
4559                                         // set right side coordinate
4560                                         $this->img_rb_x = $ximg + $w;
4561                                 }
4562                         }
4563                         if ($ismask) {
4564                                 // embed hidden, ouside the canvas
4565                                 $xkimg = ($this->pagedim[$this->page]['w'] + 10);
4566                         } else {
4567                                 $xkimg = $ximg * $this->k;
4568                         }
4569                         $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']));
4570                         if (!empty($border)) {
4571                                 $bx = $x;
4572                                 $by = $y;
4573                                 $this->x = $ximg;
4574                                 $this->y = $y;
4575                                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
4576                                 $this->x = $bx;
4577                                 $this->y = $by;
4578                         }
4579                         if ($link) {
4580                                 $this->Link($ximg, $y, $w, $h, $link, 0);
4581                         }
4582                         // set pointer to align the successive text/objects
4583                         switch($align) {
4584                                 case 'T': {
4585                                         $this->y = $y;
4586                                         $this->x = $this->img_rb_x;
4587                                         break;
4588                                 }
4589                                 case 'M': {
4590                                         $this->y = $y + round($h/2);
4591                                         $this->x = $this->img_rb_x;
4592                                         break;
4593                                 }
4594                                 case 'B': {
4595                                         $this->y = $this->img_rb_y;
4596                                         $this->x = $this->img_rb_x;
4597                                         break;
4598                                 }
4599                                 case 'N': {
4600                                         $this->SetY($this->img_rb_y);
4601                                         break;
4602                                 }
4603                                 default:{
4604                                         break;
4605                                 }
4606                         }
4607                         $this->endlinex = $this->img_rb_x;
4608                         return $info['i'];
4609                 }
4610                                 
4611                 /**
4612                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
4613                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
4614                 * @param string $file Image file name.
4615                 * @param image $image Image object.
4616                 * return image JPEG image object.
4617                 * @access protected
4618                 */
4619                 protected function _toJPEG($image) {
4620                         $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4621                         imagejpeg($image, $tempname, $this->jpeg_quality);
4622                         imagedestroy($image);
4623                         $retvars = $this->_parsejpeg($tempname);
4624                         // tidy up by removing temporary image
4625                         unlink($tempname);
4626                         return $retvars;
4627                 }
4628                 
4629                 /**
4630                 * Extract info from a JPEG file without using the GD library.
4631                 * @param string $file image file to parse
4632                 * @return array structure containing the image data
4633                 * @access protected
4634                 */
4635                 protected function _parsejpeg($file) {
4636                         $a = getimagesize($file);
4637                         if (empty($a)) {
4638                                 $this->Error('Missing or incorrect image file: '.$file);
4639                         }
4640                         if ($a[2] != 2) {
4641                                 $this->Error('Not a JPEG file: '.$file);
4642                         }
4643                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
4644                                 $colspace = 'DeviceRGB';
4645                         } elseif ($a['channels'] == 4) {
4646                                 $colspace = 'DeviceCMYK';
4647                         } else {
4648                                 $colspace = 'DeviceGray';
4649                         }
4650                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
4651                         $data = file_get_contents($file);
4652                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
4653                 }
4654
4655                 /**
4656                 * Extract info from a PNG file without using the GD library.
4657                 * @param string $file image file to parse
4658                 * @return array structure containing the image data
4659                 * @access protected
4660                 */
4661                 protected function _parsepng($file) {
4662                         $f = fopen($file, 'rb');
4663                         if ($f === false) {
4664                                 $this->Error('Can\'t open image file: '.$file);
4665                         }
4666                         //Check signature
4667                         if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
4668                                 $this->Error('Not a PNG file: '.$file);
4669                         }
4670                         //Read header chunk
4671                         fread($f, 4);
4672                         if (fread($f, 4) != 'IHDR') {
4673                                 $this->Error('Incorrect PNG file: '.$file);
4674                         }
4675                         $w = $this->_freadint($f);
4676                         $h = $this->_freadint($f);
4677                         $bpc = ord(fread($f, 1));
4678                         if ($bpc > 8) {
4679                                 //$this->Error('16-bit depth not supported: '.$file);
4680                                 fclose($f);
4681                                 return false;
4682                         }
4683                         $ct = ord(fread($f, 1));
4684                         if ($ct == 0) {
4685                                 $colspace = 'DeviceGray';
4686                         } elseif ($ct == 2) {
4687                                 $colspace = 'DeviceRGB';
4688                         } elseif ($ct == 3) {
4689                                 $colspace = 'Indexed';
4690                         } else {
4691                                 // alpha channel
4692                                 fclose($f);
4693                                 return 'pngalpha';
4694                         }
4695                         if (ord(fread($f, 1)) != 0) {
4696                                 //$this->Error('Unknown compression method: '.$file);
4697                                 fclose($f);
4698                                 return false;
4699                         }
4700                         if (ord(fread($f, 1)) != 0) {
4701                                 //$this->Error('Unknown filter method: '.$file);
4702                                 fclose($f);
4703                                 return false;
4704                         }
4705                         if (ord(fread($f, 1)) != 0) {
4706                                 //$this->Error('Interlacing not supported: '.$file);
4707                                 fclose($f);
4708                                 return false;
4709                         }
4710                         fread($f, 4);
4711                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
4712                         //Scan chunks looking for palette, transparency and image data
4713                         $pal = '';
4714                         $trns = '';
4715                         $data = '';
4716                         do {
4717                                 $n = $this->_freadint($f);
4718                                 $type = fread($f, 4);
4719                                 if ($type == 'PLTE') {
4720                                         //Read palette
4721                                         $pal = $this->rfread($f, $n);
4722                                         fread($f, 4);
4723                                 } elseif ($type == 'tRNS') {
4724                                         //Read transparency info
4725                                         $t = $this->rfread($f, $n);
4726                                         if ($ct == 0) {
4727                                                 $trns = array(ord(substr($t, 1, 1)));
4728                                         } elseif ($ct == 2) {
4729                                                 $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
4730                                         } else {
4731                                                 $pos = strpos($t, chr(0));
4732                                                 if ($pos !== false) {
4733                                                         $trns = array($pos);
4734                                                 }
4735                                         }
4736                                         fread($f, 4);
4737                                 } elseif ($type == 'IDAT') {
4738                                         //Read image data block
4739                                         $data .= $this->rfread($f, $n);
4740                                         fread($f, 4);
4741                                 } elseif ($type == 'IEND') {
4742                                         break;
4743                                 } else {
4744                                         $this->rfread($f, $n + 4);
4745                                 }
4746                         } while ($n);
4747                         if (($colspace == 'Indexed') AND (empty($pal))) {
4748                                 //$this->Error('Missing palette in '.$file);
4749                                 fclose($f);
4750                                 return false;
4751                         }
4752                         fclose($f);
4753                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
4754                 }
4755
4756                 /**
4757                 * Binary-safe and URL-safe file read.
4758                 * 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.
4759                 * @param resource $handle
4760                 * @param int $length
4761                 * @return Returns the read string or FALSE in case of error.
4762                 * @author Nicola Asuni
4763                 * @access protected
4764                 * @since 4.5.027 (2009-03-16)
4765                 */
4766                 protected function rfread($handle, $length) {
4767                         $data = fread($handle, $length);
4768                         if ($data === false) {
4769                                 return false;
4770                         }
4771                         $rest = $length - strlen($data);
4772                         if ($rest > 0) {
4773                                 $data .= $this->rfread($handle, $rest);
4774                         }
4775                         return $data;
4776                 }
4777
4778                 /**
4779                 * Extract info from a PNG image with alpha channel using the GD library.
4780                 * @param string $file Name of the file containing the image.
4781                 * @param float $x Abscissa of the upper-left corner.
4782                 * @param float $y Ordinate of the upper-left corner.
4783                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
4784                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
4785                 * @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.
4786                 * @param mixed $link URL or identifier returned by AddLink().
4787                 * @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>
4788                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
4789                 * @param int $dpi dot-per-inch resolution used on resize
4790                 * @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>
4791                 * @author Valentin Schmidt, Nicola Asuni
4792                 * @access protected
4793                 * @since 4.3.007 (2008-12-04)
4794                 * @see Image()
4795                 */
4796                 protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
4797                         // get image size
4798                         list($wpx, $hpx) = getimagesize($file);
4799                         // generate images
4800                         $img = imagecreatefrompng($file);
4801                         $imgalpha = imagecreate($wpx, $hpx);
4802                         // generate gray scale pallete
4803                         for ($c = 0; $c < 256; ++$c) {
4804                                 ImageColorAllocate($imgalpha, $c, $c, $c);
4805                         }
4806                         // extract alpha channel
4807                         for ($xpx = 0; $xpx < $wpx; ++$xpx) {
4808                                 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
4809                                         $colorindex = imagecolorat($img, $xpx, $ypx);
4810                                         $col = imagecolorsforindex($img, $colorindex);
4811                                         imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
4812                                 }
4813                         }
4814                         // create temp alpha file
4815                         $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
4816                         imagepng($imgalpha, $tempfile_alpha);
4817                         imagedestroy($imgalpha);
4818                         // extract image without alpha channel
4819                         $imgplain = imagecreatetruecolor($wpx, $hpx);
4820                         imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4821                         // create temp image file
4822                         $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
4823                         imagepng($imgplain, $tempfile_plain);
4824                         imagedestroy($imgplain);
4825                         // embed mask image
4826                         $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
4827                         // embed image, masked with previously embedded mask
4828                         $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
4829                         // remove temp files
4830                         unlink($tempfile_alpha);
4831                         unlink($tempfile_plain);
4832                 }
4833
4834                 /**
4835                 * Correct the gamma value to be used with GD library
4836                 * @param float $v the gamma value to be corrected
4837                 * @access protected
4838                 * @since 4.3.007 (2008-12-04)
4839                 */
4840                 protected function getGDgamma($v) {
4841                         return (pow(($v / 255), 2.2) * 255);
4842                 } 
4843                 
4844                 /**
4845                 * Performs a line break. 
4846                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
4847                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
4848                 * @param boolean $cell if true add a cMargin to the x coordinate
4849                 * @access public
4850                 * @since 1.0
4851                 * @see Cell()
4852                 */
4853                 public function Ln($h='', $cell=false) {
4854                         //Line feed; default value is last cell height
4855                         if ($cell) {
4856                                 $cellmargin = $this->cMargin;
4857                         } else {
4858                                 $cellmargin = 0;
4859                         }
4860                         if ($this->rtl) {
4861                                 $this->x = $this->w - $this->rMargin - $cellmargin;
4862                         } else {
4863                                 $this->x = $this->lMargin + $cellmargin;
4864                         }
4865                         if (is_string($h)) {
4866                                 $this->y += $this->lasth;
4867                         } else {
4868                                 $this->y += $h;
4869                         }
4870                         $this->newline = true;
4871                 }
4872
4873                 /**
4874                 * Returns the relative X value of current position.
4875                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
4876                 * @return float
4877                 * @access public
4878                 * @since 1.2
4879                 * @see SetX(), GetY(), SetY()
4880                 */
4881                 public function GetX() {
4882                         //Get x position
4883                         if ($this->rtl) {
4884                                 return ($this->w - $this->x);
4885                         } else {
4886                                 return $this->x;
4887                         }
4888                 }
4889                 
4890                 /**
4891                 * Returns the absolute X value of current position.
4892                 * @return float
4893                 * @access public
4894                 * @since 1.2
4895                 * @see SetX(), GetY(), SetY()
4896                 */
4897                 public function GetAbsX() {
4898                         return $this->x;
4899                 }
4900                 
4901                 /**
4902                 * Returns the ordinate of the current position.
4903                 * @return float
4904                 * @access public
4905                 * @since 1.0
4906                 * @see SetY(), GetX(), SetX()
4907                 */
4908                 public function GetY() {
4909                         //Get y position
4910                         return $this->y;
4911                 }
4912                 
4913                 /**
4914                 * Defines the abscissa of the current position. 
4915                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
4916                 * @param float $x The value of the abscissa.
4917                 * @access public
4918                 * @since 1.2
4919                 * @see GetX(), GetY(), SetY(), SetXY()
4920                 */
4921                 public function SetX($x) {
4922                         //Set x position
4923                         if ($this->rtl) {
4924                                 if ($x >= 0) {
4925                                         $this->x = $this->w - $x;
4926                                 } else {
4927                                         $this->x = abs($x);
4928                                 }
4929                         } else {
4930                                 if ($x >= 0) {
4931                                         $this->x = $x;
4932                                 } else {
4933                                         $this->x = $this->w + $x;
4934                                 }
4935                         }
4936                         if ($this->x < 0) {
4937                                 $this->x = 0;
4938                         }
4939                         if ($this->x > $this->w) {
4940                                 $this->x = $this->w;
4941                         }
4942                 }
4943                 
4944                 /**
4945                 * Moves the current abscissa back to the left margin and sets the ordinate.
4946                 * If the passed value is negative, it is relative to the bottom of the page.
4947                 * @param float $y The value of the ordinate.
4948                 * @param bool $resetx if true (default) reset the X position.
4949                 * @access public
4950                 * @since 1.0
4951                 * @see GetX(), GetY(), SetY(), SetXY()
4952                 */
4953                 public function SetY($y, $resetx=true) {
4954                         if ($resetx) {
4955                                 //reset x
4956                                 if ($this->rtl) {
4957                                         $this->x = $this->w - $this->rMargin;
4958                                 } else {
4959                                         $this->x = $this->lMargin;
4960                                 }
4961                         }
4962                         if ($y >= 0) {
4963                                 $this->y = $y;
4964                         } else {
4965                                 $this->y = $this->h + $y;
4966                         }
4967                         if ($this->y < 0) {
4968                                 $this->y = 0;
4969                         }
4970                         if ($this->y > $this->h) {
4971                                 $this->y = $this->h;
4972                         }
4973                 }
4974                 
4975                 /**
4976                 * Defines the abscissa and ordinate of the current position. 
4977                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
4978                 * @param float $x The value of the abscissa
4979                 * @param float $y The value of the ordinate
4980                 * @access public
4981                 * @since 1.2
4982                 * @see SetX(), SetY()
4983                 */
4984                 public function SetXY($x, $y) {
4985                         //Set x and y positions
4986                         $this->SetY($y);
4987                         $this->SetX($x);
4988                 }
4989
4990                 /**
4991                 * Send the document to a given destination: string, local file or browser. 
4992                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
4993                 * The method first calls Close() if necessary to terminate the document.
4994                 * @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.
4995                 * @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>
4996                 * @access public
4997                 * @since 1.0
4998                 * @see Close()
4999                 */
5000                 public function Output($name='doc.pdf', $dest='I') {
5001                         //Output PDF to some destination
5002                         //Finish document if necessary
5003                         if ($this->state < 3) {
5004                                 $this->Close();
5005                         }
5006                         //Normalize parameters
5007                         if (is_bool($dest)) {
5008                                 $dest = $dest ? 'D' : 'F';
5009                         }
5010                         $dest = strtoupper($dest);
5011                         if ($dest != 'F') {
5012                                 $name = preg_replace('/[\s]+/', '_', $name);
5013                                 $name = preg_replace('/[^a-zA-Z0-9\._-\x{4e00}-\x{9fa5}]/u', '', $name);
5014                         }
5015                         if ($this->sign) {
5016                                 // *** apply digital signature to the document ***
5017                                 // get the document content
5018                                 $pdfdoc = $this->getBuffer();
5019                                 // remove last newline
5020                                 $pdfdoc = substr($pdfdoc, 0, -1);
5021                                 // Remove the original buffer
5022                                 if (isset($this->diskcache) AND $this->diskcache) {
5023                                         // remove buffer file from cache
5024                                         unlink($this->buffer);
5025                                 }
5026                                 unset($this->buffer);
5027                                 // remove filler space
5028                                 $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58;
5029                                 $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght);
5030                                 // define the ByteRange
5031                                 $byte_range = array();
5032                                 $byte_range[0] = 0;
5033                                 $byte_range[1] = $tmppos - 1;
5034                                 $byte_range[2] = $byte_range[1] + $this->signature_max_lenght;
5035                                 $byte_range[3] = strlen($pdfdoc) - $byte_range[1];
5036                                 // replace the ByteRange
5037                                 $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]);
5038                                 $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc);
5039                                 // write the document to a temporary folder
5040                                 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
5041                                 $f = fopen($tempdoc, 'wb');
5042                                 if (!$f) {
5043                                         $this->Error('Unable to create temporary file: '.$tempdoc);
5044                                 }
5045                                 $pdfdoc_lenght = strlen($pdfdoc);
5046                                 fwrite($f, $pdfdoc, $pdfdoc_lenght);
5047                                 fclose($f);
5048                                 // get digital signature.
5049                                 // IS THE FOLLOWING PROCEDURE CORRECT? THE SIGNED DOCUMENTS ARE NOT VALID!
5050                                 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
5051                                 if (empty($this->signature_data['extracerts'])) {
5052                                         openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
5053                                 } else {
5054                                         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']);
5055                                 }       
5056                                 unlink($tempdoc);
5057                                 // read signature
5058                                 $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
5059                                 unlink($tempsign);
5060                                 // extract signature
5061                                 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
5062                                 $tmparr = explode("\n\n", $signature);
5063                                 $signature = $tmparr[1];
5064                                 unset($tmparr);
5065                                 // decode signature
5066                                 $signature = base64_decode(trim($signature));
5067                                 // convert signature to hex
5068                                 $signature = current(unpack('H*', $signature));
5069                                 $signature = str_pad($signature, $this->signature_max_lenght, '0');
5070                                 // Add signature to the document
5071                                 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3]));
5072                                 $this->diskcache = false;
5073                                 $this->buffer = &$pdfdoc;
5074                                 $this->bufferlen = strlen($pdfdoc);
5075                         }
5076                         switch($dest) {
5077                                 case 'I': {
5078                                         // Send PDF to the standard output
5079                                         if (ob_get_contents()) {
5080                                                 $this->Error('Some data has already been output, can\'t send PDF file');
5081                                         }
5082                                         if (php_sapi_name() != 'cli') {
5083                                                 //We send to a browser
5084                                                 header('Content-Type: application/pdf');
5085                                                 if (headers_sent()) {
5086                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
5087                                                 }
5088                                                 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5089                                                 header('Pragma: public');
5090                                                 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5091                                                 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');      
5092                                                 header('Content-Length: '.$this->bufferlen);
5093                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
5094                                         }
5095                                         echo $this->getBuffer();
5096                                         break;
5097                                 }
5098                                 case 'D': {
5099                                         // Download PDF as file
5100                                         if (ob_get_contents()) {
5101                                                 $this->Error('Some data has already been output, can\'t send PDF file');
5102                                         }
5103                                         header('Content-Description: File Transfer');
5104                                         if (headers_sent()) {
5105                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
5106                                         }
5107                                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5108                                         header('Pragma: public');
5109                                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5110                                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
5111                                         // force download dialog
5112                                         header('Content-Type: application/force-download');
5113                                         header('Content-Type: application/octet-stream', false);
5114                                         header('Content-Type: application/download', false);
5115                                         header('Content-Type: application/pdf', false);
5116                                         // use the Content-Disposition header to supply a recommended filename
5117                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
5118                                         header('Content-Transfer-Encoding: binary');
5119                                         header('Content-Length: '.$this->bufferlen);
5120                                         echo $this->getBuffer();
5121                                         break;
5122                                 }
5123                                 case 'F': {
5124                                         // Save PDF to a local file
5125                                         if ($this->diskcache) {
5126                                                 copy($this->buffer, $name);
5127                                         } else {
5128                                                 $f = fopen($name, 'wb');
5129                                                 if (!$f) {
5130                                                         $this->Error('Unable to create output file: '.$name);
5131                                                 }
5132                                                 fwrite($f, $this->getBuffer(), $this->bufferlen);
5133                                                 fclose($f);
5134                                         }
5135                                         break;
5136                                 }
5137                                 case 'S': {
5138                                         // Returns PDF as a string
5139                                         return $this->getBuffer();
5140                                 }
5141                                 default: {
5142                                         $this->Error('Incorrect output destination: '.$dest);
5143                                 }
5144                         }
5145                         return '';
5146                 }
5147
5148                 /**
5149                  * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
5150                  * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.
5151                  * @param boolean $preserve_objcopy if true preserves the objcopy variable
5152                  * @access public
5153                  * @since 4.5.016 (2009-02-24)
5154                  */
5155                 public function _destroy($destroyall=false, $preserve_objcopy=false) {
5156                         if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
5157                                 // remove buffer file from cache
5158                                 unlink($this->buffer);
5159                         }
5160                         foreach (array_keys(get_object_vars($this)) as $val) {
5161                                 if ($destroyall OR (
5162                                         ($val != 'internal_encoding') 
5163                                         AND ($val != 'state') 
5164                                         AND ($val != 'bufferlen') 
5165                                         AND ($val != 'buffer') 
5166                                         AND ($val != 'diskcache')
5167                                         AND ($val != 'sign')
5168                                         AND ($val != 'signature_data')
5169                                         AND ($val != 'signature_max_lenght')
5170                                         )) {
5171                                         if (!$preserve_objcopy OR ($val != 'objcopy')) {
5172                                                 unset($this->$val);
5173                                         }
5174                                 }
5175                         }
5176                 }
5177                 
5178                 /**
5179                 * Check for locale-related bug
5180                 * @access protected
5181                 */
5182                 protected function _dochecks() {
5183                         //Check for locale-related bug
5184                         if (1.1 == 1) {
5185                                 $this->Error('Don\'t alter the locale before including class file');
5186                         }
5187                         //Check for decimal separator
5188                         if (sprintf('%.1F', 1.0) != '1.0') {
5189                                 setlocale(LC_NUMERIC, 'C');
5190                         }
5191                 }
5192
5193                 /**
5194                 * Return fonts path
5195                 * @return string
5196                 * @access protected
5197                 */
5198                 protected function _getfontpath() {
5199                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
5200                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
5201                         }
5202                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
5203                 }
5204                 
5205                 /**
5206                 * Output pages.
5207                 * @access protected
5208                 */
5209                 protected function _putpages() {
5210                         $nb = $this->numpages;
5211                         if (!empty($this->AliasNbPages)) {
5212                                 $nbs = $this->formatPageNumber($nb);
5213                                 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
5214                                 $alias_a = $this->_escape($this->AliasNbPages);
5215                                 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
5216                                 if ($this->isunicode) {
5217                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
5218                                         $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
5219                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
5220                                         $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
5221                                 }
5222                         }
5223                         if (!empty($this->AliasNumPage)) {
5224                                 $alias_pa = $this->_escape($this->AliasNumPage);
5225                                 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
5226                                 if ($this->isunicode) {
5227                                         $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
5228                                         $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
5229                                         $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
5230                                         $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
5231                                 }
5232                         }
5233                         $pagegroupnum = 0;
5234                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
5235                         for ($n=1; $n <= $nb; ++$n) {
5236                                 $temppage = $this->getPageBuffer($n);
5237                                 if (!empty($this->pagegroups)) {
5238                                         if(isset($this->newpagegroup[$n])) {
5239                                                 $pagegroupnum = 0;
5240                                         }
5241                                         ++$pagegroupnum;
5242                                         foreach ($this->pagegroups as $k => $v) {
5243                                                 // replace total pages group numbers
5244                                                 $vs = $this->formatPageNumber($v);
5245                                                 $vu = $this->UTF8ToUTF16BE($vs, false);
5246                                                 $alias_ga = $this->_escape($k);
5247                                                 $alias_gau = $this->_escape('{'.$k.'}');
5248                                                 if ($this->isunicode) {
5249                                                         $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
5250                                                         $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
5251                                                         $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
5252                                                         $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
5253                                                 }
5254                                                 $temppage = str_replace($alias_gau, $vu, $temppage);
5255                                                 if ($this->isunicode) {
5256                                                         $temppage = str_replace($alias_gbu, $vu, $temppage);
5257                                                         $temppage = str_replace($alias_gcu, $vu, $temppage);
5258                                                         $temppage = str_replace($alias_gb, $vs, $temppage);
5259                                                         $temppage = str_replace($alias_gc, $vs, $temppage);
5260                                                 }
5261                                                 $temppage = str_replace($alias_ga, $vs, $temppage);
5262                                                 // replace page group numbers
5263                                                 $pvs = $this->formatPageNumber($pagegroupnum);
5264                                                 $pvu = $this->UTF8ToUTF16BE($pvs, false);
5265                                                 $pk = str_replace('{nb', '{pnb', $k);
5266                                                 $alias_pga = $this->_escape($pk);
5267                                                 $alias_pgau = $this->_escape('{'.$pk.'}');
5268                                                 if ($this->isunicode) {
5269                                                         $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
5270                                                         $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
5271                                                         $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
5272                                                         $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
5273                                                 }
5274                                                 $temppage = str_replace($alias_pgau, $pvu, $temppage);
5275                                                 if ($this->isunicode) {
5276                                                         $temppage = str_replace($alias_pgbu, $pvu, $temppage);
5277                                                         $temppage = str_replace($alias_pgcu, $pvu, $temppage);
5278                                                         $temppage = str_replace($alias_pgb, $pvs, $temppage);
5279                                                         $temppage = str_replace($alias_pgc, $pvs, $temppage);
5280                                                 }
5281                                                 $temppage = str_replace($alias_pga, $pvs, $temppage);
5282                                         }
5283                                 }
5284                                 if (!empty($this->AliasNbPages)) {
5285                                         // replace total pages number
5286                                         $temppage = str_replace($alias_au, $nbu, $temppage);
5287                                         if ($this->isunicode) {
5288                                                 $temppage = str_replace($alias_bu, $nbu, $temppage);
5289                                                 $temppage = str_replace($alias_cu, $nbu, $temppage);
5290                                                 $temppage = str_replace($alias_b, $nbs, $temppage);
5291                                                 $temppage = str_replace($alias_c, $nbs, $temppage);
5292                                         }
5293                                         $temppage = str_replace($alias_a, $nbs, $temppage);
5294                                 }
5295                                 if (!empty($this->AliasNumPage)) {
5296                                         // replace page number
5297                                         $pnbs = $this->formatPageNumber($n);
5298                                         $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
5299                                         $temppage = str_replace($alias_pau, $pnbu, $temppage);
5300                                         if ($this->isunicode) {
5301                                                 $temppage = str_replace($alias_pbu, $pnbu, $temppage);
5302                                                 $temppage = str_replace($alias_pcu, $pnbu, $temppage);
5303                                                 $temppage = str_replace($alias_pb, $pnbs, $temppage);
5304                                                 $temppage = str_replace($alias_pc, $pnbs, $temppage);
5305                                         }
5306                                         $temppage = str_replace($alias_pa, $pnbs, $temppage);
5307                                 }
5308                                 $temppage = str_replace($this->epsmarker, '', $temppage);
5309                                 //$this->setPageBuffer($n, $temppage);
5310                                 //Page
5311                                 $this->_newobj();
5312                                 $this->_out('<</Type /Page');
5313                                 $this->_out('/Parent 1 0 R');
5314                                 $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
5315                                 $this->_out('/Resources 2 0 R');
5316                                 $this->_putannots($n);
5317                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
5318                                 $this->_out('endobj');
5319                                 //Page content
5320                                 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
5321                                 $this->_newobj();
5322                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
5323                                 $this->_putstream($p);
5324                                 $this->_out('endobj');
5325                                 if ($this->diskcache) {
5326                                         // remove temporary files
5327                                         unlink($this->pages[$n]);
5328                                 }
5329                         }
5330                         //Pages root
5331                         $this->offsets[1] = $this->bufferlen;
5332                         $this->_out('1 0 obj');
5333                         $this->_out('<</Type /Pages');
5334                         $kids='/Kids [';
5335                         for ($i=0; $i < $nb; ++$i) {
5336                                 $kids .= (3 + (2 * $i)).' 0 R ';
5337                         }
5338                         $this->_out($kids.']');
5339                         $this->_out('/Count '.$nb);
5340                         //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
5341                         $this->_out('>>');
5342                         $this->_out('endobj');
5343                 }
5344
5345                 /**
5346                 * Output Page Annotations.
5347                 * !!! THIS FUNCTION IS NOT YET COMPLETED !!!
5348                 * See section 8.4 of PDF reference.
5349                 * @param int $n page number
5350                 * @access protected
5351                 * @author Nicola Asuni
5352                 * @since 4.0.018 (2008-08-06)
5353                 */
5354                 protected function _putannots($n) {
5355                         if (isset($this->PageAnnots[$n])) {
5356                                 $annots = '/Annots [';
5357                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
5358                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
5359                                         $a = $pl['x'] * $this->k;
5360                                         $b = $this->pagedim[$n]['h'] - ($pl['y']  * $this->k);
5361                                         $c = $pl['w'] * $this->k;
5362                                         $d = $pl['h'] * $this->k;
5363                                         $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d);
5364                                         $annots .= "\n";
5365                                         $annots .= '<</Type /Annot';
5366                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
5367                                         $annots .= ' /Rect ['.$rect.']';
5368                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
5369                                         //$annots .= ' /P ';
5370                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
5371                                         $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis'));
5372                                         if (isset($pl['opt']['f'])) {
5373                                                 $val = 0;
5374                                                 if (is_array($pl['opt']['f'])) {
5375                                                         foreach ($pl['opt']['f'] as $f) {
5376                                                                 switch (strtolower($f)) {
5377                                                                         case 'invisible': {
5378                                                                                 $val += 1 << 0;
5379                                                                                 break;
5380                                                                         }
5381                                                                         case 'hidden': {
5382                                                                                 $val += 1 << 1;
5383                                                                                 break;
5384                                                                         }
5385                                                                         case 'print': {
5386                                                                                 $val += 1 << 2;
5387                                                                                 break;
5388                                                                         }
5389                                                                         case 'nozoom': {
5390                                                                                 $val += 1 << 3;
5391                                                                                 break;
5392                                                                         }
5393                                                                         case 'norotate': {
5394                                                                                 $val += 1 << 4;
5395                                                                                 break;
5396                                                                         }
5397                                                                         case 'noview': {
5398                                                                                 $val += 1 << 5;
5399                                                                                 break;
5400                                                                         }
5401                                                                         case 'readonly': {
5402                                                                                 $val += 1 << 6;
5403                                                                                 break;
5404                                                                         }
5405                                                                         case 'locked': {
5406                                                                                 $val += 1 << 8;
5407                                                                                 break;
5408                                                                         }
5409                                                                         case 'togglenoview': {
5410                                                                                 $val += 1 << 9;
5411                                                                                 break;
5412                                                                         }
5413                                                                         case 'lockedcontents': {
5414                                                                                 $val += 1 << 10;
5415                                                                                 break;
5416                                                                         }
5417                                                                         default: {
5418                                                                                 break;
5419                                                                         }
5420                                                                 }
5421                                                         }
5422                                                 }
5423                                                 $annots .= ' /F '.intval($val);
5424                                         }
5425                                         //$annots .= ' /AP ';
5426                                         //$annots .= ' /AS ';
5427                                         $annots .= ' /Border [';
5428                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
5429                                                 $annots .= intval($pl['opt']['border'][0]).' ';
5430                                                 $annots .= intval($pl['opt']['border'][1]).' ';
5431                                                 $annots .= intval($pl['opt']['border'][2]);
5432                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
5433                                                         $annots .= ' [';
5434                                                         foreach ($pl['opt']['border'][3] as $dash) {
5435                                                                 $annots .= intval($dash).' ';
5436                                                         }
5437                                                         $annots .= ']';
5438                                                 }
5439                                         } else {
5440                                                 $annots .= '0 0 0';
5441                                         }
5442                                         $annots .= ']';
5443                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
5444                                                 $annots .= ' /BS <<Type /Border';
5445                                                 if (isset($pl['opt']['bs']['w'])) {
5446                                                         $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w']));
5447                                                 }
5448                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
5449                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
5450                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
5451                                                 }
5452                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
5453                                                         $annots .= ' /D [';
5454                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
5455                                                                 $cord = floatval($cord);
5456                                                                 $annots .= sprintf(" %.4F", $cord);
5457                                                         }
5458                                                         $annots .= ']';
5459                                                 }
5460                                                 $annots .= '>> ';
5461                                         }
5462                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
5463                                                 $annots .= ' /BE <<';
5464                                                 $bstyles = array('S', 'C');
5465                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
5466                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
5467                                                 } else {
5468                                                         $annots .= ' /S /S';
5469                                                 }
5470                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
5471                                                         $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
5472                                                 }
5473                                                 $annots .= '>>';
5474                                         }
5475                                         $annots .= ' /C [';
5476                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
5477                                                 foreach ($pl['opt']['c'] as $col) {
5478                                                         $col = intval($col);
5479                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
5480                                                         $annots .= sprintf(" %.4F", $color);
5481                                                 }
5482                                         }
5483                                         $annots .= ']';
5484                                         //$annots .= ' /StructParent ';
5485                                         //$annots .= ' /OC ';
5486                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
5487                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
5488                                                 // this is a markup type
5489                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
5490                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
5491                                                 }
5492                                                 //$annots .= ' /Popup ';
5493                                                 if (isset($pl['opt']['ca'])) {
5494                                                         $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
5495                                                 }
5496                                                 if (isset($pl['opt']['rc'])) {
5497                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5498                                                 }
5499                                                 $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis'));
5500                                                 //$annots .= ' /IRT ';
5501                                                 if (isset($pl['opt']['subj'])) {
5502                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
5503                                                 }
5504                                                 //$annots .= ' /RT ';
5505                                                 //$annots .= ' /IT ';
5506                                                 //$annots .= ' /ExData ';
5507                                         }
5508                                         switch (strtolower($pl['opt']['subtype'])) {
5509                                                 case 'text': {
5510                                                         if (isset($pl['opt']['open'])) {
5511                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
5512                                                         }
5513                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
5514                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5515                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
5516                                                         } else {
5517                                                                 $annots .= ' /Name /Note';
5518                                                         }
5519                                                         $statemodels = array('Marked', 'Review');
5520                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
5521                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5522                                                         } else {
5523                                                                 $pl['opt']['statemodel'] = 'Marked';
5524                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5525                                                         }
5526                                                         if ($pl['opt']['statemodel'] == 'Marked') {
5527                                                                 $states = array('Accepted', 'Unmarked');
5528                                                         } else {
5529                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
5530                                                         }
5531                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
5532                                                                 $annots .= ' /State /'.$pl['opt']['state'];
5533                                                         } else {
5534                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
5535                                                                         $annots .= ' /State /Unmarked';
5536                                                                 } else {
5537                                                                         $annots .= ' /State /None';
5538                                                                 }
5539                                                         }
5540                                                         break;
5541                                                 }
5542                                                 case 'link': {
5543                                                         if(is_string($pl['txt'])) {
5544                                                                 // external URI link
5545                                                                 $annots .= ' /A <</S /URI /URI '.$this->_datastring($pl['txt']).'>>';
5546                                                         } else {
5547                                                                 // internal link
5548                                                                 $l = $this->links[$pl['txt']];
5549                                                                 $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
5550                                                         }
5551                                                         $hmodes = array('N', 'I', 'O', 'P');
5552                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
5553                                                                 $annots .= ' /H /'.$pl['opt']['h'];
5554                                                         } else {
5555                                                                 $annots .= ' /H /I';
5556                                                         }
5557                                                         //$annots .= ' /PA ';
5558                                                         //$annots .= ' /Quadpoints ';
5559                                                         break;
5560                                                 }
5561                                                 case 'freetext': {
5562                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
5563                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
5564                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
5565                                                         }
5566                                                         if (isset($pl['opt']['rc'])) {
5567                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5568                                                         }
5569                                                         if (isset($pl['opt']['ds'])) {
5570                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
5571                                                         }
5572                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
5573                                                                 $annots .= ' /CL [';
5574                                                                 foreach ($pl['opt']['cl'] as $cl) {
5575                                                                         $annots .= sprintf("%.4F ", $cl * $this->k);
5576                                                                 }
5577                                                                 $annots .= ']';
5578                                                         }
5579                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
5580                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
5581                                                                 $annots .= ' /IT '.$pl['opt']['it'];
5582                                                         }
5583                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
5584                                                                 $l = $pl['opt']['rd'][0] * $this->k;
5585                                                                 $r = $pl['opt']['rd'][1] * $this->k;
5586                                                                 $t = $pl['opt']['rd'][2] * $this->k;
5587                                                                 $b = $pl['opt']['rd'][3] * $this->k;
5588                                                                 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
5589                                                         }
5590                                                         //$annots .= ' /LE ';
5591                                                         break;
5592                                                 }
5593                                                 // ... to be completed ...
5594                                                 case 'line': {
5595                                                         break;
5596                                                 }
5597                                                 case 'square': {
5598                                                         break;
5599                                                 }
5600                                                 case 'circle': {
5601                                                         break;
5602                                                 }
5603                                                 case 'polygon': {
5604                                                         break;
5605                                                 }
5606                                                 case 'polyline': {
5607                                                         break;
5608                                                 }
5609                                                 case 'highlight': {
5610                                                         break;
5611                                                 }
5612                                                 case 'underline': {
5613                                                         break;
5614                                                 }
5615                                                 case 'squiggly': {
5616                                                         break;
5617                                                 }
5618                                                 case 'strikeout': {
5619                                                         break;
5620                                                 }
5621                                                 case 'stamp': {
5622                                                         break;
5623                                                 }
5624                                                 case 'caret': {
5625                                                         break;
5626                                                 }
5627                                                 case 'ink': {
5628                                                         break;
5629                                                 }
5630                                                 case 'popup': {
5631                                                         break;
5632                                                 }
5633                                                 case 'fileattachment': {
5634                                                         if (!isset($pl['opt']['fs'])) {
5635                                                                 break;
5636                                                         }
5637                                                         $filename = basename($pl['opt']['fs']);
5638                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
5639                                                                 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
5640                                                                 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
5641                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5642                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
5643                                                                 } else {
5644                                                                         $annots .= ' /Name /PushPin';
5645                                                                 }
5646                                                         }
5647                                                         break;
5648                                                 }
5649                                                 case 'sound': {
5650                                                         if (!isset($pl['opt']['sound'])) {
5651                                                                 break;
5652                                                         }
5653                                                         $filename = basename($pl['opt']['sound']);
5654                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
5655                                                                 // ... TO BE COMPLETED ...
5656                                                                 $iconsapp = array('Speaker', 'Mic');
5657                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5658                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
5659                                                                 } else {
5660                                                                         $annots .= ' /Name /Speaker';
5661                                                                 }
5662                                                         }
5663                                                         break;
5664                                                 }
5665                                                 case 'movie': {
5666                                                         break;
5667                                                 }
5668                                                 case 'widget': {
5669                                                         if (isset($pl['opt']['h'])) {
5670                                                                 $annots .= ' /H '.intval($pl['opt']['h']);
5671                                                         }
5672                                                         if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) {
5673                                                                 $annots .= ' /MK <<';
5674                                                                 // ... TO BE COMPLETED ...
5675                                                                 $annots .= '>>';
5676                                                         }
5677                                                         break;
5678                                                 }
5679                                                 case 'screen': {
5680                                                         break;
5681                                                 }
5682                                                 case 'printermark': {
5683                                                         break;
5684                                                 }
5685                                                 case 'trapnet': {
5686                                                         break;
5687                                                 }
5688                                                 case 'watermark': {
5689                                                         break;
5690                                                 }
5691                                                 case '3d': {
5692                                                         break;
5693                                                 }
5694                                                 default: {
5695                                                         break;
5696                                                 }
5697                                         }
5698                                         
5699                                 $annots .= '>>';
5700                                 }
5701                                 $annots .= "\n]";
5702                                 $this->_out($annots);
5703                         }
5704                 }
5705
5706                 /**
5707                 * Output fonts.
5708                 * @access protected
5709                 */
5710                 protected function _putfonts() {
5711                         $nf = $this->n;
5712                         foreach ($this->diffs as $diff) {
5713                                 //Encodings
5714                                 $this->_newobj();
5715                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
5716                                 $this->_out('endobj');
5717                         }
5718                         $mqr = get_magic_quotes_runtime();
5719                         //set_magic_quotes_runtime(0);
5720                         foreach ($this->FontFiles as $file => $info) {
5721                                 // search and get font file to embedd
5722                                 $fontdir = $info['fontdir'];
5723                                 $file = strtolower($file);
5724                                 $fontfile = '';
5725                                 // search files on various directories
5726                                 if (file_exists($fontdir.$file)) {
5727                                         $fontfile = $fontdir.$file;
5728                                 } elseif (file_exists($this->_getfontpath().$file)) {
5729                                         $fontfile = $this->_getfontpath().$file;
5730                                 } elseif (file_exists($file)) {
5731                                         $fontfile = $file;
5732                                 }
5733                                 if (!$this->empty_string($fontfile)) {
5734                                         $font = file_get_contents($fontfile);
5735                                         $compressed = (substr($file, -2) == '.z');
5736                                         if ((!$compressed) AND (isset($info['length2']))) {
5737                                                 $header = (ord($font{0}) == 128);
5738                                                 if ($header) {
5739                                                         //Strip first binary header
5740                                                         $font = substr($font, 6);
5741                                                 }
5742                                                 if ($header AND (ord($font{$info['length1']}) == 128)) {
5743                                                         //Strip second binary header
5744                                                         $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
5745                                                 }
5746                                         }
5747                                         $this->_newobj();
5748                                         $this->FontFiles[$file]['n'] = $this->n;
5749                                         $this->_out('<</Length '.strlen($font));
5750                                         if ($compressed) {
5751                                                 $this->_out('/Filter /FlateDecode');
5752                                         }
5753                                         $this->_out('/Length1 '.$info['length1']);
5754                                         if (isset($info['length2'])) {
5755                                                 $this->_out('/Length2 '.$info['length2'].' /Length3 0');
5756                                         }
5757                                         $this->_out('>>');
5758                                         $this->_putstream($font);
5759                                         $this->_out('endobj');
5760                                 }
5761                         }
5762                         //set_magic_quotes_runtime($mqr);
5763                         foreach ($this->fontkeys as $k) {
5764                                 //Font objects
5765                                 $this->setFontSubBuffer($k, 'n', $this->n + 1);
5766                                 $font = $this->getFontBuffer($k);
5767                                 $type = $font['type'];
5768                                 $name = $font['name'];
5769                                 if ($type == 'core') {
5770                                         //Standard font
5771                                         $this->_newobj();
5772                                         $this->_out('<</Type /Font');
5773                                         $this->_out('/BaseFont /'.$name);
5774                                         $this->_out('/Subtype /Type1');
5775                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
5776                                                 $this->_out('/Encoding /WinAnsiEncoding');
5777                                         }
5778                                         $this->_out('>>');
5779                                         $this->_out('endobj');
5780                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
5781                                         //Additional Type1 or TrueType font
5782                                         $this->_newobj();
5783                                         $this->_out('<</Type /Font');
5784                                         $this->_out('/BaseFont /'.$name);
5785                                         $this->_out('/Subtype /'.$type);
5786                                         $this->_out('/FirstChar 32 /LastChar 255');
5787                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
5788                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
5789                                         if ($font['enc']) {
5790                                                 if (isset($font['diff'])) {
5791                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
5792                                                 } else {
5793                                                         $this->_out('/Encoding /WinAnsiEncoding');
5794                                                 }
5795                                         }
5796                                         $this->_out('>>');
5797                                         $this->_out('endobj');
5798                                         // Widths
5799                                         $this->_newobj();
5800                                         $cw = &$font['cw'];
5801                                         $s = '[';
5802                                         for ($i = 32; $i < 256; ++$i) {
5803                                                 $s .= $cw[$i].' ';
5804                                         }
5805                                         $this->_out($s.']');
5806                                         $this->_out('endobj');
5807                                         //Descriptor
5808                                         $this->_newobj();
5809                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
5810                                         foreach ($font['desc'] as $k => $v) {
5811                                                 $s .= ' /'.$k.' '.$v.'';
5812                                         }
5813                                         if (!$this->empty_string($font['file'])) {
5814                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
5815                                         }
5816                                         $this->_out($s.'>>');
5817                                         $this->_out('endobj');
5818                                 } else {
5819                                         //Allow for additional types
5820                                         $mtd = '_put'.strtolower($type);
5821                                         if (!method_exists($this, $mtd)) {
5822                                                 $this->Error('Unsupported font type: '.$type);
5823                                         }
5824                                         $this->$mtd($font);
5825                                 }
5826                         }
5827                 }
5828                 
5829                 /**
5830                 * Outputs font widths
5831                 * @parameter array $font font data
5832                 * @parameter int $cidoffset offset for CID values
5833                 * @author Nicola Asuni
5834                 * @access protected
5835                 * @since 4.4.000 (2008-12-07)
5836                 */
5837                 protected function _putfontwidths($font, $cidoffset=0) {
5838                         ksort($font['cw']);
5839                         $rangeid = 0;
5840                         $range = array();
5841                         $prevcid = -2;
5842                         $prevwidth = -1;
5843                         $interval = false;
5844                         // for each character
5845                         foreach ($font['cw'] as $cid => $width) {
5846                                 $cid -= $cidoffset;
5847                                 if ($width != $font['dw']) {
5848                                         if ($cid == ($prevcid + 1)) {
5849                                                 // consecutive CID
5850                                                 if ($width == $prevwidth) {
5851                                                         if ($width == $range[$rangeid][0]) {
5852                                                                 $range[$rangeid][] = $width;
5853                                                         } else {
5854                                                                 array_pop($range[$rangeid]);
5855                                                                 // new range
5856                                                                 $rangeid = $prevcid;
5857                                                                 $range[$rangeid] = array();
5858                                                                 $range[$rangeid][] = $prevwidth;
5859                                                                 $range[$rangeid][] = $width;
5860                                                         }
5861                                                         $interval = true;
5862                                                         $range[$rangeid]['interval'] = true;
5863                                                 } else {
5864                                                         if ($interval) {
5865                                                                 // new range
5866                                                                 $rangeid = $cid;
5867                                                                 $range[$rangeid] = array();
5868                                                                 $range[$rangeid][] = $width;
5869                                                         } else {
5870                                                                 $range[$rangeid][] = $width;
5871                                                         }
5872                                                         $interval = false;
5873                                                 }
5874                                         } else {
5875                                                 // new range
5876                                                 $rangeid = $cid;
5877                                                 $range[$rangeid] = array();
5878                                                 $range[$rangeid][] = $width;
5879                                                 $interval = false;
5880                                         }
5881                                         $prevcid = $cid;
5882                                         $prevwidth = $width;
5883                                 }
5884                         }
5885                         // optimize ranges
5886                         $prevk = -1;
5887                         $nextk = -1;
5888                         $prevint = false;
5889                         foreach ($range as $k => $ws) {
5890                                 $cws = count($ws);
5891                                 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
5892                                         if (isset($range[$k]['interval'])) {
5893                                                 unset($range[$k]['interval']);
5894                                         }
5895                                         $range[$prevk] = array_merge($range[$prevk], $range[$k]);
5896                                         unset($range[$k]);
5897                                 } else {
5898                                         $prevk = $k;
5899                                 }
5900                                 $nextk = $k + $cws;
5901                                 if (isset($ws['interval'])) {
5902                                         if ($cws > 3) {
5903                                                 $prevint = true;
5904                                         } else {
5905                                                 $prevint = false;
5906                                         }
5907                                         unset($range[$k]['interval']);
5908                                         --$nextk;
5909                                 } else {
5910                                         $prevint = false;
5911                                 }
5912                         }
5913                         // output data
5914                         $w = '';
5915                         foreach ($range as $k => $ws) {
5916                                 if (count(array_count_values($ws)) == 1) {
5917                                         // interval mode is more compact
5918                                         $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
5919                                 } else {
5920                                         // range mode
5921                                         $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
5922                                 }
5923                         }
5924                         $this->_out('/W ['.$w.' ]');
5925                 }
5926                 
5927                 /**
5928                 * Adds unicode fonts.<br>
5929                 * Based on PDF Reference 1.3 (section 5)
5930                 * @parameter array $font font data
5931                 * @access protected
5932                 * @author Nicola Asuni
5933                 * @since 1.52.0.TC005 (2005-01-05)
5934                 */
5935                 protected function _puttruetypeunicode($font) {
5936                         // Type0 Font
5937                         // A composite font composed of other fonts, organized hierarchically
5938                         $this->_newobj();
5939                         $this->_out('<</Type /Font');
5940                         $this->_out('/Subtype /Type0');
5941                         $this->_out('/BaseFont /'.$font['name'].'');
5942                         $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.
5943                         $this->_out('/ToUnicode /Identity-H');
5944                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5945                         $this->_out('>>');
5946                         $this->_out('endobj');
5947                         // CIDFontType2
5948                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5949                         $this->_newobj();
5950                         $this->_out('<</Type /Font');
5951                         $this->_out('/Subtype /CIDFontType2');
5952                         $this->_out('/BaseFont /'.$font['name'].'');
5953                         // A dictionary containing entries that define the character collection of the CIDFont.
5954                         $cidinfo = '/Registry '.$this->_datastring('Adobe');
5955                         $cidinfo .= ' /Ordering '.$this->_datastring('Identity');
5956                         $cidinfo .= ' /Supplement 0';
5957                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
5958                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
5959                         $this->_out('/DW '.$font['dw'].''); // default width
5960                         $this->_putfontwidths($font, 0);
5961                         $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
5962                         $this->_out('>>');
5963                         $this->_out('endobj');                  
5964                         // Font descriptor
5965                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
5966                         $this->_newobj();
5967                         $this->_out('<</Type /FontDescriptor');
5968                         $this->_out('/FontName /'.$font['name']);
5969                         foreach ($font['desc'] as $key => $value) {
5970                                 $this->_out('/'.$key.' '.$value);
5971                         }
5972                         $fontdir = '';
5973                         if (!$this->empty_string($font['file'])) {
5974                                 // A stream containing a TrueType font
5975                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
5976                                 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
5977                         }
5978                         $this->_out('>>');
5979                         $this->_out('endobj');
5980                         $this->_newobj();
5981                         if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
5982                                 // Embed CIDToGIDMap
5983                                 // A specification of the mapping from CIDs to glyph indices
5984                                 // search and get CTG font file to embedd
5985                                 $ctgfile = strtolower($font['ctg']);
5986                                 // search and get ctg font file to embedd
5987                                 $fontfile = '';
5988                                 // search files on various directories
5989                                 if (file_exists($fontdir.$ctgfile)) {
5990                                         $fontfile = $fontdir.$ctgfile;
5991                                 } elseif (file_exists($this->_getfontpath().$ctgfile)) {
5992                                         $fontfile = $this->_getfontpath().$ctgfile;
5993                                 } elseif (file_exists($ctgfile)) {
5994                                         $fontfile = $ctgfile;
5995                                 }
5996                                 if ($this->empty_string($fontfile)) {
5997                                         $this->Error('Font file not found: '.$ctgfile);
5998                                 }
5999                                 $size = filesize($fontfile);
6000                                 $this->_out('<</Length '.$size.'');
6001                                 if (substr($fontfile, -2) == '.z') { // check file extension
6002                                         // Decompresses data encoded using the public-domain 
6003                                         // zlib/deflate compression method, reproducing the 
6004                                         // original text or binary data
6005                                         $this->_out('/Filter /FlateDecode');
6006                                 }
6007                                 $this->_out('>>');
6008                                 $this->_putstream(file_get_contents($fontfile));
6009                         }
6010                         $this->_out('endobj');
6011                 }
6012                 
6013                 /**
6014                  * Output CID-0 fonts.
6015                  * @param array $font font data
6016                  * @access protected
6017                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
6018                  * @since 3.2.000 (2008-06-23)
6019                  */
6020                 protected function _putcidfont0($font) {
6021                         $cidoffset = 31;
6022                         if (isset($font['cidinfo']['uni2cid'])) {
6023                                 // convert unicode to cid.
6024                                 $uni2cid = $font['cidinfo']['uni2cid'];
6025                                 $cw = array();
6026                                 foreach ($font['cw'] as $uni => $width) {
6027                                         if (isset($uni2cid[$uni])) {
6028                                                 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
6029                                         } elseif ($uni < 256) {
6030                                                 $cw[$uni] = $width;
6031                                         } // else unknown character
6032                                 }
6033                                 $font = array_merge($font, array('cw' => $cw));
6034                         }
6035                         $name = $font['name'];
6036                         $enc = $font['enc'];
6037                         if ($enc) {
6038                                 $longname = $name.'-'.$enc;
6039                         } else {
6040                                 $longname = $name;
6041                         }
6042                         $this->_newobj();
6043                         $this->_out('<</Type /Font');
6044                         $this->_out('/BaseFont /'.$longname);
6045                         $this->_out('/Subtype /Type0');
6046                         if ($enc) {
6047                                 $this->_out('/Encoding /'.$enc);
6048                         }
6049                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
6050                         $this->_out('>>');
6051                         $this->_out('endobj');
6052                         $this->_newobj();
6053                         $this->_out('<</Type /Font');
6054                         $this->_out('/BaseFont /'.$name);
6055                         $this->_out('/Subtype /CIDFontType0');
6056                         $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
6057                         $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
6058                         $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
6059                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
6060                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
6061                         $this->_out('/DW '.$font['dw']);
6062                         $this->_putfontwidths($font, $cidoffset);
6063                         $this->_out('>>');
6064                         $this->_out('endobj');
6065                         $this->_newobj();
6066                         $s = '<</Type /FontDescriptor /FontName /'.$name;
6067                         foreach ($font['desc'] as $k => $v) {
6068                                 if ($k != 'Style') {
6069                                         $s .= ' /'.$k.' '.$v.'';
6070                                 }
6071                         }
6072                         $this->_out($s.'>>');
6073                         $this->_out('endobj');
6074                 }
6075
6076                 /**
6077                  * Output images.
6078                  * @access protected
6079                  */
6080                 protected function _putimages() {
6081                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
6082                         foreach ($this->imagekeys as $file) {
6083                                 $info = $this->getImageBuffer($file);
6084                                 $this->_newobj();
6085                                 $this->setImageSubBuffer($file, 'n', $this->n);
6086                                 $this->_out('<</Type /XObject');
6087                                 $this->_out('/Subtype /Image');
6088                                 $this->_out('/Width '.$info['w']);
6089                                 $this->_out('/Height '.$info['h']);
6090                                 if (isset($info['masked'])) {
6091                                         $this->_out('/SMask '.($this->n - 1).' 0 R');
6092                                 }
6093                                 if ($info['cs'] == 'Indexed') {
6094                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
6095                                 } else {
6096                                         $this->_out('/ColorSpace /'.$info['cs']);
6097                                         if ($info['cs'] == 'DeviceCMYK') {
6098                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
6099                                         }
6100                                 }
6101                                 $this->_out('/BitsPerComponent '.$info['bpc']);
6102                                 if (isset($info['f'])) {
6103                                         $this->_out('/Filter /'.$info['f']);
6104                                 }
6105                                 if (isset($info['parms'])) {
6106                                         $this->_out($info['parms']);
6107                                 }
6108                                 if (isset($info['trns']) AND is_array($info['trns'])) {
6109                                         $trns='';
6110                                         $count_info = count($info['trns']);
6111                                         for ($i=0; $i < $count_info; ++$i) {
6112                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
6113                                         }
6114                                         $this->_out('/Mask ['.$trns.']');
6115                                 }
6116                                 $this->_out('/Length '.strlen($info['data']).'>>');
6117                                 $this->_putstream($info['data']);
6118                                 $this->_out('endobj');
6119                                 //Palette
6120                                 if ($info['cs'] == 'Indexed') {
6121                                         $this->_newobj();
6122                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
6123                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
6124                                         $this->_putstream($pal);
6125                                         $this->_out('endobj');
6126                                 }
6127                         }
6128                 }
6129
6130                 /**
6131                 * Output Spot Colors Resources.
6132                 * @access protected
6133                 * @since 4.0.024 (2008-09-12)
6134                 */
6135                 protected function _putspotcolors() {
6136                         foreach ($this->spot_colors as $name => $color) {
6137                                 $this->_newobj();
6138                                 $this->spot_colors[$name]['n'] = $this->n;
6139                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
6140                                 $this->_out('/DeviceCMYK <<');
6141                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
6142                                 $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
6143                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
6144                                 $this->_out('endobj');
6145                         }
6146                 }
6147
6148                 /**
6149                 * Output object dictionary for images.
6150                 * @access protected
6151                 */
6152                 protected function _putxobjectdict() {
6153                         foreach ($this->imagekeys as $file) {
6154                                 $info = $this->getImageBuffer($file);
6155                                 $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
6156                         }
6157                 }
6158
6159                 /**
6160                 * Output Resources Dictionary.
6161                 * @access protected
6162                 */
6163                 protected function _putresourcedict() {
6164                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
6165                         $this->_out('/Font <<');
6166                         foreach ($this->fontkeys as $fontkey) {
6167                                 $font = $this->getFontBuffer($fontkey);
6168                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
6169                         }
6170                         $this->_out('>>');
6171                         $this->_out('/XObject <<');
6172                         $this->_putxobjectdict();
6173                         $this->_out('>>');
6174                         // visibility
6175                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
6176                         // transparency
6177                         $this->_out('/ExtGState <<');
6178                         foreach ($this->extgstates as $k => $extgstate) {
6179                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
6180                         }
6181                         $this->_out('>>');
6182                         // gradients
6183                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
6184                                 $this->_out('/Shading <<');
6185                                 foreach ($this->gradients as $id => $grad) {
6186                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
6187                                 }
6188                                 $this->_out('>>');
6189                         }
6190                         // spot colors
6191                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
6192                                 $this->_out('/ColorSpace <<');
6193                                 foreach ($this->spot_colors as $color) {
6194                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
6195                                 }
6196                                 $this->_out('>>');
6197                         }
6198                 }
6199                 
6200                 /**
6201                 * Output Resources.
6202                 * @access protected
6203                 */
6204                 protected function _putresources() {
6205                         $this->_putextgstates();
6206                         $this->_putocg();
6207                         $this->_putfonts();
6208                         $this->_putimages();
6209                         $this->_putspotcolors();
6210                         $this->_putshaders();
6211                         //Resource dictionary
6212                         $this->offsets[2] = $this->bufferlen;
6213                         $this->_out('2 0 obj');
6214                         $this->_out('<<');
6215                         $this->_putresourcedict();
6216                         $this->_out('>>');
6217                         $this->_out('endobj');
6218                         $this->_putjavascript();
6219                         $this->_putbookmarks();
6220                         $this->_putEmbeddedFiles();
6221                         // encryption
6222                         if ($this->encrypted) {
6223                                 $this->_newobj();
6224                                 $this->enc_obj_id = $this->n;
6225                                 $this->_out('<<');
6226                                 $this->_putencryption();
6227                                 $this->_out('>>');
6228                                 $this->_out('endobj');
6229                         }
6230                 }
6231                 
6232                 /**
6233                 * Adds some Metadata information
6234                 * (see Chapter 10.2 of PDF Reference)
6235                 * @access protected
6236                 */
6237                 protected function _putinfo() {
6238                         if (!$this->empty_string($this->title)) {
6239                                 $this->_out('/Title '.$this->_textstring($this->title));
6240                         }
6241                         if (!$this->empty_string($this->author)) {
6242                                 $this->_out('/Author '.$this->_textstring($this->author));
6243                         }
6244                         if (!$this->empty_string($this->subject)) {
6245                                 $this->_out('/Subject '.$this->_textstring($this->subject));
6246                         }
6247                         if (!$this->empty_string($this->keywords)) {
6248                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
6249                         }
6250                         if (!$this->empty_string($this->creator)) {
6251                                 $this->_out('/Creator '.$this->_textstring($this->creator));
6252                         }
6253                         if (defined('PDF_PRODUCER')) {
6254                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
6255                         }
6256                         $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis')));
6257                         $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis')));       
6258                 }
6259                 
6260                 /**
6261                 * Output Catalog.
6262                 * @access protected
6263                 */
6264                 protected function _putcatalog() {
6265                         $this->_out('/Type /Catalog');
6266                         $this->_out('/Pages 1 0 R');
6267                         if ($this->ZoomMode == 'fullpage') {
6268                                 $this->_out('/OpenAction [3 0 R /Fit]');
6269                         } elseif ($this->ZoomMode == 'fullwidth') {
6270                                 $this->_out('/OpenAction [3 0 R /FitH null]');
6271                         } elseif ($this->ZoomMode == 'real') {
6272                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
6273                         } elseif (!is_string($this->ZoomMode)) {
6274                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
6275                         }
6276                         if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
6277                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
6278                         }
6279                         if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
6280                                 $this->_out('/PageMode /'.$this->PageMode);
6281                         }
6282                         if (isset($this->l['a_meta_language'])) {
6283                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
6284                         }
6285                         $this->_out('/Names <<');
6286                         if (!$this->empty_string($this->javascript)) {
6287                                 $this->_out('/JavaScript '.($this->n_js).' 0 R');
6288                         }
6289                         $this->_out('>>');
6290                         if (count($this->outlines) > 0) {
6291                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
6292                                 $this->_out('/PageMode /UseOutlines');
6293                         }
6294                         $this->_putviewerpreferences();
6295                         $p = $this->n_ocg_print.' 0 R';
6296                         $v = $this->n_ocg_view.' 0 R';
6297                         $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
6298                         $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
6299                 }
6300                 
6301                 /**
6302                 * Output viewer preferences.
6303                 * @author Nicola asuni
6304                 * @since 3.1.000 (2008-06-09)
6305                 * @access protected
6306                 */
6307                 protected function _putviewerpreferences() {
6308                         $this->_out('/ViewerPreferences<<');
6309                         if ($this->rtl) {
6310                                 $this->_out('/Direction /R2L');
6311                         } else {
6312                                 $this->_out('/Direction /L2R');
6313                         }
6314                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
6315                                 $this->_out('/HideToolbar true');
6316                         }
6317                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
6318                                 $this->_out('/HideMenubar true');
6319                         }
6320                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
6321                                 $this->_out('/HideWindowUI true');
6322                         }
6323                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
6324                                 $this->_out('/FitWindow true');
6325                         }
6326                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
6327                                 $this->_out('/CenterWindow true');
6328                         }
6329                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
6330                                 $this->_out('/DisplayDocTitle true');
6331                         }
6332                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
6333                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
6334                         }
6335                         if (isset($this->viewer_preferences['ViewArea'])) {
6336                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
6337                         }
6338                         if (isset($this->viewer_preferences['ViewClip'])) {
6339                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
6340                         }
6341                         if (isset($this->viewer_preferences['PrintArea'])) {
6342                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
6343                         }
6344                         if (isset($this->viewer_preferences['PrintClip'])) {
6345                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
6346                         }
6347                         if (isset($this->viewer_preferences['PrintScaling'])) {
6348                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
6349                         }
6350                         if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
6351                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
6352                         }
6353                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
6354                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
6355                                         $this->_out('/PickTrayByPDFSize true');
6356                                 } else {
6357                                         $this->_out('/PickTrayByPDFSize false');
6358                                 }
6359                         }
6360                         if (isset($this->viewer_preferences['PrintPageRange'])) {
6361                                 $PrintPageRangeNum = '';
6362                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
6363                                         $PrintPageRangeNum .= ' '.($v - 1).'';
6364                                 }
6365                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
6366                         }
6367                         if (isset($this->viewer_preferences['NumCopies'])) {
6368                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
6369                         }
6370                         $this->_out('>>');
6371                 }
6372
6373                 /**
6374                 * Output trailer.
6375                 * @access protected
6376                 */
6377                 protected function _puttrailer() {
6378                         $this->_out('/Size '.($this->n + 1));
6379                         $this->_out('/Root '.$this->n.' 0 R');
6380                         $this->_out('/Info '.($this->n - 1).' 0 R');
6381                         if ($this->encrypted) {
6382                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
6383                                 $this->_out('/ID [()()]');
6384                         }
6385                 }
6386
6387                 /**
6388                 * Output PDF header.
6389                 * @access protected
6390                 */
6391                 protected function _putheader() {
6392                         $this->_out('%PDF-'.$this->PDFVersion);
6393                 }
6394
6395                 /**
6396                 * Output end of document (EOF).
6397                 * @access protected
6398                 */
6399                 protected function _enddoc() {
6400                         $this->state = 1;
6401                         $this->_putheader();
6402                         $this->_putpages();
6403                         $this->_putresources();
6404                         //Info
6405                         $this->_newobj();
6406                         $this->_out('<<');
6407                         $this->_putinfo();
6408                         $this->_out('>>');
6409                         $this->_out('endobj');
6410                         //Catalog
6411                         $this->_newobj();
6412                         $this->_out('<<');
6413                         $this->_putcatalog();
6414                         $this->_putcertification();
6415                         $this->_putuserrights();
6416                         $this->_out('>>');
6417                         $this->_out('endobj');
6418                         //Cross-ref
6419                         $o = $this->bufferlen;
6420                         $this->_out('xref');
6421                         $this->_out('0 '.($this->n + 1));
6422                         $this->_out('0000000000 65535 f ');
6423                         for ($i=1; $i <= $this->n; ++$i) {
6424                                 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
6425                         }
6426                         if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
6427                                 $this->_out('100000 '.count($this->embeddedfiles));
6428                                 foreach ($this->embeddedfiles as $filename => $filedata) {
6429                                         $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
6430                                 }
6431                         }
6432                         //Trailer
6433                         $this->_out('trailer');
6434                         $this->_out('<<');
6435                         $this->_puttrailer();
6436                         $this->_out('>>');
6437                         $this->_out('startxref');
6438                         $this->_out($o);
6439                         $this->_out('%%EOF');
6440                         $this->state = 3; // end-of-doc
6441                         if ($this->diskcache) {
6442                                 // remove temporary files used for images
6443                                 foreach ($this->imagekeys as $key) {
6444                                         // remove temporary files
6445                                         unlink($this->images[$key]);
6446                                 }
6447                                 foreach ($this->fontkeys as $key) {
6448                                         // remove temporary files
6449                                         unlink($this->fonts[$key]);
6450                                 }
6451                         }
6452                 }
6453
6454                 /**
6455                 * Initialize a new page.
6456                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
6457                 * @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>
6458                 * @access protected
6459                 */
6460                 protected function _beginpage($orientation='', $format='') {
6461                         ++$this->page;
6462                         $this->setPageBuffer($this->page, '');
6463                         // initialize array for graphics tranformation positions inside a page buffer
6464                         $this->transfmrk[$this->page] = array();
6465                         $this->state = 2;
6466                         if ($this->empty_string($orientation)) {
6467                                 if (isset($this->CurOrientation)) {
6468                                         $orientation = $this->CurOrientation;
6469                                 } else {
6470                                         $orientation = 'P';
6471                                 }
6472                         }
6473                         if ($this->empty_string($format)) {
6474                                 $this->setPageOrientation($orientation);
6475                         } else {
6476                                 $this->setPageFormat($format, $orientation);
6477                         }
6478                         if ($this->rtl) {
6479                                 $this->x = $this->w - $this->rMargin;
6480                         } else {
6481                                 $this->x = $this->lMargin;
6482                         }
6483                         $this->y = $this->tMargin;
6484                         if (isset($this->newpagegroup[$this->page])) {
6485                                 // start a new group
6486                                 $n = sizeof($this->pagegroups) + 1;
6487                                 $alias = '{nb'.$n.'}';
6488                                 $this->pagegroups[$alias] = 1;
6489                                 $this->currpagegroup = $alias;
6490                         } elseif ($this->currpagegroup) {
6491                                 ++$this->pagegroups[$this->currpagegroup];
6492                         }
6493                 }
6494
6495                 /**
6496                 * Mark end of page.
6497                 * @access protected
6498                 */
6499                 protected function _endpage() {
6500                         $this->setVisibility('all');
6501                         $this->state = 1;
6502                 }
6503
6504                 /**
6505                 * Begin a new object.
6506                 * @access protected
6507                 */
6508                 protected function _newobj() {
6509                         ++$this->n;
6510                         $this->offsets[$this->n] = $this->bufferlen;
6511                         $this->_out($this->n.' 0 obj');
6512                 }
6513
6514                 /**
6515                 * Underline text.
6516                 * @param int $x X coordinate
6517                 * @param int $y Y coordinate
6518                 * @param string $txt text to underline
6519                 * @access protected
6520                 */
6521                 protected function _dounderline($x, $y, $txt) {
6522                         $up = $this->CurrentFont['up'];
6523                         $ut = $this->CurrentFont['ut'];
6524                         $w = $this->GetStringWidth($txt);
6525                         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);
6526                 }
6527                 
6528                 /**
6529                 * Line through text.
6530                 * @param int $x X coordinate
6531                 * @param int $y Y coordinate
6532                 * @param string $txt text to linethrough
6533                 * @access protected
6534                 */
6535                 protected function _dolinethrough($x, $y, $txt) {
6536                         $up = $this->CurrentFont['up'];
6537                         $ut = $this->CurrentFont['ut'];
6538                         $w = $this->GetStringWidth($txt);
6539                         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);
6540                 }
6541                 
6542                 /**
6543                 * Read a 4-byte integer from file.
6544                 * @param string $f file name.
6545                 * @return 4-byte integer
6546                 * @access protected
6547                 */
6548                 protected function _freadint($f) {
6549                         $a = unpack('Ni', fread($f, 4));
6550                         return $a['i'];
6551                 }
6552                 
6553                 /**
6554                 * Add "\" before "\", "(" and ")"
6555                 * @param string $s string to escape.
6556                 * @return string escaped string.
6557                 * @access protected
6558                 */
6559                 protected function _escape($s) {
6560                         // the chr(13) substitution fixes the Bugs item #1421290.
6561                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
6562                 }
6563                 
6564                 /**
6565                 * Format a date string for meta information
6566                 * @param string $s date string to escape.
6567                 * @return string escaped string.
6568                 * @access protected
6569                 */
6570                 protected function _datastring($s) {
6571                         if ($this->encrypted) {
6572                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
6573                         }
6574                         return '('. $this->_escape($s).')';
6575                 }
6576                 
6577                 /**
6578                 * Format a text string for meta information
6579                 * @param string $s string to escape.
6580                 * @return string escaped string.
6581                 * @access protected
6582                 */
6583                 protected function _textstring($s) {
6584                         if ($this->isunicode) {
6585                                 //Convert string to UTF-16BE
6586                                 $s = $this->UTF8ToUTF16BE($s, true);
6587                         }
6588                         return $this->_datastring($s);
6589                 }
6590                                 
6591                 /**
6592                 * Format a text string
6593                 * @param string $s string to escape.
6594                 * @return string escaped string.
6595                 * @access protected
6596                 */
6597                 protected function _escapetext($s) {
6598                         if ($this->isunicode) {
6599                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
6600                                         $s = $this->UTF8ToLatin1($s);
6601                                 } else {
6602                                         //Convert string to UTF-16BE and reverse RTL language
6603                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
6604                                 }
6605                         }
6606                         return $this->_escape($s);
6607                 }
6608                 
6609                 /**
6610                 * Output a stream.
6611                 * @param string $s string to output.
6612                 * @access protected
6613                 */
6614                 protected function _putstream($s) {
6615                         if ($this->encrypted) {
6616                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
6617                         }
6618                         $this->_out('stream');
6619                         $this->_out($s);
6620                         $this->_out('endstream');
6621                 }
6622                 
6623                 /**
6624                 * Output a string to the document.
6625                 * @param string $s string to output.
6626                 * @access protected
6627                 */
6628                 protected function _out($s) {
6629                         if ($this->state == 2) {
6630                                 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
6631                                         // puts data before page footer
6632                                         $pagebuff = $this->getPageBuffer($this->page);
6633                                         $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
6634                                         $footer = substr($pagebuff, -$this->footerlen[$this->page]);
6635                                         $this->setPageBuffer($this->page, $page.$s."\n".$footer);
6636                                         // update footer position
6637                                         $this->footerpos[$this->page] += strlen($s."\n");       
6638                                 } else {
6639                                         $this->setPageBuffer($this->page, $s."\n", true);
6640                                 }
6641                         } else {
6642                                 $this->setBuffer($s."\n");
6643                         }
6644                 }
6645                 
6646                  /**
6647                  * Converts UTF-8 strings to codepoints array.<br>
6648                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
6649                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
6650                  * <pre>
6651                  *        Char. number range  |        UTF-8 octet sequence
6652                  *       (hexadecimal)    |              (binary)
6653                  *    --------------------+-----------------------------------------------
6654                  *    0000 0000-0000 007F | 0xxxxxxx
6655                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
6656                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
6657                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
6658                  *    ---------------------------------------------------------------------
6659                  *
6660                  *   ABFN notation:
6661                  *   ---------------------------------------------------------------------
6662                  *   UTF8-octets = *( UTF8-char )
6663                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
6664                  *   UTF8-1      = %x00-7F
6665                  *   UTF8-2      = %xC2-DF UTF8-tail
6666                  *
6667                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
6668                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
6669                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
6670                  *                 %xF4 %x80-8F 2( UTF8-tail )
6671                  *   UTF8-tail   = %x80-BF
6672                  *   ---------------------------------------------------------------------
6673                  * </pre>
6674                  * @param string $str string to process.
6675                  * @return array containing codepoints (UTF-8 characters values)
6676                  * @access protected
6677                  * @author Nicola Asuni
6678                  * @since 1.53.0.TC005 (2005-01-05)
6679                  */
6680                 protected function UTF8StringToArray($str) {
6681                         if (isset($this->cache_UTF8StringToArray['_'.$str])) {
6682                                 // return cached value
6683                                 return($this->cache_UTF8StringToArray['_'.$str]);
6684                         }
6685                         // check cache size
6686                         if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
6687                                 // remove first element
6688                                 array_shift($this->cache_UTF8StringToArray);
6689                         }
6690                         ++$this->cache_size_UTF8StringToArray;
6691                         if (!$this->isunicode) {
6692                                 // split string into array of equivalent codes
6693                                 $strarr = array();
6694                                 $strlen = strlen($str);
6695                                 for ($i=0; $i < $strlen; ++$i) {
6696                                         $strarr[] = ord($str{$i});
6697                                 }
6698                                 // insert new value on cache
6699                                 $this->cache_UTF8StringToArray['_'.$str] = $strarr;
6700                                 return $strarr;
6701                         }
6702                         $unicode = array(); // array containing unicode values
6703                         $bytes  = array(); // array containing single character byte sequences
6704                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
6705                         $str .= ''; // force $str to be a string
6706                         $length = strlen($str);
6707                         for ($i = 0; $i < $length; ++$i) {
6708                                 $char = ord($str{$i}); // get one string character at time
6709                                 if (count($bytes) == 0) { // get starting octect
6710                                         if ($char <= 0x7F) {
6711                                                 $unicode[] = $char; // use the character "as is" because is ASCII
6712                                                 $numbytes = 1;
6713                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
6714                                                 $bytes[] = ($char - 0xC0) << 0x06; 
6715                                                 $numbytes = 2;
6716                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
6717                                                 $bytes[] = ($char - 0xE0) << 0x0C; 
6718                                                 $numbytes = 3;
6719                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
6720                                                 $bytes[] = ($char - 0xF0) << 0x12; 
6721                                                 $numbytes = 4;
6722                                         } else {
6723                                                 // use replacement character for other invalid sequences
6724                                                 $unicode[] = 0xFFFD;
6725                                                 $bytes = array();
6726                                                 $numbytes = 1;
6727                                         }
6728                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
6729                                         $bytes[] = $char - 0x80;
6730                                         if (count($bytes) == $numbytes) {
6731                                                 // compose UTF-8 bytes to a single unicode value
6732                                                 $char = $bytes[0];
6733                                                 for ($j = 1; $j < $numbytes; ++$j) {
6734                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
6735                                                 }
6736                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
6737                                                         /* The definition of UTF-8 prohibits encoding character numbers between
6738                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
6739                                                         encoding form (as surrogate pairs) and do not directly represent
6740                                                         characters. */
6741                                                         $unicode[] = 0xFFFD; // use replacement character
6742                                                 } else {
6743                                                         $unicode[] = $char; // add char to array
6744                                                 }
6745                                                 // reset data for next char
6746                                                 $bytes = array(); 
6747                                                 $numbytes = 1;
6748                                         }
6749                                 } else {
6750                                         // use replacement character for other invalid sequences
6751                                         $unicode[] = 0xFFFD;
6752                                         $bytes = array();
6753                                         $numbytes = 1;
6754                                 }
6755                         }
6756                         // insert new value on cache
6757                         $this->cache_UTF8StringToArray['_'.$str] = $unicode;
6758                         return $unicode;
6759                 }
6760                 
6761                 /**
6762                  * Converts UTF-8 strings to UTF16-BE.<br>
6763                  * @param string $str string to process.
6764                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
6765                  * @return string
6766                  * @access protected
6767                  * @author Nicola Asuni
6768                  * @since 1.53.0.TC005 (2005-01-05)
6769                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
6770                  */
6771                 protected function UTF8ToUTF16BE($str, $setbom=true) {
6772                         if (!$this->isunicode) {
6773                                 return $str; // string is not in unicode
6774                         }
6775                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6776                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
6777                 }
6778                 
6779                 /**
6780                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
6781                  * @param string $str string to process.
6782                  * @return string
6783                  * @author Andrew Whitehead, Nicola Asuni
6784                  * @access protected
6785                  * @since 3.2.000 (2008-06-23)
6786                  */
6787                 protected function UTF8ToLatin1($str) {
6788                         global $utf8tolatin;
6789                         if (!$this->isunicode) {
6790                                 return $str; // string is not in unicode
6791                         }
6792                         $outstr = ''; // string to be returned
6793                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6794                         foreach ($unicode as $char) {
6795                                 if ($char < 256) {
6796                                         $outstr .= chr($char);
6797                                 } elseif (array_key_exists($char, $utf8tolatin)) {
6798                                         // map from UTF-8
6799                                         $outstr .= chr($utf8tolatin[$char]);
6800                                 } elseif ($char == 0xFFFD) {
6801                                         // skip
6802                                 } else {
6803                                         $outstr .= '?';
6804                                 }
6805                         }
6806                         return $outstr;
6807                 }
6808
6809                 /**
6810                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
6811                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
6812                  * <pre>
6813                  *   Encoding UTF-16:
6814                  * 
6815                  *   Encoding of a single character from an ISO 10646 character value to
6816                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
6817                  *    than 0x10FFFF.
6818                  * 
6819                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
6820                  *       terminate.
6821                  * 
6822                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
6823                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
6824                  *       represented in 20 bits.
6825                  * 
6826                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
6827                  *       0xDC00, respectively. These integers each have 10 bits free to
6828                  *       encode the character value, for a total of 20 bits.
6829                  * 
6830                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
6831                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
6832                  *       bits of W2. Terminate.
6833                  * 
6834                  *    Graphically, steps 2 through 4 look like:
6835                  *    U' = yyyyyyyyyyxxxxxxxxxx
6836                  *    W1 = 110110yyyyyyyyyy
6837                  *    W2 = 110111xxxxxxxxxx
6838                  * </pre>
6839                  * @param array $unicode array containing UTF-8 unicode values
6840                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
6841                  * @return string
6842                  * @access protected
6843                  * @author Nicola Asuni
6844                  * @since 2.1.000 (2008-01-08)
6845                  * @see UTF8ToUTF16BE()
6846                  */
6847                 protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
6848                         $outstr = ''; // string to be returned
6849                         if ($setbom) {
6850                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
6851                         }
6852                         foreach ($unicode as $char) {
6853                                 if ($char == 0xFFFD) {
6854                                         $outstr .= "\xFF\xFD"; // replacement character
6855                                 } elseif ($char < 0x10000) {
6856                                         $outstr .= chr($char >> 0x08);
6857                                         $outstr .= chr($char & 0xFF);
6858                                 } else {
6859                                         $char -= 0x10000;
6860                                         $w1 = 0xD800 | ($char >> 0x10);
6861                                         $w2 = 0xDC00 | ($char & 0x3FF); 
6862                                         $outstr .= chr($w1 >> 0x08);
6863                                         $outstr .= chr($w1 & 0xFF);
6864                                         $outstr .= chr($w2 >> 0x08);
6865                                         $outstr .= chr($w2 & 0xFF);
6866                                 }
6867                         }
6868                         return $outstr;
6869                 }
6870                 // ====================================================
6871                 
6872                 /**
6873                  * Set header font.
6874                  * @param array $font font
6875                  * @access public
6876                  * @since 1.1
6877                  */
6878                 public function setHeaderFont($font) {
6879                         $this->header_font = $font;
6880                 }
6881                 
6882                 /**
6883                  * Get header font.
6884                  * @return array()
6885                  * @access public
6886                  * @since 4.0.012 (2008-07-24)
6887                  */
6888                 public function getHeaderFont() {
6889                         return $this->header_font;
6890                 }
6891                 
6892                 /**
6893                  * Set footer font.
6894                  * @param array $font font
6895                  * @access public
6896                  * @since 1.1
6897                  */
6898                 public function setFooterFont($font) {
6899                         $this->footer_font = $font;
6900                 }
6901                 
6902                 /**
6903                  * Get Footer font.
6904                  * @return array()
6905                  * @access public
6906                  * @since 4.0.012 (2008-07-24)
6907                  */
6908                 public function getFooterFont() {
6909                         return $this->footer_font;
6910                 }
6911                 
6912                 /**
6913                  * Set language array.
6914                  * @param array $language
6915                  * @access public
6916                  * @since 1.1
6917                  */
6918                 public function setLanguageArray($language) {
6919                         $this->l = $language;
6920                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
6921                 }
6922                 
6923                 /**
6924                  * Returns the PDF data.
6925                  * @access public
6926                  */
6927                 public function getPDFData() {
6928                         if ($this->state < 3) {
6929                                 $this->Close();
6930                         }
6931                         return $this->buffer;
6932                 }
6933                                 
6934                 /**
6935                  * Output anchor link.
6936                  * @param string $url link URL or internal link (i.e.: &lt;a href="#23"&gt;link to page 23&lt;/a&gt;)
6937                  * @param string $name link name
6938                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
6939                  * @param boolean $firstline if true prints only the first line and return the remaining string.
6940                  * @param array $color array of RGB text color
6941                  * @param string $style font style (U, D, B, I)
6942                  * @return the number of cells used or the remaining text if $firstline = true;
6943                  * @access public
6944                  */
6945                 public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
6946                         if (!$this->empty_string($url) AND ($url{0} == '#')) {
6947                                 // convert url to internal link
6948                                 $page = intval(substr($url, 1));
6949                                 $url = $this->AddLink();
6950                                 $this->SetLink($url, 0, $page);
6951                         }
6952                         // store current settings
6953                         $prevcolor = $this->fgcolor;
6954                         $prevstyle = $this->FontStyle;
6955                         if (empty($color)) {
6956                                 $this->SetTextColorArray($this->htmlLinkColorArray);
6957                         } else {
6958                                 $this->SetTextColorArray($color);
6959                         }
6960                         if ($style == -1) {
6961                                 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
6962                         } else {
6963                                 $this->SetFont('', $this->FontStyle.$style);
6964                         }
6965                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
6966                         // restore settings
6967                         $this->SetFont('', $prevstyle);
6968                         $this->SetTextColorArray($prevcolor);
6969                         return $ret;
6970                 }
6971                 
6972                 /**
6973                  * 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).
6974                  * @param string $color html color 
6975                  * @return array RGB color or false in case of error.
6976                  * @access public
6977                  */             
6978                 public function convertHTMLColorToDec($color='#FFFFFF') {
6979                         global $webcolor;
6980                         $returncolor = false;
6981                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
6982                         $color = strtolower($color);
6983                         if (strlen($color) == 0) {
6984                                 return false;
6985                         }
6986                         if (substr($color, 0, 3) == 'rgb') {
6987                                 $codes = substr($color, 4);
6988                                 $codes = str_replace(')', '', $codes);
6989                                 $returncolor = explode(',', $codes, 3);
6990                                 return $returncolor;
6991                         }
6992                         if (substr($color, 0, 1) != '#') {
6993                                 // decode color name
6994                                 if (isset($webcolor[$color])) {
6995                                         $color_code = $webcolor[$color];
6996                                 } else {
6997                                         return false;
6998                                 }
6999                         } else {
7000                                 $color_code = substr($color, 1);
7001                         }
7002                         switch (strlen($color_code)) {
7003                                 case 3: {
7004                                         // three-digit hexadecimal representation
7005                                         $r = substr($color_code, 0, 1);
7006                                         $g = substr($color_code, 1, 1);
7007                                         $b = substr($color_code, 2, 1);
7008                                         $returncolor['R'] = hexdec($r.$r);
7009                                         $returncolor['G'] = hexdec($g.$g);
7010                                         $returncolor['B'] = hexdec($b.$b);
7011                                         break;
7012                                 }
7013                                 case 6: {
7014                                         // six-digit hexadecimal representation
7015                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
7016                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
7017                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
7018                                         break;
7019                                 }
7020                         }
7021                         return $returncolor;
7022                 }
7023                 
7024                 /**
7025                  * Converts pixels to User's Units.
7026                  * @param int $px pixels
7027                  * @return float value in user's unit
7028                  * @access public
7029                  * @see setImageScale(), getImageScale()
7030                  */
7031                 public function pixelsToUnits($px) {
7032                         return ($px / ($this->imgscale * $this->k));
7033                 }
7034                         
7035                 /**
7036                  * Reverse function for htmlentities. 
7037                  * Convert entities in UTF-8.
7038                  * @param $text_to_convert Text to convert.
7039                  * @return string converted
7040                  * @access public
7041                 */
7042                 public function unhtmlentities($text_to_convert) {
7043                         return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
7044                 }
7045                 
7046                 // ENCRYPTION METHODS ----------------------------------
7047                 // SINCE 2.0.000 (2008-01-02)
7048                 
7049                 /**
7050                 * Compute encryption key depending on object number where the encrypted data is stored
7051                 * @param int $n object number
7052                 * @access protected
7053                 * @since 2.0.000 (2008-01-02)
7054                 */
7055                 protected function _objectkey($n) {
7056                         return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
7057                 }
7058                 
7059                 /**
7060                  * Put encryption on PDF document.
7061                  * @access protected
7062                  * @since 2.0.000 (2008-01-02)
7063                  */
7064                 protected function _putencryption() {
7065                         $this->_out('/Filter /Standard');
7066                         $this->_out('/V 1');
7067                         $this->_out('/R 2');
7068                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
7069                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
7070                         $this->_out('/P '.$this->Pvalue);
7071                 }
7072                 
7073                 /**
7074                 * Returns the input text exrypted using RC4 algorithm and the specified key.
7075                 * RC4 is the standard encryption algorithm used in PDF format
7076                 * @param string $key encryption key
7077                 * @param String $text input text to be encrypted
7078                 * @return String encrypted text
7079                 * @access protected
7080                 * @since 2.0.000 (2008-01-02)
7081                 * @author Klemen Vodopivec
7082                 */
7083                 protected function _RC4($key, $text) {
7084                         if ($this->last_rc4_key != $key) {
7085                                 $k = str_repeat($key, ((256 / strlen($key)) + 1));
7086                                 $rc4 = range(0, 255);
7087                                 $j = 0;
7088                                 for ($i = 0; $i < 256; ++$i) {
7089                                         $t = $rc4[$i];
7090                                         $j = ($j + $t + ord($k{$i})) % 256;
7091                                         $rc4[$i] = $rc4[$j];
7092                                         $rc4[$j] = $t;
7093                                 }
7094                                 $this->last_rc4_key = $key;
7095                                 $this->last_rc4_key_c = $rc4;
7096                         } else {
7097                                 $rc4 = $this->last_rc4_key_c;
7098                         }
7099                         $len = strlen($text);
7100                         $a = 0;
7101                         $b = 0;
7102                         $out = '';
7103                         for ($i = 0; $i < $len; ++$i) {
7104                                 $a = ($a + 1) % 256;
7105                                 $t = $rc4[$a];
7106                                 $b = ($b + $t) % 256;
7107                                 $rc4[$a] = $rc4[$b];
7108                                 $rc4[$b] = $t;
7109                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
7110                                 $out .= chr(ord($text{$i}) ^ $k);
7111                         }
7112                         return $out;
7113                 }
7114                 
7115                 /**
7116                 * Encrypts a string using MD5 and returns it's value as a binary string.
7117                 * @param string $str input string
7118                 * @return String MD5 encrypted binary string
7119                 * @access protected
7120                 * @since 2.0.000 (2008-01-02)
7121                 * @author Klemen Vodopivec
7122                 */
7123                 protected function _md5_16($str) {
7124                         return pack('H*', md5($str));
7125                 }
7126                 
7127                 /**
7128                 * Compute O value (used for RC4 encryption)
7129                 * @param String $user_pass user password
7130                 * @param String $owner_pass user password
7131                 * @return String O value
7132                 * @access protected
7133                 * @since 2.0.000 (2008-01-02)
7134                 * @author Klemen Vodopivec
7135                 */
7136                 protected function _Ovalue($user_pass, $owner_pass) {
7137                         $tmp = $this->_md5_16($owner_pass);
7138                         $owner_RC4_key = substr($tmp, 0, 5);
7139                         return $this->_RC4($owner_RC4_key, $user_pass);
7140                 }
7141                 
7142                 /**
7143                 * Compute U value (used for RC4 encryption)
7144                 * @return String U value
7145                 * @access protected
7146                 * @since 2.0.000 (2008-01-02)
7147                 * @author Klemen Vodopivec
7148                 */
7149                 protected function _Uvalue() {
7150                         return $this->_RC4($this->encryption_key, $this->padding);
7151                 }
7152                 
7153                 /**
7154                 * Compute encryption key
7155                 * @param String $user_pass user password
7156                 * @param String $owner_pass user password
7157                 * @param String $protection protection type
7158                 * @access protected
7159                 * @since 2.0.000 (2008-01-02)
7160                 * @author Klemen Vodopivec
7161                 */
7162                 protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
7163                         // Pad passwords
7164                         $user_pass = substr($user_pass.$this->padding, 0, 32);
7165                         $owner_pass = substr($owner_pass.$this->padding, 0, 32);
7166                         // Compute O value
7167                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
7168                         // Compute encyption key
7169                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
7170                         $this->encryption_key = substr($tmp, 0, 5);
7171                         // Compute U value
7172                         $this->Uvalue = $this->_Uvalue();
7173                         // Compute P value
7174                         $this->Pvalue = -(($protection^255) + 1);
7175                 }
7176                 
7177                 /**
7178                 * Set document protection
7179                 * The permission array is composed of values taken from the following ones:
7180                 * - copy: copy text and images to the clipboard
7181                 * - print: print the document
7182                 * - modify: modify it (except for annotations and forms)
7183                 * - annot-forms: add annotations and forms 
7184                 * Remark: the protection against modification is for people who have the full Acrobat product.
7185                 * 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.
7186                 * 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.
7187                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
7188                 * @param String $user_pass user password. Empty by default.
7189                 * @param String $owner_pass owner password. If not specified, a random value is used.
7190                 * @access public
7191                 * @since 2.0.000 (2008-01-02)
7192                 * @author Klemen Vodopivec
7193                 */
7194                 public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
7195                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
7196                         $protection = 192;
7197                         foreach ($permissions as $permission) {
7198                                 if (!isset($options[$permission])) {
7199                                         $this->Error('Incorrect permission: '.$permission);
7200                                 }
7201                                 $protection += $options[$permission];
7202                         }
7203                         if ($owner_pass === null) {
7204                                 $owner_pass = uniqid(rand());
7205                         }
7206                         $this->encrypted = true;
7207                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
7208                 }
7209                 
7210                 // END OF ENCRYPTION FUNCTIONS -------------------------
7211                 
7212                 // START TRANSFORMATIONS SECTION -----------------------
7213                 
7214                 /**
7215                 * Starts a 2D tranformation saving current graphic state.
7216                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
7217                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
7218                 * @access public
7219                 * @since 2.1.000 (2008-01-07)
7220                 * @see StartTransform(), StopTransform()
7221                 */
7222                 public function StartTransform() {
7223                         $this->_out('q');
7224                         $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
7225                 }
7226                 
7227                 /**
7228                 * Stops a 2D tranformation restoring previous graphic state.
7229                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
7230                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
7231                 * @access public
7232                 * @since 2.1.000 (2008-01-07)
7233                 * @see StartTransform(), StopTransform()
7234                 */
7235                 public function StopTransform() {
7236                         $this->_out('Q');
7237                         if (isset($this->transfmatrix)) {
7238                                 array_pop($this->transfmatrix);
7239                         }
7240                         array_pop($this->transfmrk[$this->page]);
7241                 }
7242                 /**
7243                 * Horizontal Scaling.
7244                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
7245                 * @param int $x abscissa of the scaling center. Default is current x position
7246                 * @param int $y ordinate of the scaling center. Default is current y position
7247                 * @access public
7248                 * @since 2.1.000 (2008-01-07)
7249                 * @see StartTransform(), StopTransform()
7250                 */
7251                 public function ScaleX($s_x, $x='', $y='') {
7252                         $this->Scale($s_x, 100, $x, $y);
7253                 }
7254                 
7255                 /**
7256                 * Vertical Scaling.
7257                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
7258                 * @param int $x abscissa of the scaling center. Default is current x position
7259                 * @param int $y ordinate of the scaling center. Default is current y position
7260                 * @access public
7261                 * @since 2.1.000 (2008-01-07)
7262                 * @see StartTransform(), StopTransform()
7263                 */
7264                 public function ScaleY($s_y, $x='', $y='') {
7265                         $this->Scale(100, $s_y, $x, $y);
7266                 }
7267                 
7268                 /**
7269                 * Vertical and horizontal proportional Scaling.
7270                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
7271                 * @param int $x abscissa of the scaling center. Default is current x position
7272                 * @param int $y ordinate of the scaling center. Default is current y position
7273                 * @access public
7274                 * @since 2.1.000 (2008-01-07)
7275                 * @see StartTransform(), StopTransform()
7276                 */
7277                 public function ScaleXY($s, $x='', $y='') {
7278                         $this->Scale($s, $s, $x, $y);
7279                 }
7280                 
7281                 /**
7282                 * Vertical and horizontal non-proportional Scaling.
7283                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
7284                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
7285                 * @param int $x abscissa of the scaling center. Default is current x position
7286                 * @param int $y ordinate of the scaling center. Default is current y position
7287                 * @access public
7288                 * @since 2.1.000 (2008-01-07)
7289                 * @see StartTransform(), StopTransform()
7290                 */
7291                 public function Scale($s_x, $s_y, $x='', $y='') {
7292                         if ($x === '') {
7293                                 $x = $this->x;
7294                         }
7295                         if ($y === '') {
7296                                 $y = $this->y;
7297                         }
7298                         if ($this->rtl) {
7299                                 $x = $this->w - $x;
7300                         }
7301                         if (($s_x == 0) OR ($s_y == 0)) {
7302                                 $this->Error('Please do not use values equal to zero for scaling');
7303                         }
7304                         $y = ($this->h - $y) * $this->k;
7305                         $x *= $this->k;
7306                         //calculate elements of transformation matrix
7307                         $s_x /= 100;
7308                         $s_y /= 100;
7309                         $tm[0] = $s_x;
7310                         $tm[1] = 0;
7311                         $tm[2] = 0;
7312                         $tm[3] = $s_y;
7313                         $tm[4] = $x * (1 - $s_x);
7314                         $tm[5] = $y * (1 - $s_y);
7315                         //scale the coordinate system
7316                         $this->Transform($tm);
7317                 }
7318                 
7319                 /**
7320                 * Horizontal Mirroring.
7321                 * @param int $x abscissa of the point. Default is current x position
7322                 * @access public
7323                 * @since 2.1.000 (2008-01-07)
7324                 * @see StartTransform(), StopTransform()
7325                 */
7326                 public function MirrorH($x='') {
7327                         $this->Scale(-100, 100, $x);
7328                 }
7329                 
7330                 /**
7331                 * Verical Mirroring.
7332                 * @param int $y ordinate of the point. Default is current y position
7333                 * @access public
7334                 * @since 2.1.000 (2008-01-07)
7335                 * @see StartTransform(), StopTransform()
7336                 */
7337                 public function MirrorV($y='') {
7338                         $this->Scale(100, -100, '', $y);
7339                 }
7340                 
7341                 /**
7342                 * Point reflection mirroring.
7343                 * @param int $x abscissa of the point. Default is current x position
7344                 * @param int $y ordinate of the point. Default is current y position
7345                 * @access public
7346                 * @since 2.1.000 (2008-01-07)
7347                 * @see StartTransform(), StopTransform()
7348                 */
7349                 public function MirrorP($x='',$y='') {
7350                         $this->Scale(-100, -100, $x, $y);
7351                 }
7352                 
7353                 /**
7354                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
7355                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
7356                 * @param int $x abscissa of the point. Default is current x position
7357                 * @param int $y ordinate of the point. Default is current y position
7358                 * @access public
7359                 * @since 2.1.000 (2008-01-07)
7360                 * @see StartTransform(), StopTransform()
7361                 */
7362                 public function MirrorL($angle=0, $x='',$y='') {
7363                         $this->Scale(-100, 100, $x, $y);
7364                         $this->Rotate(-2*($angle-90), $x, $y);
7365                 }
7366                 
7367                 /**
7368                 * Translate graphic object horizontally.
7369                 * @param int $t_x movement to the right (or left for RTL)
7370                 * @access public
7371                 * @since 2.1.000 (2008-01-07)
7372                 * @see StartTransform(), StopTransform()
7373                 */
7374                 public function TranslateX($t_x) {
7375                         $this->Translate($t_x, 0);
7376                 }
7377                 
7378                 /**
7379                 * Translate graphic object vertically.
7380                 * @param int $t_y movement to the bottom
7381                 * @access public
7382                 * @since 2.1.000 (2008-01-07)
7383                 * @see StartTransform(), StopTransform()
7384                 */
7385                 public function TranslateY($t_y) {
7386                         $this->Translate(0, $t_y);
7387                 }
7388                 
7389                 /**
7390                 * Translate graphic object horizontally and vertically.
7391                 * @param int $t_x movement to the right
7392                 * @param int $t_y movement to the bottom
7393                 * @access public
7394                 * @since 2.1.000 (2008-01-07)
7395                 * @see StartTransform(), StopTransform()
7396                 */
7397                 public function Translate($t_x, $t_y) {
7398                         if ($this->rtl) {
7399                                 $t_x = -$t_x;
7400                         }
7401                         //calculate elements of transformation matrix
7402                         $tm[0] = 1;
7403                         $tm[1] = 0;
7404                         $tm[2] = 0;
7405                         $tm[3] = 1;
7406                         $tm[4] = $t_x * $this->k;
7407                         $tm[5] = -$t_y * $this->k;
7408                         //translate the coordinate system
7409                         $this->Transform($tm);
7410                 }
7411                 
7412                 /**
7413                 * Rotate object.
7414                 * @param float $angle angle in degrees for counter-clockwise rotation
7415                 * @param int $x abscissa of the rotation center. Default is current x position
7416                 * @param int $y ordinate of the rotation center. Default is current y position
7417                 * @access public
7418                 * @since 2.1.000 (2008-01-07)
7419                 * @see StartTransform(), StopTransform()
7420                 */
7421                 public function Rotate($angle, $x='', $y='') {
7422                         if ($x === '') {
7423                                 $x = $this->x;
7424                         }
7425                         if ($y === '') {
7426                                 $y = $this->y;
7427                         }
7428                         if ($this->rtl) {
7429                                 $x = $this->w - $x;
7430                                 $angle = -$angle;
7431                         }
7432                         $y = ($this->h - $y) * $this->k;
7433                         $x *= $this->k;
7434                         //calculate elements of transformation matrix
7435                         $tm[0] = cos(deg2rad($angle));
7436                         $tm[1] = sin(deg2rad($angle));
7437                         $tm[2] = -$tm[1];
7438                         $tm[3] = $tm[0];
7439                         $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
7440                         $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
7441                         //rotate the coordinate system around ($x,$y)
7442                         $this->Transform($tm);
7443                 }
7444                 
7445                 /**
7446                 * Skew horizontally.
7447                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
7448                 * @param int $x abscissa of the skewing center. default is current x position
7449                 * @param int $y ordinate of the skewing center. default is current y position
7450                 * @access public
7451                 * @since 2.1.000 (2008-01-07)
7452                 * @see StartTransform(), StopTransform()
7453                 */
7454                 public function SkewX($angle_x, $x='', $y='') {
7455                         $this->Skew($angle_x, 0, $x, $y);
7456                 }
7457                 
7458                 /**
7459                 * Skew vertically.
7460                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
7461                 * @param int $x abscissa of the skewing center. default is current x position
7462                 * @param int $y ordinate of the skewing center. default is current y position
7463                 * @access public
7464                 * @since 2.1.000 (2008-01-07)
7465                 * @see StartTransform(), StopTransform()
7466                 */
7467                 public function SkewY($angle_y, $x='', $y='') {
7468                         $this->Skew(0, $angle_y, $x, $y);
7469                 }
7470                 
7471                 /**
7472                 * Skew.
7473                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
7474                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
7475                 * @param int $x abscissa of the skewing center. default is current x position
7476                 * @param int $y ordinate of the skewing center. default is current y position
7477                 * @access public
7478                 * @since 2.1.000 (2008-01-07)
7479                 * @see StartTransform(), StopTransform()
7480                 */
7481                 public function Skew($angle_x, $angle_y, $x='', $y='') {
7482                         if ($x === '') {
7483                                 $x = $this->x;
7484                         }
7485                         if ($y === '') {
7486                                 $y = $this->y;
7487                         }
7488                         if ($this->rtl) {
7489                                 $x = $this->w - $x;
7490                                 $angle_x = -$angle_x;
7491                         }
7492                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
7493                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
7494                         }
7495                         $x *= $this->k;
7496                         $y = ($this->h - $y) * $this->k;
7497                         //calculate elements of transformation matrix
7498                         $tm[0] = 1;
7499                         $tm[1] = tan(deg2rad($angle_y));
7500                         $tm[2] = tan(deg2rad($angle_x));
7501                         $tm[3] = 1;
7502                         $tm[4] = -$tm[2] * $y;
7503                         $tm[5] = -$tm[1] * $x;
7504                         //skew the coordinate system
7505                         $this->Transform($tm);
7506                 }
7507                 
7508                 /**
7509                 * Apply graphic transformations.
7510                 * @access protected
7511                 * @since 2.1.000 (2008-01-07)
7512                 * @see StartTransform(), StopTransform()
7513                 */
7514                 protected function Transform($tm) {
7515                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
7516                         // store transformation matrix
7517                         $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
7518                         // update tranformation mark
7519                         if (end($this->transfmrk[$this->page]) !== false) {
7520                                 $key = key($this->transfmrk[$this->page]);
7521                                 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
7522                         }
7523                 }
7524                 
7525                 // END TRANSFORMATIONS SECTION -------------------------
7526                 
7527                 
7528                 // START GRAPHIC FUNCTIONS SECTION ---------------------
7529                 // The following section is based on the code provided by David Hernandez Sanz
7530                 
7531                 /**
7532                 * 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.
7533                 * @param float $width The width.
7534                 * @access public
7535                 * @since 1.0
7536                 * @see Line(), Rect(), Cell(), MultiCell()
7537                 */
7538                 public function SetLineWidth($width) {
7539                         //Set line width
7540                         $this->LineWidth = $width;
7541                         $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
7542                         $this->_out($this->linestyleWidth);
7543                 }
7544                 
7545                 /**
7546                 * Returns the current the line width.
7547                 * @return int Line width 
7548                 * @access public
7549                 * @since 2.1.000 (2008-01-07)
7550                 * @see Line(), SetLineWidth()
7551                 */
7552                 public function GetLineWidth() {
7553                         return $this->LineWidth;
7554                 }
7555                 
7556                 /**
7557                 * Set line style.
7558                 * @param array $style Line style. Array with keys among the following:
7559                 * <ul>
7560                 *        <li>width (float): Width of the line in user units.</li>
7561                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
7562                 * butt, round, square. The difference between "square" and "butt" is that
7563                 * "square" projects a flat end past the end of the line.</li>
7564                 *        <li>join (string): Type of join. Possible values are: miter, round,
7565                 * bevel.</li>
7566                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
7567                 * series of length values, which are the lengths of the on and off dashes.
7568                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
7569                 * 1 off, 2 on, 1 off, ...</li>
7570                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
7571                 * the point at which the pattern starts.</li>
7572                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
7573                 * </ul>
7574                 * @access public
7575                 * @since 2.1.000 (2008-01-08)
7576                 */
7577                 public function SetLineStyle($style) {
7578                         extract($style);
7579                         if (isset($width)) {
7580                                 $width_prev = $this->LineWidth;
7581                                 $this->SetLineWidth($width);
7582                                 $this->LineWidth = $width_prev;
7583                         }
7584                         if (isset($cap)) {
7585                                 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
7586                                 if (isset($ca[$cap])) {
7587                                         $this->linestyleCap = $ca[$cap].' J';
7588                                         $this->_out($this->linestyleCap);
7589                                 }
7590                         }
7591                         if (isset($join)) {
7592                                 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
7593                                 if (isset($ja[$join])) {
7594                                         $this->linestyleJoin = $ja[$join].' j';
7595                                         $this->_out($this->linestyleJoin);
7596                                 }
7597                         }
7598                         if (isset($dash)) {
7599                                 $dash_string = '';
7600                                 if ($dash) {
7601                                         if (preg_match('/^.+,/', $dash)) {
7602                                                 $tab = explode(',', $dash);
7603                                         } else {
7604                                                 $tab = array($dash);
7605                                         }
7606                                         $dash_string = '';
7607                                         foreach ($tab as $i => $v) {
7608                                                 if ($i) {
7609                                                         $dash_string .= ' ';
7610                                                 }
7611                                                 $dash_string .= sprintf("%.2F", $v);
7612                                         }
7613                                 }
7614                                 if (!isset($phase) OR !$dash) {
7615                                         $phase = 0;
7616                                 }
7617                                 $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
7618                                 $this->_out($this->linestyleDash);
7619                         }
7620                         if (isset($color)) {
7621                                 $this->SetDrawColorArray($color);
7622                         }
7623                 }
7624                 
7625                 /*
7626                 * Set a draw point.
7627                 * @param float $x Abscissa of point.
7628                 * @param float $y Ordinate of point.
7629                 * @access protected
7630                 * @since 2.1.000 (2008-01-08)
7631                 */
7632                 protected function _outPoint($x, $y) {
7633                         if ($this->rtl) {
7634                                 $x = $this->w - $x;
7635                         }
7636                         $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
7637                 }
7638                 
7639                 /*
7640                 * Draws a line from last draw point.
7641                 * @param float $x Abscissa of end point.
7642                 * @param float $y Ordinate of end point.
7643                 * @access protected
7644                 * @since 2.1.000 (2008-01-08)
7645                 */
7646                 protected function _outLine($x, $y) {
7647                         if ($this->rtl) {
7648                                 $x = $this->w - $x;
7649                         }
7650                         $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
7651                 }
7652                 
7653                 /**
7654                 * Draws a rectangle.
7655                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
7656                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
7657                 * @param float $w Width.
7658                 * @param float $h Height.
7659                 * @param string $op options
7660                 * @access protected
7661                 * @since 2.1.000 (2008-01-08)
7662                 */
7663                 protected function _outRect($x, $y, $w, $h, $op) {
7664                         if ($this->rtl) {
7665                                 $x = $this->w - $x - $w;
7666                         }
7667                         $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
7668                 }
7669                 
7670                 /*
7671                 * Draws a Bezier curve from last draw point.
7672                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
7673                 * @param float $x1 Abscissa of control point 1.
7674                 * @param float $y1 Ordinate of control point 1.
7675                 * @param float $x2 Abscissa of control point 2.
7676                 * @param float $y2 Ordinate of control point 2.
7677                 * @param float $x3 Abscissa of end point.
7678                 * @param float $y3 Ordinate of end point.
7679                 * @access protected
7680                 * @since 2.1.000 (2008-01-08)
7681                 */
7682                 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
7683                         if ($this->rtl) {
7684                                 $x1 = $this->w - $x1;
7685                                 $x2 = $this->w - $x2;
7686                                 $x3 = $this->w - $x3;
7687                         }
7688                         $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));
7689                 }
7690                 
7691                 /**
7692                 * Draws a line between two points.
7693                 * @param float $x1 Abscissa of first point.
7694                 * @param float $y1 Ordinate of first point.
7695                 * @param float $x2 Abscissa of second point.
7696                 * @param float $y2 Ordinate of second point.
7697                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7698                 * @access public
7699                 * @since 1.0
7700                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
7701                 */
7702                 public function Line($x1, $y1, $x2, $y2, $style=array()) {
7703                         if ($style) {
7704                                 $this->SetLineStyle($style);
7705                         }
7706                         $this->_outPoint($x1, $y1);
7707                         $this->_outLine($x2, $y2);
7708                         $this->_out(' S');
7709                 }
7710                 
7711                 /**
7712                 * Draws a rectangle.
7713                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
7714                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
7715                 * @param float $w Width.
7716                 * @param float $h Height.
7717                 * @param string $style Style of rendering. Possible values are:
7718                 * <ul>
7719                 *        <li>D or empty string: Draw (default).</li>
7720                 *        <li>F: Fill.</li>
7721                 *        <li>DF or FD: Draw and fill.</li>
7722                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7723                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7724                 * </ul>
7725                 * @param array $border_style Border style of rectangle. Array with keys among the following:
7726                 * <ul>
7727                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
7728                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
7729                 * </ul>
7730                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
7731                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7732                 * @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).
7733                 * @access public
7734                 * @since 1.0
7735                 * @see SetLineStyle()
7736                 */
7737                 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
7738                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7739                                 $this->SetFillColorArray($fill_color);
7740                         }
7741                         switch ($style) {
7742                                 case 'F': {
7743                                         $op = 'f';
7744                                         $border_style = array();
7745                                         $this->_outRect($x, $y, $w, $h, $op);
7746                                         break;
7747                                 }
7748                                 case 'DF':
7749                                 case 'FD': {
7750                                         if ((!$border_style) OR (isset($border_style['all']))) {
7751                                                 $op = 'B';
7752                                                 if (isset($border_style['all'])) {
7753                                                         $this->SetLineStyle($border_style['all']);
7754                                                         $border_style = array();
7755                                                 }
7756                                         } else {
7757                                                 $op = 'f';
7758                                         }
7759                                         $this->_outRect($x, $y, $w, $h, $op);
7760                                         break;
7761                                 }
7762                                 case 'CNZ': {
7763                                         $op = 'W n';
7764                                         $this->_outRect($x, $y, $w, $h, $op);
7765                                         break;
7766                                 }
7767                                 case 'CEO': {
7768                                         $op = 'W* n';
7769                                         $this->_outRect($x, $y, $w, $h, $op);
7770                                         break;
7771                                 }
7772                                 default: {
7773                                         $op = 'S';
7774                                         if ((!$border_style) OR (isset($border_style['all']))) {
7775                                                 if (isset($border_style['all']) AND $border_style['all']) {
7776                                                         $this->SetLineStyle($border_style['all']);
7777                                                         $border_style = array();
7778                                                 }
7779                                                 $this->_outRect($x, $y, $w, $h, $op);
7780                                         }
7781                                         break;
7782                                 }
7783                         }
7784                         if ($border_style) {
7785                                 $border_style2 = array();
7786                                 foreach ($border_style as $line => $value) {
7787                                         $lenght = strlen($line);
7788                                         for ($i = 0; $i < $lenght; ++$i) {
7789                                                 $border_style2[$line[$i]] = $value;
7790                                         }
7791                                 }
7792                                 $border_style = $border_style2;
7793                                 if (isset($border_style['L']) AND $border_style['L']) {
7794                                         $this->Line($x, $y, $x, $y + $h, $border_style['L']);
7795                                 }
7796                                 if (isset($border_style['T']) AND $border_style['T']) {
7797                                         $this->Line($x, $y, $x + $w, $y, $border_style['T']);
7798                                 }
7799                                 if (isset($border_style['R']) AND $border_style['R']) {
7800                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
7801                                 }
7802                                 if (isset($border_style['B']) AND $border_style['B']) {
7803                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
7804                                 }
7805                         }
7806                 }
7807                 
7808                 
7809                 /**
7810                 * Draws a Bezier curve.
7811                 * The Bezier curve is a tangent to the line between the control points at
7812                 * either end of the curve.
7813                 * @param float $x0 Abscissa of start point.
7814                 * @param float $y0 Ordinate of start point.
7815                 * @param float $x1 Abscissa of control point 1.
7816                 * @param float $y1 Ordinate of control point 1.
7817                 * @param float $x2 Abscissa of control point 2.
7818                 * @param float $y2 Ordinate of control point 2.
7819                 * @param float $x3 Abscissa of end point.
7820                 * @param float $y3 Ordinate of end point.
7821                 * @param string $style Style of rendering. Possible values are:
7822                 * <ul>
7823                 *        <li>D or empty string: Draw (default).</li>
7824                 *        <li>F: Fill.</li>
7825                 *        <li>DF or FD: Draw and fill.</li>
7826                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7827                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7828                 * </ul>
7829                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7830                 * @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).
7831                 * @access public
7832                 * @see SetLineStyle()
7833                 * @since 2.1.000 (2008-01-08)
7834                 */
7835                 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
7836                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7837                                 $this->SetFillColorArray($fill_color);
7838                         }
7839                         switch ($style) {
7840                                 case 'F': {
7841                                         $op = 'f';
7842                                         $line_style = array();
7843                                         break;
7844                                 }
7845                                 case 'FD': 
7846                                 case 'DF': {
7847                                         $op = 'B';
7848                                         break;
7849                                 }
7850                                 case 'CNZ': {
7851                                         $op = 'W n';
7852                                         break;
7853                                 }
7854                                 case 'CEO': {
7855                                         $op = 'W* n';
7856                                         break;
7857                                 }
7858                                 default: {
7859                                         $op = 'S';
7860                                         break;
7861                                 }
7862                         }
7863                         if ($line_style) {
7864                                 $this->SetLineStyle($line_style);
7865                         }
7866                         $this->_outPoint($x0, $y0);
7867                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7868                         $this->_out($op);
7869                 }
7870                 
7871                 /**
7872                 * Draws a poly-Bezier curve.
7873                 * Each Bezier curve segment is a tangent to the line between the control points at
7874                 * either end of the curve.
7875                 * @param float $x0 Abscissa of start point.
7876                 * @param float $y0 Ordinate of start point.
7877                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
7878                 * @param string $style Style of rendering. Possible values are:
7879                 * <ul>
7880                 *        <li>D or empty string: Draw (default).</li>
7881                 *        <li>F: Fill.</li>
7882                 *        <li>DF or FD: Draw and fill.</li>
7883                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7884                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7885                 * </ul>
7886                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7887                 * @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).
7888                 * @access public
7889                 * @see SetLineStyle()
7890                 * @since 3.0008 (2008-05-12)
7891                 */
7892                 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
7893                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7894                                 $this->SetFillColorArray($fill_color);
7895                         }
7896                         switch ($style) {
7897                                 case 'F': {
7898                                         $op = 'f';
7899                                         $line_style = array();
7900                                         break;
7901                                 }
7902                                 case 'FD':
7903                                 case 'DF': {
7904                                         $op = 'B';
7905                                         break;
7906                                 }
7907                                 case 'CNZ': {
7908                                         $op = 'W n';
7909                                         break;
7910                                 }
7911                                 case 'CEO': {
7912                                         $op = 'W* n';
7913                                         break;
7914                                 }
7915                                 default: {
7916                                         $op = 'S';
7917                                         break;
7918                                 }
7919                         }
7920                         if ($line_style) {
7921                                 $this->SetLineStyle($line_style);
7922                         }
7923                         $this->_outPoint($x0, $y0);
7924                         foreach ($segments as $segment) {
7925                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
7926                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7927                         }       
7928                         $this->_out($op);
7929                 }
7930                 
7931                 /**
7932                 * Draws an ellipse.
7933                 * An ellipse is formed from n Bezier curves.
7934                 * @param float $x0 Abscissa of center point.
7935                 * @param float $y0 Ordinate of center point.
7936                 * @param float $rx Horizontal radius.
7937                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
7938                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
7939                 * @param float $astart: Angle start of draw line. Default value: 0.
7940                 * @param float $afinish: Angle finish of draw line. Default value: 360.
7941                 * @param string $style Style of rendering. Possible values are:
7942                 * <ul>
7943                 *        <li>D or empty string: Draw (default).</li>
7944                 *        <li>F: Fill.</li>
7945                 *        <li>DF or FD: Draw and fill.</li>
7946                 *        <li>C: Draw close.</li>
7947                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7948                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7949                 * </ul>
7950                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7951                 * @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).
7952                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
7953                 * @access public
7954                 * @since 2.1.000 (2008-01-08)
7955                 */
7956                 public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
7957                         if ($angle) {
7958                                 $this->StartTransform();
7959                                 $this->Rotate($angle, $x0, $y0);
7960                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
7961                                 $this->StopTransform();
7962                                 return;
7963                         }
7964                         if ($rx) {
7965                                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7966                                         $this->SetFillColorArray($fill_color);
7967                                 }
7968                                 switch ($style) {
7969                                         case 'F': {
7970                                                 $op = 'f';
7971                                                 $line_style = array();
7972                                                 break;
7973                                         }
7974                                         case 'FD': 
7975                                         case 'DF': {
7976                                                 $op = 'B';
7977                                                 break;
7978                                         }
7979                                         case 'C': {
7980                                                 $op = 's'; // Small 's' signifies closing the path as well
7981                                                 break;
7982                                         }
7983                                         case 'CNZ': {
7984                                                 $op = 'W n';
7985                                                 break;
7986                                         }
7987                                         case 'CEO': {
7988                                                 $op = 'W* n';
7989                                                 break;
7990                                         }
7991                                         default: {
7992                                                 $op = 'S';
7993                                                 break;
7994                                         }
7995                                 }
7996                                 if ($line_style) {
7997                                         $this->SetLineStyle($line_style);
7998                                 }
7999                                 if (!$ry) {
8000                                         $ry = $rx;
8001                                 }
8002                                 $rx *= $this->k;
8003                                 $ry *= $this->k;
8004                                 if ($nc < 2) {
8005                                         $nc = 2;
8006                                 }
8007                                 $astart = deg2rad((float) $astart);
8008                                 $afinish = deg2rad((float) $afinish);
8009                                 $total_angle = $afinish - $astart;
8010                                 $dt = $total_angle / $nc;
8011                                 $dtm = $dt / 3;
8012                                 $x0 *= $this->k;
8013                                 $y0 = ($this->h - $y0) * $this->k;
8014                                 $t1 = $astart;
8015                                 $a0 = $x0 + ($rx * cos($t1));
8016                                 $b0 = $y0 + ($ry * sin($t1));
8017                                 $c0 = -$rx * sin($t1);
8018                                 $d0 = $ry * cos($t1);
8019                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
8020                                 for ($i = 1; $i <= $nc; ++$i) {
8021                                         // Draw this bit of the total curve
8022                                         $t1 = ($i * $dt) + $astart;
8023                                         $a1 = $x0 + ($rx * cos($t1));
8024                                         $b1 = $y0 + ($ry * sin($t1));
8025                                         $c1 = -$rx * sin($t1);
8026                                         $d1 = $ry * cos($t1);
8027                                         $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));
8028                                         $a0 = $a1;
8029                                         $b0 = $b1;
8030                                         $c0 = $c1;
8031                                         $d0 = $d1;
8032                                 }
8033                                 $this->_out($op);
8034                         }
8035                 }
8036                 
8037                 /**
8038                 * Draws a circle.
8039                 * A circle is formed from n Bezier curves.
8040                 * @param float $x0 Abscissa of center point.
8041                 * @param float $y0 Ordinate of center point.
8042                 * @param float $r Radius.
8043                 * @param float $astart: Angle start of draw line. Default value: 0.
8044                 * @param float $afinish: Angle finish of draw line. Default value: 360.
8045                 * @param string $style Style of rendering. Possible values are:
8046                 * <ul>
8047                 *        <li>D or empty string: Draw (default).</li>
8048                 *        <li>F: Fill.</li>
8049                 *        <li>DF or FD: Draw and fill.</li>
8050                 *        <li>C: Draw close.</li>
8051                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8052                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8053                 * </ul>
8054                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8055                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8056                 * @param integer $nc Number of curves used in circle. Default value: 8.
8057                 * @access public
8058                 * @since 2.1.000 (2008-01-08)
8059                 */
8060                 public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
8061                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8062                 }
8063                 
8064                 /**
8065                 * Draws a polygon.
8066                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
8067                 * @param string $style Style of rendering. Possible values are:
8068                 * <ul>
8069                 *        <li>D or empty string: Draw (default).</li>
8070                 *        <li>F: Fill.</li>
8071                 *        <li>DF or FD: Draw and fill.</li>
8072                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8073                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8074                 * </ul>
8075                 * @param array $line_style Line style of polygon. Array with keys among the following:
8076                 * <ul>
8077                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
8078                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
8079                 * </ul>
8080                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
8081                 * @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).
8082                 * @access public
8083                 * @since 2.1.000 (2008-01-08)
8084                 */
8085                 public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) {
8086                         $np = count($p) / 2;
8087                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8088                                 $this->SetFillColorArray($fill_color);
8089                         }
8090                         switch ($style) {
8091                                 case 'F': {
8092                                         $line_style = array();
8093                                         $op = 'f';
8094                                         break;
8095                                 }
8096                                 case 'FD': 
8097                                 case 'DF': {
8098                                         $op = 'B';
8099                                         break;
8100                                 }
8101                                 case 'CNZ': {
8102                                         $op = 'W n';
8103                                         break;
8104                                 }
8105                                 case 'CEO': {
8106                                         $op = 'W* n';
8107                                         break;
8108                                 }                               
8109                                 default: {
8110                                         $op = 'S';
8111                                         break;
8112                                 }
8113                         }
8114                         $draw = true;
8115                         if ($line_style) {
8116                                 if (isset($line_style['all'])) {
8117                                         $this->SetLineStyle($line_style['all']);
8118                                 } else { // 0 .. (np - 1), op = {B, S}
8119                                         $draw = false;
8120                                         if ('B' == $op) {
8121                                                 $op = 'f';
8122                                                 $this->_outPoint($p[0], $p[1]);
8123                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8124                                                         $this->_outLine($p[$i], $p[$i + 1]);
8125                                                 }
8126                                                 $this->_outLine($p[0], $p[1]);
8127                                                 $this->_out($op);
8128                                         }
8129                                         $p[($np * 2)] = $p[0];
8130                                         $p[(($np * 2) + 1)] = $p[1];
8131                                         for ($i = 0; $i < $np; ++$i) {
8132                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
8133                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
8134                                                 }
8135                                         }
8136                                 }
8137                         }
8138                         if ($draw) {
8139                                 $this->_outPoint($p[0], $p[1]);
8140                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8141                                         $this->_outLine($p[$i], $p[$i + 1]);
8142                                 }
8143                                 $this->_outLine($p[0], $p[1]);
8144                                 $this->_out($op);
8145                         }
8146                 }
8147                 
8148                 /**
8149                 * Draws a regular polygon.
8150                 * @param float $x0 Abscissa of center point.
8151                 * @param float $y0 Ordinate of center point.
8152                 * @param float $r: Radius of inscribed circle.
8153                 * @param integer $ns Number of sides.
8154                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
8155                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
8156                 * @param string $style Style of rendering. Possible values are:
8157                 * <ul>
8158                 *        <li>D or empty string: Draw (default).</li>
8159                 *        <li>F: Fill.</li>
8160                 *        <li>DF or FD: Draw and fill.</li>
8161                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8162                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8163                 * </ul>
8164                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
8165                 * <ul>
8166                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
8167                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
8168                 * </ul>
8169                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
8170                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8171                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
8172                 * <ul>
8173                 *        <li>D or empty string: Draw (default).</li>
8174                 *        <li>F: Fill.</li>
8175                 *        <li>DF or FD: Draw and fill.</li>
8176                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8177                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8178                 * </ul>
8179                 * @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).
8180                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
8181                 * @access public
8182                 * @since 2.1.000 (2008-01-08)
8183                 */
8184                 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()) {
8185                         if (3 > $ns) {
8186                                 $ns = 3;
8187                         }
8188                         if ($draw_circle) {
8189                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8190                         }
8191                         $p = array();
8192                         for ($i = 0; $i < $ns; ++$i) {
8193                                 $a = $angle + ($i * 360 / $ns);
8194                                 $a_rad = deg2rad((float) $a);
8195                                 $p[] = $x0 + ($r * sin($a_rad));
8196                                 $p[] = $y0 + ($r * cos($a_rad));
8197                         }
8198                         $this->Polygon($p, $style, $line_style, $fill_color);
8199                 }
8200                 
8201                 /**
8202                 * Draws a star polygon
8203                 * @param float $x0 Abscissa of center point.
8204                 * @param float $y0 Ordinate of center point.
8205                 * @param float $r Radius of inscribed circle.
8206                 * @param integer $nv Number of vertices.
8207                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
8208                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
8209                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
8210                 * @param string $style Style of rendering. Possible values are:
8211                 * <ul>
8212                 *        <li>D or empty string: Draw (default).</li>
8213                 *        <li>F: Fill.</li>
8214                 *        <li>DF or FD: Draw and fill.</li>
8215                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8216                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8217                 * </ul>
8218                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
8219                 * <ul>
8220                 *        <li>all: Line style of all sides. Array like for
8221                 * {@link SetLineStyle SetLineStyle}.</li>
8222                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
8223                 * </ul>
8224                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
8225                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8226                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
8227                 * <ul>
8228                 *        <li>D or empty string: Draw (default).</li>
8229                 *        <li>F: Fill.</li>
8230                 *        <li>DF or FD: Draw and fill.</li>
8231                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8232                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8233                 * </ul>
8234                 * @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).
8235                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
8236                 * @access public
8237                 * @since 2.1.000 (2008-01-08)
8238                 */
8239                 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()) {
8240                         if (2 > $nv) {
8241                                 $nv = 2;
8242                         }
8243                         if ($draw_circle) {
8244                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8245                         }
8246                         $p2 = array();
8247                         $visited = array();
8248                         for ($i = 0; $i < $nv; ++$i) {
8249                                 $a = $angle + ($i * 360 / $nv);
8250                                 $a_rad = deg2rad((float) $a);
8251                                 $p2[] = $x0 + ($r * sin($a_rad));
8252                                 $p2[] = $y0 + ($r * cos($a_rad));
8253                                 $visited[] = false;
8254                         }
8255                         $p = array();
8256                         $i = 0;
8257                         do {
8258                                 $p[] = $p2[$i * 2];
8259                                 $p[] = $p2[($i * 2) + 1];
8260                                 $visited[$i] = true;
8261                                 $i += $ng;
8262                                 $i %= $nv;
8263                         } while (!$visited[$i]);
8264                         $this->Polygon($p, $style, $line_style, $fill_color);
8265                 }
8266                 
8267                 /**
8268                 * Draws a rounded rectangle.
8269                 * @param float $x Abscissa of upper-left corner.
8270                 * @param float $y Ordinate of upper-left corner.
8271                 * @param float $w Width.
8272                 * @param float $h Height.
8273                 * @param float $r Radius of the rounded corners.
8274                 * @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").
8275                 * @param string $style Style of rendering. Possible values are:
8276                 * <ul>
8277                 *        <li>D or empty string: Draw (default).</li>
8278                 *        <li>F: Fill.</li>
8279                 *        <li>DF or FD: Draw and fill.</li>
8280                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8281                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8282                 * </ul>
8283                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8284                 * @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).
8285                 * @access public
8286                 * @since 2.1.000 (2008-01-08)
8287                 */
8288                 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
8289                         if ('0000' == $round_corner) { // Not rounded
8290                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
8291                         } else { // Rounded
8292                                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8293                                         $this->SetFillColorArray($fill_color);
8294                                 }
8295                                 switch ($style) {
8296                                         case 'F': {
8297                                                 $border_style = array();
8298                                                 $op = 'f';
8299                                                 break;
8300                                         }
8301                                         case 'FD': 
8302                                         case 'DF': {
8303                                                 $op = 'B';
8304                                                 break;
8305                                         }
8306                                         case 'CNZ': {
8307                                                 $op = 'W n';
8308                                                 break;
8309                                         }
8310                                         case 'CEO': {
8311                                                 $op = 'W* n';
8312                                                 break;
8313                                         }
8314                                         default: {
8315                                                 $op = 'S';
8316                                                 break;
8317                                         }
8318                                 }
8319                                 if ($border_style) {
8320                                         $this->SetLineStyle($border_style);
8321                                 }
8322                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
8323                                 $this->_outPoint($x + $r, $y);
8324                                 $xc = $x + $w - $r;
8325                                 $yc = $y + $r;
8326                                 $this->_outLine($xc, $y);
8327                                 if ($round_corner[0]) {
8328                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
8329                                 } else {
8330                                         $this->_outLine($x + $w, $y);
8331                                 }
8332                                 $xc = $x + $w - $r;
8333                                 $yc = $y + $h - $r;
8334                                 $this->_outLine($x + $w, $yc);
8335                                 if ($round_corner[1]) {
8336                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
8337                                 } else {
8338                                         $this->_outLine($x + $w, $y + $h);
8339                                 }
8340                                 $xc = $x + $r;
8341                                 $yc = $y + $h - $r;
8342                                 $this->_outLine($xc, $y + $h);
8343                                 if ($round_corner[2]) {
8344                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
8345                                 } else {
8346                                         $this->_outLine($x, $y + $h);
8347                                 }
8348                                 $xc = $x + $r;
8349                                 $yc = $y + $r;
8350                                 $this->_outLine($x, $yc);
8351                                 if ($round_corner[3]) {
8352                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
8353                                 } else {
8354                                         $this->_outLine($x, $y);
8355                                         $this->_outLine($x + $r, $y);
8356                                 }
8357                                 $this->_out($op);
8358                         }
8359                 }
8360                 
8361                 // END GRAPHIC FUNCTIONS SECTION -----------------------
8362                 
8363                 // BIDIRECTIONAL TEXT SECTION --------------------------
8364                 /**
8365                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
8366                  * @param string $str string to manipulate.
8367                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
8368                  * @return string
8369                  * @access protected
8370                  * @author Nicola Asuni
8371                  * @since 2.1.000 (2008-01-08)
8372                 */
8373                 protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
8374                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
8375                 }
8376                 
8377                 /**
8378                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
8379                  * @param array $ta array of characters composing the string.
8380                  * @param string $str string to process
8381                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
8382                  * @return string
8383                  * @author Nicola Asuni
8384                  * @access protected
8385                  * @since 2.4.000 (2008-03-06)
8386                 */
8387                 protected function utf8Bidi($ta, $str='', $forcertl=false) {
8388                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
8389                         // paragraph embedding level
8390                         $pel = 0;
8391                         // max level
8392                         $maxlevel = 0;
8393                         if ($this->empty_string($str)) {
8394                                 // create string from array
8395                                 $str = $this->UTF8ArrSubString($ta);
8396                         }
8397                         // check if string contains arabic text
8398                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
8399                                 $arabic = true;
8400                         } else {
8401                                 $arabic = false;
8402                         }
8403                         // check if string contains RTL text
8404                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
8405                                 return $ta;
8406                         }
8407                         
8408                         // get number of chars
8409                         $numchars = count($ta);
8410                         
8411                         if ($forcertl == 'R') {
8412                                         $pel = 1;
8413                         } elseif ($forcertl == 'L') {
8414                                         $pel = 0;
8415                         } else {
8416                                 // P2. In each paragraph, find the first character of type L, AL, or R.
8417                                 // 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.
8418                                 for ($i=0; $i < $numchars; ++$i) {
8419                                         $type = $unicode[$ta[$i]];
8420                                         if ($type == 'L') {
8421                                                 $pel = 0;
8422                                                 break;
8423                                         } elseif (($type == 'AL') OR ($type == 'R')) {
8424                                                 $pel = 1;
8425                                                 break;
8426                                         }
8427                                 }
8428                         }
8429                         
8430                         // Current Embedding Level
8431                         $cel = $pel;
8432                         // directional override status
8433                         $dos = 'N';
8434                         $remember = array();
8435                         // start-of-level-run
8436                         $sor = $pel % 2 ? 'R' : 'L';
8437                         $eor = $sor;
8438                         
8439                         // Array of characters data
8440                         $chardata = Array();
8441                         
8442                         // 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.
8443                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
8444                         for ($i=0; $i < $numchars; ++$i) {
8445                                 if ($ta[$i] == K_RLE) {
8446                                         // X2. With each RLE, compute the least greater odd embedding level.
8447                                         //      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.
8448                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8449                                         $next_level = $cel + ($cel % 2) + 1;
8450                                         if ($next_level < 62) {
8451                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
8452                                                 $cel = $next_level;
8453                                                 $dos = 'N';
8454                                                 $sor = $eor;
8455                                                 $eor = $cel % 2 ? 'R' : 'L';
8456                                         }
8457                                 } elseif ($ta[$i] == K_LRE) {
8458                                         // X3. With each LRE, compute the least greater even embedding level.
8459                                         //      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.
8460                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8461                                         $next_level = $cel + 2 - ($cel % 2);
8462                                         if ( $next_level < 62 ) {
8463                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
8464                                                 $cel = $next_level;
8465                                                 $dos = 'N';
8466                                                 $sor = $eor;
8467                                                 $eor = $cel % 2 ? 'R' : 'L';
8468                                         }
8469                                 } elseif ($ta[$i] == K_RLO) {
8470                                         // X4. With each RLO, compute the least greater odd embedding level.
8471                                         //      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.
8472                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8473                                         $next_level = $cel + ($cel % 2) + 1;
8474                                         if ($next_level < 62) {
8475                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
8476                                                 $cel = $next_level;
8477                                                 $dos = 'R';
8478                                                 $sor = $eor;
8479                                                 $eor = $cel % 2 ? 'R' : 'L';
8480                                         }
8481                                 } elseif ($ta[$i] == K_LRO) {
8482                                         // X5. With each LRO, compute the least greater even embedding level.
8483                                         //      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.
8484                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8485                                         $next_level = $cel + 2 - ($cel % 2);
8486                                         if ( $next_level < 62 ) {
8487                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
8488                                                 $cel = $next_level;
8489                                                 $dos = 'L';
8490                                                 $sor = $eor;
8491                                                 $eor = $cel % 2 ? 'R' : 'L';
8492                                         }
8493                                 } elseif ($ta[$i] == K_PDF) {
8494                                         // 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.
8495                                         if (count($remember)) {
8496                                                 $last = count($remember ) - 1;
8497                                                 if (($remember[$last]['num'] == K_RLE) OR 
8498                                                           ($remember[$last]['num'] == K_LRE) OR 
8499                                                           ($remember[$last]['num'] == K_RLO) OR 
8500                                                           ($remember[$last]['num'] == K_LRO)) {
8501                                                         $match = array_pop($remember);
8502                                                         $cel = $match['cel'];
8503                                                         $dos = $match['dos'];
8504                                                         $sor = $eor;
8505                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
8506                                                 }
8507                                         }
8508                                 } elseif (($ta[$i] != K_RLE) AND
8509                                                                  ($ta[$i] != K_LRE) AND
8510                                                                  ($ta[$i] != K_RLO) AND
8511                                                                  ($ta[$i] != K_LRO) AND
8512                                                                  ($ta[$i] != K_PDF)) {
8513                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
8514                                         //      a. Set the level of the current character to the current embedding level.
8515                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
8516                                         if ($dos != 'N') {
8517                                                 $chardir = $dos;
8518                                         } else {
8519                                                 $chardir = $unicode[$ta[$i]];
8520                                         }
8521                                         // stores string characters and other information
8522                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
8523                                 }
8524                         } // end for each char
8525                         
8526                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
8527                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
8528                         // 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.
8529                         
8530                         // 3.3.3 Resolving Weak Types
8531                         // 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.
8532                         // Nonspacing marks are now resolved based on the previous characters.
8533                         $numchars = count($chardata);
8534                         
8535                         // 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.
8536                         $prevlevel = -1; // track level changes
8537                         $levcount = 0; // counts consecutive chars at the same level
8538                         for ($i=0; $i < $numchars; ++$i) {
8539                                 if ($chardata[$i]['type'] == 'NSM') {
8540                                         if ($levcount) {
8541                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8542                                         } elseif ($i > 0) {
8543                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
8544                                         }
8545                                 }
8546                                 if ($chardata[$i]['level'] != $prevlevel) {
8547                                         $levcount = 0;
8548                                 } else {
8549                                         ++$levcount;
8550                                 }
8551                                 $prevlevel = $chardata[$i]['level'];
8552                         }
8553                         
8554                         // 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.
8555                         $prevlevel = -1;
8556                         $levcount = 0;
8557                         for ($i=0; $i < $numchars; ++$i) {
8558                                 if ($chardata[$i]['char'] == 'EN') {
8559                                         for ($j=$levcount; $j >= 0; $j--) {
8560                                                 if ($chardata[$j]['type'] == 'AL') {
8561                                                         $chardata[$i]['type'] = 'AN';
8562                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
8563                                                         break;
8564                                                 }
8565                                         }
8566                                 }
8567                                 if ($chardata[$i]['level'] != $prevlevel) {
8568                                         $levcount = 0;
8569                                 } else {
8570                                         ++$levcount;
8571                                 }
8572                                 $prevlevel = $chardata[$i]['level'];
8573                         }
8574                         
8575                         // W3. Change all ALs to R.
8576                         for ($i=0; $i < $numchars; ++$i) {
8577                                 if ($chardata[$i]['type'] == 'AL') {
8578                                         $chardata[$i]['type'] = 'R';
8579                                 } 
8580                         }
8581                         
8582                         // 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.
8583                         $prevlevel = -1;
8584                         $levcount = 0;
8585                         for ($i=0; $i < $numchars; ++$i) {
8586                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8587                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8588                                                 $chardata[$i]['type'] = 'EN';
8589                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8590                                                 $chardata[$i]['type'] = 'EN';
8591                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
8592                                                 $chardata[$i]['type'] = 'AN';
8593                                         }
8594                                 }
8595                                 if ($chardata[$i]['level'] != $prevlevel) {
8596                                         $levcount = 0;
8597                                 } else {
8598                                         ++$levcount;
8599                                 }
8600                                 $prevlevel = $chardata[$i]['level'];
8601                         }
8602                         
8603                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
8604                         $prevlevel = -1;
8605                         $levcount = 0;
8606                         for ($i=0; $i < $numchars; ++$i) {
8607                                 if ($chardata[$i]['type'] == 'ET') {
8608                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
8609                                                 $chardata[$i]['type'] = 'EN';
8610                                         } else {
8611                                                 $j = $i+1;
8612                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
8613                                                         if ($chardata[$j]['type'] == 'EN') {
8614                                                                 $chardata[$i]['type'] = 'EN';
8615                                                                 break;
8616                                                         } elseif ($chardata[$j]['type'] != 'ET') {
8617                                                                 break;
8618                                                         }
8619                                                         ++$j;
8620                                                 }
8621                                         }
8622                                 }
8623                                 if ($chardata[$i]['level'] != $prevlevel) {
8624                                         $levcount = 0;
8625                                 } else {
8626                                         ++$levcount;
8627                                 }
8628                                 $prevlevel = $chardata[$i]['level'];
8629                         }
8630                         
8631                         // W6. Otherwise, separators and terminators change to Other Neutral.
8632                         $prevlevel = -1;
8633                         $levcount = 0;
8634                         for ($i=0; $i < $numchars; ++$i) {
8635                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
8636                                         $chardata[$i]['type'] = 'ON';
8637                                 }
8638                                 if ($chardata[$i]['level'] != $prevlevel) {
8639                                         $levcount = 0;
8640                                 } else {
8641                                         ++$levcount;
8642                                 }
8643                                 $prevlevel = $chardata[$i]['level'];
8644                         }
8645                         
8646                         //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.
8647                         $prevlevel = -1;
8648                         $levcount = 0;
8649                         for ($i=0; $i < $numchars; ++$i) {
8650                                 if ($chardata[$i]['char'] == 'EN') {
8651                                         for ($j=$levcount; $j >= 0; $j--) {
8652                                                 if ($chardata[$j]['type'] == 'L') {
8653                                                         $chardata[$i]['type'] = 'L';
8654                                                 } elseif ($chardata[$j]['type'] == 'R') {
8655                                                         break;
8656                                                 }
8657                                         }
8658                                 }
8659                                 if ($chardata[$i]['level'] != $prevlevel) {
8660                                         $levcount = 0;
8661                                 } else {
8662                                         ++$levcount;
8663                                 }
8664                                 $prevlevel = $chardata[$i]['level'];
8665                         }
8666                         
8667                         // 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.
8668                         $prevlevel = -1;
8669                         $levcount = 0;
8670                         for ($i=0; $i < $numchars; ++$i) {
8671                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8672                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8673                                                 $chardata[$i]['type'] = 'L';
8674                                         } elseif (($chardata[$i]['type'] == 'N') AND
8675                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8676                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8677                                                 $chardata[$i]['type'] = 'R';
8678                                         } elseif ($chardata[$i]['type'] == 'N') {
8679                                                 // N2. Any remaining neutrals take the embedding direction
8680                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8681                                         }
8682                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8683                                         // first char
8684                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8685                                                 $chardata[$i]['type'] = 'L';
8686                                         } elseif (($chardata[$i]['type'] == 'N') AND
8687                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
8688                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8689                                                 $chardata[$i]['type'] = 'R';
8690                                         } elseif ($chardata[$i]['type'] == 'N') {
8691                                                 // N2. Any remaining neutrals take the embedding direction
8692                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8693                                         }
8694                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
8695                                         //last char
8696                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
8697                                                 $chardata[$i]['type'] = 'L';
8698                                         } elseif (($chardata[$i]['type'] == 'N') AND
8699                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8700                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
8701                                                 $chardata[$i]['type'] = 'R';
8702                                         } elseif ($chardata[$i]['type'] == 'N') {
8703                                                 // N2. Any remaining neutrals take the embedding direction
8704                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8705                                         }
8706                                 } elseif ($chardata[$i]['type'] == 'N') {
8707                                         // N2. Any remaining neutrals take the embedding direction
8708                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
8709                                 }
8710                                 if ($chardata[$i]['level'] != $prevlevel) {
8711                                         $levcount = 0;
8712                                 } else {
8713                                         ++$levcount;
8714                                 }
8715                                 $prevlevel = $chardata[$i]['level'];
8716                         }
8717                         
8718                         // 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.
8719                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
8720                         for ($i=0; $i < $numchars; ++$i) {
8721                                 $odd = $chardata[$i]['level'] % 2;
8722                                 if ($odd) {
8723                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8724                                                 $chardata[$i]['level'] += 1;
8725                                         }
8726                                 } else {
8727                                         if ($chardata[$i]['type'] == 'R') {
8728                                                 $chardata[$i]['level'] += 1;
8729                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8730                                                 $chardata[$i]['level'] += 2;
8731                                         }
8732                                 }
8733                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
8734                         }
8735                         
8736                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
8737                         //      1. Segment separators,
8738                         //      2. Paragraph separators,
8739                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
8740                         //      4. Any sequence of white space characters at the end of the line.
8741                         for ($i=0; $i < $numchars; ++$i) {
8742                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
8743                                         $chardata[$i]['level'] = $pel;
8744                                 } elseif ($chardata[$i]['type'] == 'WS') {
8745                                         $j = $i+1;
8746                                         while ($j < $numchars) {
8747                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
8748                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
8749                                                         $chardata[$i]['level'] = $pel;
8750                                                         break;
8751                                                 } elseif ($chardata[$j]['type'] != 'WS') {
8752                                                         break;
8753                                                 }
8754                                                 ++$j;
8755                                         }
8756                                 }
8757                         }
8758                         
8759                         // Arabic Shaping
8760                         // 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. 
8761                         if ($arabic) {
8762                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
8763                                 $alfletter = array(1570,1571,1573,1575);
8764                                 $chardata2 = $chardata;
8765                                 $laaletter = false;
8766                                 $charAL = array();
8767                                 $x = 0;
8768                                 for ($i=0; $i < $numchars; ++$i) {
8769                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
8770                                                 $charAL[$x] = $chardata[$i];
8771                                                 $charAL[$x]['i'] = $i;
8772                                                 $chardata[$i]['x'] = $x;
8773                                                 ++$x;
8774                                         }
8775                                 }
8776                                 $numAL = $x;
8777                                 for ($i=0; $i < $numchars; ++$i) {
8778                                         $thischar = $chardata[$i];
8779                                         if ($i > 0) {
8780                                                 $prevchar = $chardata[($i-1)];
8781                                         } else {
8782                                                 $prevchar = false;
8783                                         }
8784                                         if (($i+1) < $numchars) {
8785                                                 $nextchar = $chardata[($i+1)];
8786                                         } else {
8787                                                 $nextchar = false;
8788                                         }
8789                                         if ($unicode[$thischar['char']] == 'AL') {
8790                                                 $x = $thischar['x'];
8791                                                 if ($x > 0) {
8792                                                         $prevchar = $charAL[($x-1)];
8793                                                 } else {
8794                                                         $prevchar = false;
8795                                                 }
8796                                                 if (($x+1) < $numAL) {
8797                                                         $nextchar = $charAL[($x+1)];
8798                                                 } else {
8799                                                         $nextchar = false;
8800                                                 }
8801                                                 // if laa letter
8802                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
8803                                                         $arabicarr = $laa_array;
8804                                                         $laaletter = true;
8805                                                         if ($x > 1) {
8806                                                                 $prevchar = $charAL[($x-2)];
8807                                                         } else {
8808                                                                 $prevchar = false;
8809                                                         }
8810                                                 } else {
8811                                                         $arabicarr = $unicode_arlet;
8812                                                         $laaletter = false;
8813                                                 }
8814                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
8815                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8816                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8817                                                         ($prevchar['type'] == $thischar['type']) AND
8818                                                         ($nextchar['type'] == $thischar['type']) AND
8819                                                         ($nextchar['char'] != 1567)) {
8820                                                         if (in_array($prevchar['char'], $endedletter)) {
8821                                                                 if (isset($arabicarr[$thischar['char']][2])) {
8822                                                                         // initial
8823                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8824                                                                 }
8825                                                         } else {
8826                                                                 if (isset($arabicarr[$thischar['char']][3])) {
8827                                                                         // medial
8828                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
8829                                                                 }
8830                                                         }
8831                                                 } elseif (($nextchar !== false) AND
8832                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8833                                                         ($nextchar['type'] == $thischar['type']) AND
8834                                                         ($nextchar['char'] != 1567)) {
8835                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
8836                                                                 // initial
8837                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8838                                                         }
8839                                                 } elseif ((($prevchar !== false) AND
8840                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8841                                                         ($prevchar['type'] == $thischar['type'])) OR
8842                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
8843                                                         // final
8844                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
8845                                                                 ($chardata[$i-1]['char'] == 1604) AND
8846                                                                 ($chardata[$i-2]['char'] == 1604)) {
8847                                                                 //Allah Word
8848                                                                 // mark characters to delete with false
8849                                                                 $chardata2[$i-2]['char'] = false;
8850                                                                 $chardata2[$i-1]['char'] = false; 
8851                                                                 $chardata2[$i]['char'] = 65010;
8852                                                         } else {
8853                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
8854                                                                         if (isset($arabicarr[$thischar['char']][0])) {
8855                                                                                 // isolated
8856                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8857                                                                         }
8858                                                                 } else {
8859                                                                         if (isset($arabicarr[$thischar['char']][1])) {
8860                                                                                 // final
8861                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
8862                                                                         }
8863                                                                 }
8864                                                         }
8865                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
8866                                                         // isolated
8867                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8868                                                 }
8869                                                 // if laa letter
8870                                                 if ($laaletter) {
8871                                                         // mark characters to delete with false
8872                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
8873                                                 }
8874                                         } // end if AL (Arabic Letter)
8875                                 } // end for each char
8876                                 /* 
8877                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 
8878                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
8879                                  */
8880                                 $cw = &$this->CurrentFont['cw'];
8881                                 for ($i = 0; $i < ($numchars-1); ++$i) {
8882                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
8883                                                 // check if the subtitution font is defined on current font
8884                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
8885                                                         $chardata2[$i]['char'] = false;
8886                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
8887                                                 }
8888                                         }
8889                                 }
8890                                 // remove marked characters
8891                                 foreach ($chardata2 as $key => $value) {
8892                                         if ($value['char'] === false) {
8893                                                 unset($chardata2[$key]);
8894                                         }
8895                                 }
8896                                 $chardata = array_values($chardata2);
8897                                 $numchars = count($chardata);
8898                                 unset($chardata2);
8899                                 unset($arabicarr);
8900                                 unset($laaletter);
8901                                 unset($charAL);
8902                         }
8903                         
8904                         // 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.
8905                         for ($j=$maxlevel; $j > 0; $j--) {
8906                                 $ordarray = Array();
8907                                 $revarr = Array();
8908                                 $onlevel = false;
8909                                 for ($i=0; $i < $numchars; ++$i) {
8910                                         if ($chardata[$i]['level'] >= $j) {
8911                                                 $onlevel = true;
8912                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
8913                                                         // 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.
8914                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
8915                                                 }
8916                                                 $revarr[] = $chardata[$i];
8917                                         } else {
8918                                                 if ($onlevel) {
8919                                                         $revarr = array_reverse($revarr);
8920                                                         $ordarray = array_merge($ordarray, $revarr);
8921                                                         $revarr = Array();
8922                                                         $onlevel = false;
8923                                                 }
8924                                                 $ordarray[] = $chardata[$i];
8925                                         }
8926                                 }
8927                                 if ($onlevel) {
8928                                         $revarr = array_reverse($revarr);
8929                                         $ordarray = array_merge($ordarray, $revarr);
8930                                 }
8931                                 $chardata = $ordarray;
8932                         }
8933                         
8934                         $ordarray = array();
8935                         for ($i=0; $i < $numchars; ++$i) {
8936                                 $ordarray[] = $chardata[$i]['char'];
8937                         }
8938                         
8939                         return $ordarray;
8940                 }
8941                 
8942                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
8943                 
8944                 /*
8945                 * Adds a bookmark.
8946                 * @param string $txt bookmark description.
8947                 * @param int $level bookmark level (minimum value is 0).
8948                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
8949                 * @param int $page target page number (leave empty for current page).
8950                 * @access public
8951                 * @author Olivier Plathey, Nicola Asuni
8952                 * @since 2.1.002 (2008-02-12)
8953                 */
8954                 public function Bookmark($txt, $level=0, $y=-1, $page='') {
8955                         if ($level < 0) {
8956                                 $level = 0;
8957                         }
8958                         if (isset($this->outlines[0])) {
8959                                 $lastoutline = end($this->outlines);
8960                                 $maxlevel = $lastoutline['l'] + 1;
8961                         } else {
8962                                 $maxlevel = 0;
8963                         }
8964                         if ($level > $maxlevel) {
8965                                 $level = $maxlevel;
8966                         }
8967                         if ($y == -1) {
8968                                 $y = $this->GetY();
8969                         }
8970                         if (empty($page)) {
8971                                 $page = $this->PageNo();
8972                         }
8973                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
8974                 }
8975                 
8976                 /*
8977                 * Create a bookmark PDF string.
8978                 * @access protected
8979                 * @author Olivier Plathey, Nicola Asuni
8980                 * @since 2.1.002 (2008-02-12)
8981                 */
8982                 protected function _putbookmarks() {
8983                         $nb = count($this->outlines);
8984                         if ($nb == 0) {
8985                                 return;
8986                         }
8987                         $lru = array();
8988                         $level = 0;
8989                         foreach ($this->outlines as $i => $o) {
8990                                 if ($o['l'] > 0) {
8991                                         $parent = $lru[($o['l'] - 1)];
8992                                         //Set parent and last pointers
8993                                         $this->outlines[$i]['parent'] = $parent;
8994                                         $this->outlines[$parent]['last'] = $i;
8995                                         if ($o['l'] > $level) {
8996                                                 //Level increasing: set first pointer
8997                                                 $this->outlines[$parent]['first'] = $i;
8998                                         }
8999                                 } else {
9000                                         $this->outlines[$i]['parent'] = $nb;
9001                                 }
9002                                 if (($o['l'] <= $level) AND ($i > 0)) {
9003                                         //Set prev and next pointers
9004                                         $prev = $lru[$o['l']];
9005                                         $this->outlines[$prev]['next'] = $i;
9006                                         $this->outlines[$i]['prev'] = $prev;
9007                                 }
9008                                 $lru[$o['l']] = $i;
9009                                 $level = $o['l'];
9010                         }
9011                         //Outline items
9012                         $n = $this->n + 1;
9013                         foreach ($this->outlines as $i => $o) {
9014                                 $this->_newobj();
9015                                 $this->_out('<</Title '.$this->_textstring($o['t']));
9016                                 $this->_out('/Parent '.($n + $o['parent']).' 0 R');
9017                                 if (isset($o['prev']))
9018                                 $this->_out('/Prev '.($n + $o['prev']).' 0 R');
9019                                 if (isset($o['next']))
9020                                 $this->_out('/Next '.($n + $o['next']).' 0 R');
9021                                 if (isset($o['first']))
9022                                 $this->_out('/First '.($n + $o['first']).' 0 R');
9023                                 if (isset($o['last']))
9024                                 $this->_out('/Last '.($n + $o['last']).' 0 R');
9025                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
9026                                 $this->_out('/Count 0>>');
9027                                 $this->_out('endobj');
9028                         }
9029                         //Outline root
9030                         $this->_newobj();
9031                         $this->OutlineRoot = $this->n;
9032                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
9033                         $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
9034                         $this->_out('endobj');
9035                 }
9036                 
9037                 
9038                 // --- JAVASCRIPT - FORMS ------------------------------
9039                 
9040                 /*
9041                 * Adds a javascript
9042                 * @access public
9043                 * @author Johannes Güntert, Nicola Asuni
9044                 * @since 2.1.002 (2008-02-12)
9045                 */
9046                 public function IncludeJS($script) {
9047                         $this->javascript .= $script;
9048                 }
9049                 
9050                 /*
9051                 * Create a javascript PDF string.
9052                 * @access protected
9053                 * @author Johannes Güntert, Nicola Asuni
9054                 * @since 2.1.002 (2008-02-12)
9055                 */
9056                 protected function _putjavascript() {
9057                         if (empty($this->javascript)) {
9058                                 return;
9059                         }
9060                         // the following two lines are uded to avoid form fields duplication after saving
9061                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
9062                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
9063                         $this->_newobj();
9064                         $this->n_js = $this->n;
9065                         $this->_out('<<');
9066                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
9067                         $this->_out('>>');
9068                         $this->_out('endobj');
9069                         $this->_newobj();
9070                         $this->_out('<<');
9071                         $this->_out('/S /JavaScript');
9072                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
9073                         $this->_out('>>');
9074                         $this->_out('endobj');
9075                 }
9076                 
9077                 /*
9078                 * Convert color to javascript color.
9079                 * @param string $color color name or #RRGGBB
9080                 * @access protected
9081                 * @author Denis Van Nuffelen, Nicola Asuni
9082                 * @since 2.1.002 (2008-02-12)
9083                 */
9084                 protected function _JScolor($color) {
9085                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
9086                         if (substr($color,0,1) == '#') {
9087                                 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
9088                         }
9089                         if (!in_array($color,$aColors)) {
9090                                 $this->Error('Invalid color: '.$color);
9091                         }
9092                         return 'color.'.$color;
9093                 }
9094                 
9095                 /*
9096                 * Adds a javascript form field.
9097                 * @param string $type field type
9098                 * @param string $name field name
9099                 * @param int $x horizontal position
9100                 * @param int $y vertical position
9101                 * @param int $w width
9102                 * @param int $h height
9103                 * @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>
9104                 * @access protected
9105                 * @author Denis Van Nuffelen, Nicola Asuni
9106                 * @since 2.1.002 (2008-02-12)
9107                 */
9108                 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
9109                         if ($this->rtl) {
9110                                 $x = $x - $w;
9111                         }
9112                         // the followind avoid fields duplication after saving the document
9113                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
9114                         $k = $this->k;
9115                         $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";
9116                         $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
9117                         while (list($key, $val) = each($prop)) {
9118                                 if (strcmp(substr($key, -5), 'Color') == 0) {
9119                                         $val = $this->_JScolor($val);
9120                                 } else {
9121                                         $val = "'".$val."'";
9122                                 }
9123                                 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
9124                         }
9125                         if ($this->rtl) {
9126                                 $this->x -= $w;
9127                         } else {
9128                                 $this->x += $w;
9129                         }
9130                         $this->javascript .= '}';
9131                 }
9132                 
9133                 /*
9134                 * Creates a text field
9135                 * @param string $name field name
9136                 * @param int $w width
9137                 * @param int $h height
9138                 * @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>
9139                 * @access public
9140                 * @author Denis Van Nuffelen, Nicola Asuni
9141                 * @since 2.1.002 (2008-02-12)
9142                 */
9143                 public function TextField($name, $w, $h, $prop=array()) {
9144                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
9145                 }
9146                 
9147                 /*
9148                 * Creates a RadioButton field
9149                 * @param string $name field name
9150                 * @param int $w width
9151                 * @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>
9152                 * @access public
9153                 * @author Nicola Asuni
9154                 * @since 2.2.003 (2008-03-03)
9155                 */
9156                 public function RadioButton($name, $w, $prop=array()) {
9157                         if (!isset($prop['strokeColor'])) {
9158                                 $prop['strokeColor']='black';
9159                         }
9160                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
9161                 }
9162                 
9163                 /*
9164                 * Creates a List-box field
9165                 * @param string $name field name
9166                 * @param int $w width
9167                 * @param int $h height
9168                 * @param array $values array containing the list of values.
9169                 * @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>
9170                 * @access public
9171                 * @author Nicola Asuni
9172                 * @since 2.2.003 (2008-03-03)
9173                 */
9174                 public function ListBox($name, $w, $h, $values, $prop=array()) {
9175                         if (!isset($prop['strokeColor'])) {
9176                                 $prop['strokeColor'] = 'ltGray';
9177                         }
9178                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
9179                         $s = '';
9180                         foreach ($values as $value) {
9181                                 $s .= "'".addslashes($value)."',";
9182                         }
9183                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9184                 }
9185                 
9186                 /*
9187                 * Creates a Combo-box field
9188                 * @param string $name field name
9189                 * @param int $w width
9190                 * @param int $h height
9191                 * @param array $values array containing the list of values.
9192                 * @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>
9193                 * @access public
9194                 * @author Denis Van Nuffelen, Nicola Asuni
9195                 * @since 2.1.002 (2008-02-12)
9196                 */
9197                 public function ComboBox($name, $w, $h, $values, $prop=array()) {
9198                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
9199                         $s = '';
9200                         foreach ($values as $value) {
9201                                 $s .= "'".addslashes($value)."',";
9202                         }
9203                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9204                 }
9205                 
9206                 /*
9207                 * Creates a CheckBox field
9208                 * @param string $name field name
9209                 * @param int $w width
9210                 * @param boolean $checked define the initial state (default = false).
9211                 * @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>
9212                 * @access public
9213                 * @author Denis Van Nuffelen, Nicola Asuni
9214                 * @since 2.1.002 (2008-02-12)
9215                 */
9216                 public function CheckBox($name, $w, $checked=false, $prop=array()) {
9217                         $prop['value'] = ($checked ? 'Yes' : 'Off');
9218                         if (!isset($prop['strokeColor'])) {
9219                                 $prop['strokeColor'] = 'black';
9220                         }
9221                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
9222                 }
9223                 
9224                 /*
9225                 * Creates a button field
9226                 * @param string $name field name
9227                 * @param int $w width
9228                 * @param int $h height
9229                 * @param string $caption caption.
9230                 * @param string $action action triggered by the button (JavaScript code).
9231                 * @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>
9232                 * @access public
9233                 * @author Denis Van Nuffelen, Nicola Asuni
9234                 * @since 2.1.002 (2008-02-12)
9235                 */
9236                 public function Button($name, $w, $h, $caption, $action, $prop=array()) {
9237                         if (!isset($prop['strokeColor'])) {
9238                                 $prop['strokeColor'] = 'black';
9239                         }
9240                         if (!isset($prop['borderStyle'])) {
9241                                 $prop['borderStyle'] = 'beveled';
9242                         }
9243                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
9244                         $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
9245                         $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
9246                         $this->javascript .= 'f'.$name.".highlight='push';\n";
9247                         $this->javascript .= 'f'.$name.".print=false;\n";
9248                 }
9249                 
9250                 // END JAVASCRIPT - FORMS ------------------------------
9251                 
9252                 /*
9253                 * Enable Write permissions for PDF Reader.
9254                 * WARNING: This works only using the Adobe private key with the setSignature() method.
9255                 * @access protected
9256                 * @author Nicola Asuni
9257                 * @since 2.9.000 (2008-03-26)
9258                 */
9259                 protected function _putuserrights() {
9260                         if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) {
9261                                 return;
9262                         }
9263                         $this->_out('/Perms');
9264                         $this->_out('<<');
9265                         $this->_out('/UR3');
9266                         $this->_out('<<');
9267                         $this->_out('/Type/Sig');
9268                         $this->_out('/Filter/Adobe.PPKLite');
9269                         $this->_out('/SubFilter/adbe.pkcs7.detached');
9270                         $this->_out('/ByteRange[0 ********** ********** **********]');
9271                         $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9272                         if ($this->ur) {
9273                                 $this->_out('/Reference');
9274                                 $this->_out('[');
9275                                 $this->_out('<<');
9276                                 $this->_out('/Type/SigRef');
9277                                 $this->_out('/TransformMethod/UR3');
9278                                 $this->_out('/TransformParams');
9279                                 $this->_out('<<');
9280                                 $this->_out('/Type/TransformParams');
9281                                 $this->_out('/V/2.2');
9282                                 if (!$this->empty_string($this->ur_document)) {
9283                                         $this->_out('/Document['.$this->ur_document.']');
9284                                 }
9285                                 if (!$this->empty_string($this->ur_annots)) {
9286                                         $this->_out('/Annots['.$this->ur_annots.']');
9287                                 }
9288                                 if (!$this->empty_string($this->ur_form)) {
9289                                         $this->_out('/Form['.$this->ur_form.']');
9290                                 }
9291                                 if (!$this->empty_string($this->ur_signature)) {
9292                                         $this->_out('/Signature['.$this->ur_signature.']');
9293                                 }                       
9294                                 $this->_out('>>');
9295                                 $this->_out('>>');
9296                                 $this->_out(']');
9297                         }
9298                         $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9299                         $this->_out('>>');
9300                         $this->_out('>>');
9301                 }
9302                 
9303                 /*
9304                 * Add certification signature (DocMDP)
9305                 * @access protected
9306                 * @author Nicola Asuni
9307                 * @since 4.6.008 (2009-05-07)
9308                 */
9309                 protected function _putcertification() {
9310                         if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) {
9311                                 return;
9312                         }
9313                         $this->_out('/Perms');
9314                         $this->_out('<<');
9315                         $this->_out('/DocMDP');
9316                         $this->_out('<<');
9317                         $this->_out('/Type/Sig');
9318                         $this->_out('/Filter/Adobe.PPKLite');
9319                         $this->_out('/SubFilter/adbe.pkcs7.detached');
9320                         $this->_out('/ByteRange[0 ********** ********** **********]');
9321                         $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9322                         $this->_out('/Reference');
9323                         $this->_out('[');
9324                         $this->_out('<<');
9325                         $this->_out('/Type/SigRef');
9326                         $this->_out('/TransformMethod/DocMDP');
9327                         $this->_out('/TransformParams');
9328                         $this->_out('<<');
9329                         $this->_out('/Type/TransformParams');
9330                         $this->_out('/V/1.2');
9331                         $this->_out('/P '.$this->signature_data['cert_type'].'');
9332                         $this->_out('>>');
9333                         $this->_out('>>');
9334                         $this->_out(']');
9335                         $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9336                         if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
9337                                 $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
9338                         }
9339                         if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
9340                                 $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
9341                         }
9342                         if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
9343                                 $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
9344                         }
9345                         if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
9346                                 $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
9347                         }
9348                         $this->_out('>>');
9349                         $this->_out('>>');
9350                 }
9351                 
9352                 /*
9353                 * Set User's Rights for PDF Reader
9354                 * WARNING: This works only using the Adobe private key with the setSignature() method!.
9355                 * Check the PDF Reference 8.7.1 Transform Methods, 
9356                 * Table 8.105 Entries in the UR transform parameters dictionary
9357                 * @param boolean $enable if true enable user's rights on PDF reader
9358                 * @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.
9359                 * @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.
9360                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 
9361                 * @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.
9362                 * @access public
9363                 * @author Nicola Asuni
9364                 * @since 2.9.000 (2008-03-26)
9365                 */
9366                 public function setUserRights(
9367                                 $enable=true, 
9368                                 $document='/FullSave',
9369                                 $annots='/Create/Delete/Modify/Copy/Import/Export',
9370                                 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
9371                                 $signature='/Modify') {
9372                         $this->ur = $enable;
9373                         $this->ur_document = $document;
9374                         $this->ur_annots = $annots;
9375                         $this->ur_form = $form;
9376                         $this->ur_signature = $signature;
9377                 }
9378                 
9379                 /*
9380                 * Enable document signature (requires the OpenSSL Library).
9381                 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
9382                 * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
9383                 * @param mixed $private_key private key (string or filename prefixed with 'file://')
9384                 * @param string $private_key_password password
9385                 * @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.
9386                 * @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.
9387                 * @parm array $info array of option information: Name, Location, Reason, ContactInfo.
9388                 * @access public
9389                 * @author Nicola Asuni
9390                 * @since 4.6.005 (2009-04-24)
9391                 */
9392                 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
9393                         // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem
9394                         $this->sign = true;
9395                         $this->signature_data = array();
9396                         if (strlen($signing_cert) == 0) {
9397                                 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem';
9398                         }
9399                         if (strlen($private_key) == 0) {
9400                                 $private_key = $signing_cert;
9401                         }
9402                         $this->signature_data['signcert'] = $signing_cert;
9403                         $this->signature_data['privkey'] = $private_key;
9404                         $this->signature_data['password'] = $private_key_password;
9405                         $this->signature_data['extracerts'] = $extracerts;
9406                         $this->signature_data['cert_type'] = $cert_type;
9407                         $this->signature_data['info'] = array();
9408                 }
9409                 
9410                 /*
9411                 * Create a new page group.
9412                 * NOTE: call this function before calling AddPage()
9413                 * @param int $page starting group page (leave empty for next page).
9414                 * @access public
9415                 * @since 3.0.000 (2008-03-27)
9416                 */
9417                 public function startPageGroup($page='') {
9418                         if (empty($page)) {
9419                                 $page = $this->page + 1;
9420                         }
9421                         $this->newpagegroup[$page] = true;
9422                 }
9423
9424                 /**
9425                 * Defines an alias for the total number of pages.
9426                 * It will be substituted as the document is closed.
9427                 * @param string $alias The alias.
9428                 * @access public
9429                 * @since 1.4
9430                 * @see getAliasNbPages(), PageNo(), Footer()
9431                 */
9432                 public function AliasNbPages($alias='{nb}') {
9433                         $this->AliasNbPages = $alias;
9434                 }
9435                 
9436                 /**
9437                  * Returns the string alias used for the total number of pages.
9438          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9439                  * @return string
9440                  * @access public
9441                  * @since 4.0.018 (2008-08-08)
9442                  * @see AliasNbPages(), PageNo(), Footer()
9443                 */
9444                 public function getAliasNbPages() {
9445                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9446                                 return '{'.$this->AliasNbPages.'}';
9447             }
9448                         return $this->AliasNbPages;
9449                 }
9450
9451                 /**
9452                 * Defines an alias for the page number.
9453                 * It will be substituted as the document is closed.
9454                 * @param string $alias The alias.
9455                 * @access public
9456                 * @since 4.5.000 (2009-01-02)
9457                 * @see getAliasNbPages(), PageNo(), Footer()
9458                 */
9459                 public function AliasNumPage($alias='{pnb}') {
9460                         //Define an alias for total number of pages
9461                         $this->AliasNumPage = $alias;
9462                 }
9463                 
9464                 /**
9465                  * Returns the string alias used for the page number.
9466          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9467                  * @return string
9468                  * @access public
9469                  * @since 4.5.000 (2009-01-02)
9470                  * @see AliasNbPages(), PageNo(), Footer()
9471                 */
9472                 public function getAliasNumPage() {
9473                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9474                                 return '{'.$this->AliasNumPage.'}';
9475             }
9476                         return $this->AliasNumPage;
9477                 }
9478                 
9479                 /*
9480                 * Return the current page in the group.
9481                 * @return current page in the group
9482                 * @access public
9483                 * @since 3.0.000 (2008-03-27)
9484                 */
9485                 public function getGroupPageNo() {
9486                         return $this->pagegroups[$this->currpagegroup];
9487                 }
9488
9489                 /**
9490                 * Returns the current group page number formatted as a string.
9491                 * @access public
9492                 * @since 4.3.003 (2008-11-18)
9493                 * @see PaneNo(), formatPageNumber()
9494                 */
9495                 public function getGroupPageNoFormatted() {
9496                         return $this->formatPageNumber($this->getGroupPageNo());
9497         }
9498                 
9499                 /*
9500                  * Return the alias of the current page group
9501          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9502                  * (will be replaced by the total number of pages in this group).
9503                  * @return alias of the current page group
9504                  * @access public
9505                  * @since 3.0.000 (2008-03-27)
9506                 */
9507                 public function getPageGroupAlias() {
9508                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9509                                 return '{'.$this->currpagegroup.'}';
9510             }
9511                         return $this->currpagegroup;
9512                 }
9513                 
9514                 /*
9515                  * Return the alias for the page number on the current page group
9516          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9517                  * (will be replaced by the total number of pages in this group).
9518                  * @return alias of the current page group
9519                  * @access public
9520                  * @since 4.5.000 (2009-01-02)
9521                 */
9522                 public function getPageNumGroupAlias() {
9523                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9524                                 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
9525             }
9526                         return str_replace('{nb', '{pnb', $this->currpagegroup);
9527                 }
9528
9529                 /**
9530                 * Format the page numbers.
9531                 * This method can be overriden for custom formats.
9532                 * @param int $num page number
9533                 * @access protected
9534                 * @since 4.2.005 (2008-11-06)
9535                 */
9536                 protected function formatPageNumber($num) {
9537                         return number_format((float)$num, 0, '', '.');
9538                 }
9539
9540                 /**
9541                 * Format the page numbers on the Table Of Content.
9542                 * This method can be overriden for custom formats.
9543                 * @param int $num page number
9544                 * @access protected
9545                 * @since 4.5.001 (2009-01-04)
9546                 * @see addTOC()
9547                 */
9548                 protected function formatTOCPageNumber($num) {
9549                         return number_format((float)$num, 0, '', '.');
9550                 }
9551
9552         /**
9553                 * Returns the current page number formatted as a string.
9554                 * @access public
9555                 * @since 4.2.005 (2008-11-06)
9556                 * @see PaneNo(), formatPageNumber()
9557                 */
9558                 public function PageNoFormatted() {
9559                         return $this->formatPageNumber($this->PageNo());
9560         }
9561
9562         /*
9563                 * Put visibility settings.
9564                 * @access protected
9565                 * @since 3.0.000 (2008-03-27)
9566                 */
9567                 protected function _putocg() {
9568                         $this->_newobj();
9569                         $this->n_ocg_print = $this->n;
9570                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
9571                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
9572                         $this->_out('endobj');
9573                         $this->_newobj();
9574                         $this->n_ocg_view=$this->n;
9575                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
9576                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
9577                         $this->_out('endobj');
9578                 }
9579                 
9580                 /*
9581                 * Set the visibility of the successive elements.
9582                 * This can be useful, for instance, to put a background 
9583                 * image or color that will show on screen but won't print.
9584                 * @param string $v visibility mode. Legal values are: all, print, screen.
9585                 * @access public
9586                 * @since 3.0.000 (2008-03-27)
9587                 */
9588                 public function setVisibility($v) {
9589                         if ($this->openMarkedContent) {
9590                                 // close existing open marked-content
9591                                 $this->_out('EMC');
9592                                 $this->openMarkedContent = false;
9593                         }
9594                         switch($v) {
9595                                 case 'print': {
9596                                         $this->_out('/OC /OC1 BDC');
9597                                         $this->openMarkedContent = true;
9598                                         break;
9599                                 }
9600                                 case 'screen': {
9601                                         $this->_out('/OC /OC2 BDC');
9602                                         $this->openMarkedContent = true;
9603                                         break;
9604                                 }
9605                                 case 'all': {
9606                                         $this->_out('');
9607                                         break;
9608                                 }
9609                                 default: {
9610                                         $this->Error('Incorrect visibility: '.$v);
9611                                         break;
9612                                 }
9613                         }
9614                         $this->visibility = $v;
9615                 }
9616                 
9617                 /*
9618                 * Add transparency parameters to the current extgstate
9619                 * @param array $params parameters
9620                 * @return the number of extgstates
9621                 * @access protected
9622                 * @since 3.0.000 (2008-03-27)
9623                 */
9624                 protected function addExtGState($parms) {
9625                         $n = count($this->extgstates) + 1;
9626                         $this->extgstates[$n]['parms'] = $parms;
9627                         return $n;
9628                 }
9629                 
9630                 /*
9631                 * Add an extgstate
9632                 * @param array $gs extgstate
9633                 * @access protected
9634                 * @since 3.0.000 (2008-03-27)
9635                 */
9636                 protected function setExtGState($gs) {
9637                         $this->_out(sprintf('/GS%d gs', $gs));
9638                 }
9639                 
9640                 /*
9641                 * Put extgstates for object transparency
9642                 * @param array $gs extgstate
9643                 * @access protected
9644                 * @since 3.0.000 (2008-03-27)
9645                 */
9646                 protected function _putextgstates() {
9647                         $ne = count($this->extgstates);
9648                         for ($i = 1; $i <= $ne; ++$i) {
9649                                 $this->_newobj();
9650                                 $this->extgstates[$i]['n'] = $this->n;
9651                                 $this->_out('<</Type /ExtGState');
9652                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
9653                                         $this->_out('/'.$k.' '.$v);
9654                                 }
9655                                 $this->_out('>>');
9656                                 $this->_out('endobj');
9657                         }
9658                 }
9659                 
9660                 /*
9661                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
9662                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
9663                 * @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
9664                 * @access public
9665                 * @since 3.0.000 (2008-03-27)
9666                 */
9667                 public function setAlpha($alpha, $bm='Normal') {
9668                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
9669                         $this->setExtGState($gs);
9670                 }
9671
9672                 /*
9673                 * Set the default JPEG compression quality (1-100)
9674                 * @param int $quality JPEG quality, integer between 1 and 100
9675                 * @access public
9676                 * @since 3.0.000 (2008-03-27)
9677                 */
9678                 public function setJPEGQuality($quality) {
9679                         if (($quality < 1) OR ($quality > 100)) {
9680                                 $quality = 75;
9681                         }
9682                         $this->jpeg_quality = intval($quality);
9683                 }
9684                 
9685                 /*
9686                 * Set the default number of columns in a row for HTML tables.
9687                 * @param int $cols number of columns
9688                 * @access public
9689                 * @since 3.0.014 (2008-06-04)
9690                 */
9691                 public function setDefaultTableColumns($cols=4) { 
9692                         $this->default_table_columns = intval($cols); 
9693                 }
9694                 
9695                 /*
9696                 * Set the height of cell repect font height.
9697                 * @param int $h cell proportion respect font height (typical value = 1.25).
9698                 * @access public
9699                 * @since 3.0.014 (2008-06-04)
9700                 */
9701                 public function setCellHeightRatio($h) { 
9702                         $this->cell_height_ratio = $h; 
9703                 }
9704                 
9705                 /*
9706                 * return the height of cell repect font height.
9707                 * @access public
9708                 * @since 4.0.012 (2008-07-24)
9709                 */
9710                 public function getCellHeightRatio() { 
9711                         return $this->cell_height_ratio; 
9712                 }
9713                 
9714                 /*
9715                 * Set the PDF version (check PDF reference for valid values).
9716                 * Default value is 1.t
9717                 * @access public
9718                 * @since 3.1.000 (2008-06-09)
9719                 */
9720                 public function setPDFVersion($version='1.7') { 
9721                         $this->PDFVersion = $version;
9722                 }
9723                 
9724                 /*
9725                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
9726                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
9727                 * <ul>
9728                 * <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>
9729                 * <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>
9730                 * <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>
9731                 * <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>
9732                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
9733                 * <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>
9734                 * <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>
9735                 * <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>
9736                 * <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>
9737                 * <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>
9738                 * <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>
9739                 * <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>
9740                 * <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>
9741                 * <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>
9742                 * <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>
9743                 * <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>
9744                 * </ul>
9745                 * @param array $preferences array of options.
9746                 * @author Nicola Asuni
9747                 * @access public
9748                 * @since 3.1.000 (2008-06-09)
9749                 */
9750                 public function setViewerPreferences($preferences) { 
9751                         $this->viewer_preferences = $preferences;
9752                 }
9753                 
9754                 /**
9755                 * Paints a linear colour gradient.
9756                 * @param float $x abscissa of the top left corner of the rectangle.
9757                 * @param float $y ordinate of the top left corner of the rectangle.
9758                 * @param float $w width of the rectangle.
9759                 * @param float $h height of the rectangle.
9760                 * @param array $col1 first color (RGB components).
9761                 * @param array $col2 second color (RGB components).
9762                 * @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).
9763                 * @author Andreas Würmser, Nicola Asuni
9764                 * @since 3.1.000 (2008-06-09)
9765                 * @access public
9766                 */
9767                 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
9768                         $this->Clip($x, $y, $w, $h);
9769                         $this->Gradient(2, $col1, $col2, $coords);
9770                 }
9771                 
9772                 /**
9773                 * Paints a radial colour gradient.
9774                 * @param float $x abscissa of the top left corner of the rectangle.
9775                 * @param float $y ordinate of the top left corner of the rectangle.
9776                 * @param float $w width of the rectangle.
9777                 * @param float $h height of the rectangle.
9778                 * @param array $col1 first color (RGB components).
9779                 * @param array $col2 second color (RGB components).
9780                 * @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.
9781                 * @author Andreas Würmser, Nicola Asuni
9782                 * @since 3.1.000 (2008-06-09)
9783                 * @access public
9784                 */
9785                 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
9786                         $this->Clip($x, $y, $w, $h);
9787                         $this->Gradient(3, $col1, $col2, $coords);
9788                 }
9789                 
9790                 /**
9791                 * Paints a coons patch mesh.
9792                 * @param float $x abscissa of the top left corner of the rectangle.
9793                 * @param float $y ordinate of the top left corner of the rectangle.
9794                 * @param float $w width of the rectangle.
9795                 * @param float $h height of the rectangle.
9796                 * @param array $col1 first color (lower left corner) (RGB components).
9797                 * @param array $col2 second color (lower right corner) (RGB components).
9798                 * @param array $col3 third color (upper right corner) (RGB components).
9799                 * @param array $col4 fourth color (upper left corner) (RGB components).
9800                 * @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>
9801                 * @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
9802                 * @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
9803                 * @author Andreas Würmser, Nicola Asuni
9804                 * @since 3.1.000 (2008-06-09)
9805                 * @access public
9806                 */
9807                 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) {
9808                         $this->Clip($x, $y, $w, $h);        
9809                         $n = count($this->gradients) + 1;
9810                         $this->gradients[$n]['type'] = 6; //coons patch mesh
9811                         //check the coords array if it is the simple array or the multi patch array
9812                         if (!isset($coords[0]['f'])) {
9813                                 //simple array -> convert to multi patch array
9814                                 if (!isset($col1[1])) {
9815                                         $col1[1] = $col1[2] = $col1[0];
9816                                 }
9817                                 if (!isset($col2[1])) {
9818                                         $col2[1] = $col2[2] = $col2[0];
9819                                 }
9820                                 if (!isset($col3[1])) {
9821                                         $col3[1] = $col3[2] = $col3[0];
9822                                 }
9823                                 if (!isset($col4[1])) {
9824                                         $col4[1] = $col4[2] = $col4[0];
9825                                 }
9826                                 $patch_array[0]['f'] = 0;
9827                                 $patch_array[0]['points'] = $coords;
9828                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
9829                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
9830                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
9831                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
9832                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
9833                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
9834                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
9835                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
9836                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
9837                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
9838                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
9839                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
9840                         } else {
9841                                 //multi patch array
9842                                 $patch_array = $coords;
9843                         }
9844                         $bpcd = 65535; //16 BitsPerCoordinate
9845                         //build the data stream
9846                         $this->gradients[$n]['stream'] = '';
9847                         $count_patch = count($patch_array);
9848                         for ($i=0; $i < $count_patch; ++$i) {
9849                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
9850                                 $count_points = count($patch_array[$i]['points']);
9851                                 for ($j=0; $j < $count_points; ++$j) {
9852                                         //each point as 16 bit
9853                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
9854                                         if ($patch_array[$i]['points'][$j] < 0) {
9855                                                 $patch_array[$i]['points'][$j] = 0;
9856                                         }
9857                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
9858                                                 $patch_array[$i]['points'][$j] = $bpcd;
9859                                         }
9860                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
9861                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
9862                                 }
9863                                 $count_cols = count($patch_array[$i]['colors']);
9864                                 for ($j=0; $j < $count_cols; ++$j) {
9865                                         //each color component as 8 bit
9866                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
9867                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
9868                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
9869                                 }
9870                         }
9871                         //paint the gradient
9872                         $this->_out('/Sh'.$n.' sh');
9873                         //restore previous Graphic State
9874                         $this->_out('Q');
9875                 }
9876                 
9877                 /**
9878                 * Set a rectangular clipping area.
9879                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
9880                 * @param float $y ordinate of the top left corner of the rectangle.
9881                 * @param float $w width of the rectangle.
9882                 * @param float $h height of the rectangle.
9883                 * @author Andreas Würmser, Nicola Asuni
9884                 * @since 3.1.000 (2008-06-09)
9885                 * @access protected
9886                 */
9887                 protected function Clip($x, $y, $w, $h) {
9888                         if ($this->rtl) {
9889                                 $x = $this->w - $x - $w;
9890                         }
9891                         //save current Graphic State
9892                         $s = 'q';
9893                         //set clipping area
9894                         $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
9895                         //set up transformation matrix for gradient
9896                         $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
9897                         $this->_out($s);
9898                 }
9899                                 
9900                 /**
9901                 * Output gradient.
9902                 * @param int $type type of gradient.
9903                 * @param array $col1 first color (RGB components).
9904                 * @param array $col2 second color (RGB components).
9905                 * @param array $coords array of coordinates.
9906                 * @author Andreas Würmser, Nicola Asuni
9907                 * @since 3.1.000 (2008-06-09)
9908                 * @access protected
9909                 */
9910                 protected function Gradient($type, $col1, $col2, $coords) {
9911                         $n = count($this->gradients) + 1;
9912                         $this->gradients[$n]['type'] = $type;
9913                         if (!isset($col1[1])) {
9914                                 $col1[1]=$col1[2]=$col1[0];
9915                         }
9916                         $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
9917                         if (!isset($col2[1])) {
9918                                 $col2[1] = $col2[2] = $col2[0];
9919                         }
9920                         $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
9921                         $this->gradients[$n]['coords'] = $coords;
9922                         //paint the gradient
9923                         $this->_out('/Sh'.$n.' sh');
9924                         //restore previous Graphic State
9925                         $this->_out('Q');
9926                 }
9927                 
9928                 /**
9929                 * Output shaders.
9930                 * @author Andreas Würmser, Nicola Asuni
9931                 * @since 3.1.000 (2008-06-09)
9932                 * @access protected
9933                 */
9934                 function _putshaders() {
9935                         foreach ($this->gradients as $id => $grad) {  
9936                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
9937                                         $this->_newobj();
9938                                         $this->_out('<<');
9939                                         $this->_out('/FunctionType 2');
9940                                         $this->_out('/Domain [0.0 1.0]');
9941                                         $this->_out('/C0 ['.$grad['col1'].']');
9942                                         $this->_out('/C1 ['.$grad['col2'].']');
9943                                         $this->_out('/N 1');
9944                                         $this->_out('>>');
9945                                         $this->_out('endobj');
9946                                         $f1 = $this->n;
9947                                 }
9948                                 $this->_newobj();
9949                                 $this->_out('<<');
9950                                 $this->_out('/ShadingType '.$grad['type']);
9951                                 $this->_out('/ColorSpace /DeviceRGB');
9952                                 if ($grad['type'] == 2) {
9953                                         $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
9954                                         $this->_out('/Function '.$f1.' 0 R');
9955                                         $this->_out('/Extend [true true] ');
9956                                         $this->_out('>>');
9957                                 } elseif ($grad['type'] == 3) {
9958                                         //x0, y0, r0, x1, y1, r1
9959                                         //at this this time radius of inner circle is 0
9960                                         $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]));
9961                                         $this->_out('/Function '.$f1.' 0 R');
9962                                         $this->_out('/Extend [true true] ');
9963                                         $this->_out('>>');
9964                                 } elseif ($grad['type'] == 6) {
9965                                         $this->_out('/BitsPerCoordinate 16');
9966                                         $this->_out('/BitsPerComponent 8');
9967                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
9968                                         $this->_out('/BitsPerFlag 8');
9969                                         $this->_out('/Length '.strlen($grad['stream']));
9970                                         $this->_out('>>');
9971                                         $this->_putstream($grad['stream']);
9972                                 }
9973                                 $this->_out('endobj');
9974                                 $this->gradients[$id]['id'] = $this->n;
9975                         }
9976                 }
9977
9978                 /**
9979                 * Output an arc
9980                 * @author Maxime Delorme, Nicola Asuni
9981                 * @since 3.1.000 (2008-06-09)
9982                 * @access protected
9983                 */
9984                 protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
9985                         $h = $this->h;
9986                         $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));
9987                 }
9988                 
9989                 /**
9990                 * Draw the sector of a circle.
9991                 * It can be used for instance to render pie charts.
9992                 * @param float $xc abscissa of the center.
9993                 * @param float $yc ordinate of the center.
9994                 * @param float $r radius.
9995                 * @param float $a start angle (in degrees).
9996                 * @param float $b end angle (in degrees).
9997                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
9998                 * @param float $cw: indicates whether to go clockwise (default: true).
9999                 * @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.
10000                 * @author Maxime Delorme, Nicola Asuni
10001                 * @since 3.1.000 (2008-06-09)
10002                 * @access public
10003                 */
10004                 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
10005                         if ($this->rtl) {
10006                                 $xc = $this->w - $xc;
10007                         }
10008                         if ($cw) {
10009                                 $d = $b;
10010                                 $b = $o - $a;
10011                                 $a = $o - $d;
10012                         } else {
10013                                 $b += $o;
10014                                 $a += $o;
10015                         }
10016                         $a = ($a % 360) + 360;
10017                         $b = ($b % 360) + 360;
10018                         if ($a > $b) {
10019                                 $b +=360;
10020                         }
10021                         $b = $b / 360 * 2 * M_PI;
10022                         $a = $a / 360 * 2 * M_PI;
10023                         $d = $b - $a;
10024                         if ($d == 0 ) {
10025                                 $d = 2 * M_PI;
10026                         }
10027                         $k = $this->k;
10028                         $hp = $this->h;
10029                         if ($style=='F') {
10030                                 $op = 'f';
10031                         } elseif ($style=='FD' or $style=='DF') {
10032                                 $op = 'b';
10033                         } else {
10034                                 $op = 's';
10035                         }
10036                         if (sin($d/2)) {
10037                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
10038                         }
10039                         //first put the center
10040                         $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
10041                         //put the first point
10042                         $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
10043                         //draw the arc
10044                         if ($d < (M_PI/2)) {
10045                                 $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));
10046                         } else {
10047                                 $b = $a + $d/4;
10048                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
10049                                 $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));
10050                                 $a = $b;
10051                                 $b = $a + $d/4;
10052                                 $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));
10053                                 $a = $b;
10054                                 $b = $a + $d/4;
10055                                 $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) );
10056                                 $a = $b;
10057                                 $b = $a + $d/4;
10058                                 $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));
10059                         }
10060                         //terminate drawing
10061                         $this->_out($op);
10062                 }
10063                 
10064                 /**
10065                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
10066                 * Only vector drawing is supported, not text or bitmap. 
10067                 * 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).
10068                 * @param string $file Name of the file containing the image.
10069                 * @param float $x Abscissa of the upper-left corner.
10070                 * @param float $y Ordinate of the upper-left corner.
10071                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
10072                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
10073                 * @param mixed $link URL or identifier returned by AddLink().
10074                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
10075                 * @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>
10076                 * @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>
10077                 * @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>
10078                 * @author Valentin Schmidt, Nicola Asuni
10079                 * @since 3.1.000 (2008-06-09)
10080                 * @access public
10081                 */
10082                 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
10083                         if ($x === '') {
10084                                 $x = $this->x;
10085                         }
10086                         if ($y === '') {
10087                                 $y = $this->y;
10088                         }
10089                         $k = $this->k;
10090                         $data = file_get_contents($file);
10091                         if ($data === false) {
10092                                 $this->Error('EPS file not found: '.$file);
10093                         }
10094                         $regs = array();
10095                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
10096                         preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
10097                         if (count($regs) > 1) {
10098                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
10099                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
10100                                         $versexp = explode(' ', $version_str);
10101                                         $version = (float)array_pop($versexp);
10102                                         if ($version >= 9) {
10103                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
10104                                         }
10105                                 }
10106                         }
10107                         // strip binary bytes in front of PS-header
10108                         $start = strpos($data, '%!PS-Adobe');
10109                         if ($start > 0) {
10110                                 $data = substr($data, $start);
10111                         }
10112                         // find BoundingBox params
10113                         preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
10114                         if (count($regs) > 1) {
10115                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
10116                         } else {
10117                                 $this->Error('No BoundingBox found in EPS file: '.$file);
10118                         }
10119                         $start = strpos($data, '%%EndSetup');
10120                         if ($start === false) {
10121                                 $start = strpos($data, '%%EndProlog');
10122                         }
10123                         if ($start === false) {
10124                                 $start = strpos($data, '%%BoundingBox');
10125                         }
10126                         $data = substr($data, $start);
10127                         $end = strpos($data, '%%PageTrailer');
10128                         if ($end===false) {
10129                                 $end = strpos($data, 'showpage');
10130                         }
10131                         if ($end) {
10132                                 $data = substr($data, 0, $end);
10133                         }
10134                         if ($w > 0) {
10135                                 $scale_x = $w / (($x2 - $x1) / $k);
10136                                 if ($h > 0) {
10137                                         $scale_y = $h / (($y2 - $y1) / $k);
10138                                 } else {
10139                                         $scale_y = $scale_x;
10140                                         $h = ($y2 - $y1) / $k * $scale_y;
10141                                 }
10142                         } else {
10143                                 if ($h > 0) {
10144                                         $scale_y = $h / (($y2 - $y1) / $k);
10145                                         $scale_x = $scale_y;
10146                                         $w = ($x2-$x1) / $k * $scale_x;
10147                                 } else {
10148                                         $w = ($x2 - $x1) / $k;
10149                                         $h = ($y2 - $y1) / $k;
10150                                 }
10151                         }
10152                         // Check whether we need a new page first as this does not fit
10153                         if ($this->checkPageBreak($h, $y)) {
10154                                 $y = $this->GetY() + $this->cMargin;
10155                         }
10156                         // set bottomcoordinates
10157                         $this->img_rb_y = $y + $h;
10158                         // set alignment
10159                         if ($this->rtl) {
10160                                 if ($palign == 'L') {
10161                                         $ximg = $this->lMargin;
10162                                         // set right side coordinate
10163                                         $this->img_rb_x = $ximg + $w;
10164                                 } elseif ($palign == 'C') {
10165                                         $ximg = ($this->w - $x - $w) / 2;
10166                                         // set right side coordinate
10167                                         $this->img_rb_x = $ximg + $w;
10168                                 } else {
10169                                         $ximg = $this->w - $x - $w;
10170                                         // set left side coordinate
10171                                         $this->img_rb_x = $ximg;
10172                                 }
10173                         } else {
10174                                 if ($palign == 'R') {
10175                                         $ximg = $this->w - $this->rMargin - $w;
10176                                         // set left side coordinate
10177                                         $this->img_rb_x = $ximg;
10178                                 } elseif ($palign == 'C') {
10179                                         $ximg = ($this->w - $x - $w) / 2;
10180                                         // set right side coordinate
10181                                         $this->img_rb_x = $ximg + $w;
10182                                 } else {
10183                                         $ximg = $x;
10184                                         // set right side coordinate
10185                                         $this->img_rb_x = $ximg + $w;
10186                                 }
10187                         }
10188                         if ($useBoundingBox) {
10189                                 $dx = $ximg * $k - $x1;
10190                                 $dy = $y * $k - $y1;
10191                         } else {
10192                                 $dx = $ximg * $k;
10193                                 $dy = $y * $k;
10194                         }
10195                         // save the current graphic state
10196                         $this->_out('q'.$this->epsmarker);
10197                         // translate
10198                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
10199                         // scale
10200                         if (isset($scale_x)) {
10201                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
10202                         }
10203                         // handle pc/unix/mac line endings
10204                         preg_match('/[\r\n]+/s', $data, $regs);
10205                         $lines = explode($regs[0], $data);
10206                         $u=0;
10207                         $cnt = count($lines);
10208                         for ($i=0; $i < $cnt; ++$i) {
10209                                 $line = $lines[$i];
10210                                 if (($line == '') OR ($line{0} == '%')) {
10211                                         continue;
10212                                 }
10213                                 $len = strlen($line);
10214                                 $chunks = explode(' ', $line);
10215                                 $cmd = array_pop($chunks);
10216                                 // RGB
10217                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
10218                                         $b = array_pop($chunks); 
10219                                         $g = array_pop($chunks); 
10220                                         $r = array_pop($chunks);
10221                                         $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!
10222                                         continue;
10223                                 }
10224                                 switch ($cmd) {
10225                                         case 'm':
10226                                         case 'l':
10227                                         case 'v':
10228                                         case 'y':
10229                                         case 'c':
10230                                         case 'k':
10231                                         case 'K':
10232                                         case 'g':
10233                                         case 'G':
10234                                         case 's':
10235                                         case 'S':
10236                                         case 'J':
10237                                         case 'j':
10238                                         case 'w':
10239                                         case 'M':
10240                                         case 'd':
10241                                         case 'n':
10242                                         case 'v': {
10243                                                 $this->_out($line);
10244                                                 break;
10245                                         }
10246                                         case 'x': {// custom fill color
10247                                                 list($c,$m,$y,$k) = $chunks;
10248                                                 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
10249                                                 break;
10250                                         }
10251                                         case 'X': { // custom stroke color
10252                                                 list($c,$m,$y,$k) = $chunks;
10253                                                 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
10254                                                 break;
10255                                         }
10256                                         case 'Y':
10257                                         case 'N':
10258                                         case 'V':
10259                                         case 'L':
10260                                         case 'C': {
10261                                                 $line{$len-1} = strtolower($cmd);
10262                                                 $this->_out($line);
10263                                                 break;
10264                                         }
10265                                         case 'b':
10266                                         case 'B': {
10267                                                 $this->_out($cmd . '*');
10268                                                 break;
10269                                         }
10270                                         case 'f':
10271                                         case 'F': {
10272                                                 if ($u > 0) {
10273                                                         $isU = false;
10274                                                         $max = min($i+5, $cnt);
10275                                                         for ($j=$i+1; $j < $max; ++$j)
10276                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
10277                                                         if ($isU) {
10278                                                                 $this->_out('f*');
10279                                                         }
10280                                                 } else {
10281                                                         $this->_out('f*');
10282                                                 }
10283                                                 break;
10284                                         }
10285                                         case '*u': {
10286                                                 ++$u;
10287                                                 break;
10288                                         }
10289                                         case '*U': {
10290                                                 --$u;
10291                                                 break;
10292                                         }
10293                                 }
10294                         }
10295                         // restore previous graphic state
10296                         $this->_out($this->epsmarker.'Q');
10297                         if (!empty($border)) {
10298                                 $bx = $x;
10299                                 $by = $y;
10300                                 $this->x = $x;
10301                                 $this->y = $y;
10302                                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
10303                                 $this->x = $bx;
10304                                 $this->y = $by;
10305                         }
10306                         if ($link) {
10307                                 $this->Link($ximg, $y, $w, $h, $link, 0);
10308                         }
10309                         // set pointer to align the successive text/objects
10310                         switch($align) {
10311                                 case 'T':{
10312                                         $this->y = $y;
10313                                         $this->x = $this->img_rb_x;
10314                                         break;
10315                                 }
10316                                 case 'M':{
10317                                         $this->y = $y + round($h/2);
10318                                         $this->x = $this->img_rb_x;
10319                                         break;
10320                                 }
10321                                 case 'B':{
10322                                         $this->y = $this->img_rb_y;
10323                                         $this->x = $this->img_rb_x;
10324                                         break;
10325                                 }
10326                                 case 'N':{
10327                                         $this->SetY($this->img_rb_y);
10328                                         break;
10329                                 }
10330                                 default:{
10331                                         break;
10332                                 }
10333                         }
10334                         $this->endlinex = $this->img_rb_x;
10335                 }
10336                 
10337                 /**
10338                  * Set document barcode.
10339                  * @param string $bc barcode
10340                  * @access public
10341                  */
10342                 public function setBarcode($bc='') {
10343                         $this->barcode = $bc;
10344                 }
10345                 
10346                 /**
10347                  * Get current barcode.
10348                  * @return string
10349                  * @access public
10350                  * @since 4.0.012 (2008-07-24)
10351                  */
10352                 public function getBarcode() {
10353                         return $this->barcode;
10354                 }
10355                 
10356                 /**
10357                  * Print a Linear Barcode.
10358                  * @param string $code code to print
10359                  * @param string $type type of barcode.
10360                  * @param int $x x position in user units
10361                  * @param int $y y position in user units
10362                  * @param int $w width in user units
10363                  * @param int $h height in user units
10364                  * @param float $xres width of the smallest bar in user units
10365                  * @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>
10366                  * @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>
10367                  * @author Nicola Asuni
10368                  * @since 3.1.000 (2008-06-09)
10369                  * @access public
10370                  */
10371                 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
10372                         if ($this->empty_string($code)) {
10373                                 return;
10374                         }
10375                         require_once(dirname(__FILE__).'/barcodes.php');
10376                         // save current graphic settings
10377                         $gvars = $this->getGraphicVars();
10378                         // create new barcode object
10379                         $barcodeobj = new TCPDFBarcode($code, $type);
10380                         $arrcode = $barcodeobj->getBarcodeArray();
10381                         if ($arrcode === false) {
10382                                 $this->Error('Error in 1D barcode string');
10383                         }
10384                         // set default values
10385                         if (!isset($style['position'])) {
10386                                 if ($this->rtl) {
10387                                         $style['position'] = 'R';
10388                                 } else {
10389                                         $style['position'] = 'L';
10390                                 }
10391                         }
10392                         if (!isset($style['padding'])) {
10393                                 $style['padding'] = 0;
10394                         }
10395                         if (!isset($style['fgcolor'])) {
10396                                 $style['fgcolor'] = array(0,0,0); // default black
10397                         }
10398                         if (!isset($style['bgcolor'])) {
10399                                 $style['bgcolor'] = false; // default transparent
10400                         }
10401                         if (!isset($style['border'])) {
10402                                 $style['border'] = false;
10403                         }
10404                         if (!isset($style['text'])) {
10405                                 $style['text'] = false;
10406                                 $fontsize = 0;
10407                         }
10408                         if ($style['text'] AND isset($style['font'])) {
10409                                 if (isset($style['fontsize'])) {
10410                                         $fontsize = $style['fontsize'];
10411                                 } else {
10412                                         $fontsize = 0;
10413                                 }
10414                                 $this->SetFont($style['font'], '', $fontsize);
10415                         }
10416                         if (!isset($style['stretchtext'])) {
10417                                 $style['stretchtext'] = 4;
10418                         }
10419                         // set foreground color
10420                         $this->SetDrawColorArray($style['fgcolor']);
10421                         $this->SetTextColorArray($style['fgcolor']);
10422                         if ($this->empty_string($w) OR ($w <= 0)) {
10423                                 if ($this->rtl) {
10424                                         $w = $this->x - $this->lMargin;
10425                                 } else {
10426                                         $w = $this->w - $this->rMargin - $this->x;
10427                                 }
10428                         }
10429                         if ($this->empty_string($x)) {
10430                                 $x = $this->GetX();
10431                         }
10432                         if ($this->rtl) {
10433                                 $x = $this->w - $x;
10434                         }
10435                         if ($this->empty_string($y)) {
10436                                 $y = $this->GetY();
10437                         }
10438                         if ($this->empty_string($xres)) {
10439                                 $xres = 0.4;
10440                         }
10441                         $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
10442                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
10443                         if ($this->empty_string($h) OR ($h <= 0)) {
10444                                 $h = 10 + $extraspace;
10445                         }
10446                         if ($this->checkPageBreak($h)) {
10447                                 $y = $this->y;
10448                         }
10449                         // maximum bar heigth
10450                         $barh = $h - $extraspace;
10451                         switch ($style['position']) {
10452                                 case 'L': { // left
10453                                         if ($this->rtl) {
10454                                                 $xpos = $x - $w;
10455                                         } else {
10456                                                 $xpos = $x;
10457                                         }
10458                                         break;
10459                                 }
10460                                 case 'C': { // center
10461                                         $xdiff = (($w - $fbw) / 2);
10462                                         if ($this->rtl) {
10463                                                 $xpos = $x - $w + $xdiff;
10464                                         } else {
10465                                                 $xpos = $x + $xdiff;
10466                                         }
10467                                         break;
10468                                 }
10469                                 case 'R': { // right
10470                                         if ($this->rtl) {
10471                                                 $xpos = $x - $fbw;
10472                                         } else {
10473                                                 $xpos = $x + $w - $fbw;
10474                                         }
10475                                         break;
10476                                 }
10477                                 case 'S': { // stretch
10478                                         $fbw = $w;
10479                                         $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
10480                                         if ($this->rtl) {
10481                                                 $xpos = $x - $w;
10482                                         } else {
10483                                                 $xpos = $x;
10484                                         }
10485                                         break;
10486                                 }
10487                         }
10488                         $xpos_rect = $xpos;
10489                         $xpos = $xpos_rect + $style['padding'];
10490                         $xpos_text = $xpos;
10491                         // barcode is always printed in LTR direction
10492                         $tempRTL = $this->rtl;
10493                         $this->rtl = false;
10494                         // print background color
10495                         if ($style['bgcolor']) {
10496                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']);
10497                         } elseif ($style['border']) {
10498                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
10499                         }
10500                         // print bars
10501                         if ($arrcode !== false) {
10502                                 foreach ($arrcode['bcode'] as $k => $v) {
10503                                         $bw = ($v['w'] * $xres);
10504                                         if ($v['t']) {
10505                                                 // draw a vertical bar
10506                                                 $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
10507                                                 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
10508                                         }
10509                                         $xpos += $bw;
10510                                 }
10511                         }
10512                         // print text
10513                         if ($style['text']) {
10514                                 // print text
10515                                 $this->x = $xpos_text;
10516                                 $this->y = $y + $style['padding'] + $barh; 
10517                                 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
10518                         }
10519                         // restore original direction
10520                         $this->rtl = $tempRTL;
10521                         // restore previous settings
10522                         $this->setGraphicVars($gvars);
10523                         // set bottomcoordinates
10524                         $this->img_rb_y = $y + $h;
10525                         if ($this->rtl) {
10526                                 // set left side coordinate
10527                                 $this->img_rb_x = ($this->w - $x - $w);
10528                         } else {
10529                                 // set right side coordinate
10530                                 $this->img_rb_x = $x + $w;
10531                         }
10532                         // set pointer to align the successive text/objects
10533                         switch($align) {
10534                                 case 'T':{
10535                                         $this->y = $y;
10536                                         $this->x = $this->img_rb_x;
10537                                         break;
10538                                 }
10539                                 case 'M':{
10540                                         $this->y = $y + round($h/2);
10541                                         $this->x = $this->img_rb_x;
10542                                         break;
10543                                 }
10544                                 case 'B':{
10545                                         $this->y = $this->img_rb_y;
10546                                         $this->x = $this->img_rb_x;
10547                                         break;
10548                                 }
10549                                 case 'N':{
10550                                         $this->SetY($this->img_rb_y);
10551                                         break;
10552                                 }
10553                                 default:{
10554                                         break;
10555                                 }
10556                         }
10557                 }
10558                 
10559                 /**
10560                  * This function is DEPRECATED, please use the new write1DBarcode() function.
10561                  * @param int $x x position in user units
10562                  * @param int $y y position in user units
10563                  * @param int $w width in user units
10564                  * @param int $h height position in user units
10565                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
10566                  * @param string $style barcode style
10567                  * @param string $font font for text
10568                  * @param int $xres x resolution
10569                  * @param string $code code to print
10570                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
10571                  * @access public
10572                  * @see write1DBarcode()
10573                  */
10574                 public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
10575                         // convert old settings for the new write1DBarcode() function.
10576                         $xres = 1 / $xres;
10577                         $newstyle = array(
10578                                 'position' => 'L',
10579                                 'border' => false,
10580                                 'padding' => 0,
10581                                 'fgcolor' => array(0,0,0),
10582                                 'bgcolor' => false,
10583                                 'text' => true,
10584                                 'font' => $font,
10585                                 'fontsize' => 8,
10586                                 'stretchtext' => 4
10587                         );
10588                         if ($style & 1) {
10589                                 $newstyle['border'] = true;
10590                         }
10591                         if ($style & 2) {
10592                                 $newstyle['bgcolor'] = false;
10593                         }
10594                         if ($style & 4) {
10595                                 $newstyle['position'] = 'C';
10596                         } elseif ($style & 8) {
10597                                 $newstyle['position'] = 'L';
10598                         } elseif ($style & 16) {
10599                                 $newstyle['position'] = 'R';
10600                         }
10601                         if ($style & 128) {
10602                                 $newstyle['text'] = true;
10603                         }
10604                         if ($style & 256) {
10605                                 $newstyle['stretchtext'] = 4;
10606                         }
10607                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
10608                 }
10609                 
10610                 /**
10611                  * Print 2D Barcode.
10612                  * @param string $code code to print
10613                  * @param string $type type of barcode.
10614                  * @param int $x x position in user units
10615                  * @param int $y y position in user units
10616                  * @param int $w width in user units
10617                  * @param int $h height in user units
10618                  * @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>
10619                  * @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>
10620                  * @author Nicola Asuni
10621                  * @since 4.5.037 (2009-04-07)
10622                  * @access public
10623                  */
10624                 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
10625                         if ($this->empty_string($code)) {
10626                                 return;
10627                         }
10628                         require_once(dirname(__FILE__).'/2dbarcodes.php');
10629                         // save current graphic settings
10630                         $gvars = $this->getGraphicVars();
10631                         // create new barcode object
10632                         $barcodeobj = new TCPDF2DBarcode($code, $type);
10633                         $arrcode = $barcodeobj->getBarcodeArray();
10634                         if ($arrcode === false) {
10635                                 $this->Error('Error in 2D barcode string');
10636                         }
10637                         // set default values
10638                         if (!isset($style['padding'])) {
10639                                 $style['padding'] = 0;
10640                         }
10641                         if (!isset($style['fgcolor'])) {
10642                                 $style['fgcolor'] = array(0,0,0); // default black
10643                         }
10644                         if (!isset($style['bgcolor'])) {
10645                                 $style['bgcolor'] = false; // default transparent
10646                         }
10647                         if (!isset($style['border'])) {
10648                                 $style['border'] = false;
10649                         }
10650                         // set foreground color
10651                         $this->SetDrawColorArray($style['fgcolor']);
10652                         if ($this->empty_string($x)) {
10653                                 $x = $this->GetX();
10654                         }
10655                         if ($this->rtl) {
10656                                 $x = $this->w - $x;
10657                         }
10658                         if ($this->empty_string($y)) {
10659                                 $y = $this->GetY();
10660                         }
10661                         if ($this->empty_string($w) OR ($w <= 0)) {
10662                                 if ($this->rtl) {
10663                                         $w = $x - $this->lMargin;
10664                                 } else {
10665                                         $w = $this->w - $this->rMargin - $x;
10666                                 }
10667                         }
10668                         if ($this->empty_string($h) OR ($h <= 0)) {
10669                                 // 2d barcodes are square by default
10670                                 $h = $w;
10671                         }
10672                         if ($this->checkPageBreak($h)) {
10673                                 $y = $this->y;
10674                         }
10675                         // calculate barcode size (excluding padding)
10676                         $bw = $w - (2 * $style['padding']);
10677                         $bh = $h - (2 * $style['padding']);
10678                         // calculate starting coordinates
10679                         if ($this->rtl) {
10680                                 $xpos = $x - $w;
10681                         } else {
10682                                 $xpos = $x;
10683                         }
10684                         $xpos += $style['padding'];
10685                         $ypos = $y + $style['padding'];
10686                         // barcode is always printed in LTR direction
10687                         $tempRTL = $this->rtl;
10688                         $this->rtl = false;
10689                         // print background color
10690                         if ($style['bgcolor']) {
10691                                 $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']);
10692                         } elseif ($style['border']) {
10693                                 $this->Rect($x, $y, $w, $h, 'D');
10694                         }
10695                         // print barcode cells
10696                         if ($arrcode !== false) {
10697                                 $rows = $arrcode['num_rows'];
10698                                 $cols = $arrcode['num_cols'];
10699                                 // calculate dimension of single barcode cell
10700                                 $cw = $bw / $cols;
10701                                 $ch = $bh / $rows;
10702                                 // for each row
10703                                 for ($r = 0; $r < $rows; ++$r) {
10704                                         $xr = $xpos;
10705                                         // for each column
10706                                         for ($c = 0; $c < $cols; ++$c) {
10707                                                 if ($arrcode['bcode'][$r][$c] == 1) {
10708                                                         // draw a single barcode cell
10709                                                         $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
10710                                                 }
10711                                                 $xr += $cw;
10712                                         }
10713                                         $ypos += $ch;
10714                                 }
10715                         }
10716                         // restore original direction
10717                         $this->rtl = $tempRTL;
10718                         // restore previous settings
10719                         $this->setGraphicVars($gvars);
10720                         // set bottomcoordinates
10721                         $this->img_rb_y = $y + $h;
10722                         if ($this->rtl) {
10723                                 // set left side coordinate
10724                                 $this->img_rb_x = ($this->w - $x - $w);
10725                         } else {
10726                                 // set right side coordinate
10727                                 $this->img_rb_x = $x + $w;
10728                         }
10729                         // set pointer to align the successive text/objects
10730                         switch($align) {
10731                                 case 'T':{
10732                                         $this->y = $y;
10733                                         $this->x = $this->img_rb_x;
10734                                         break;
10735                                 }
10736                                 case 'M':{
10737                                         $this->y = $y + round($h/2);
10738                                         $this->x = $this->img_rb_x;
10739                                         break;
10740                                 }
10741                                 case 'B':{
10742                                         $this->y = $this->img_rb_y;
10743                                         $this->x = $this->img_rb_x;
10744                                         break;
10745                                 }
10746                                 case 'N':{
10747                                         $this->SetY($this->img_rb_y);
10748                                         break;
10749                                 }
10750                                 default:{
10751                                         break;
10752                                 }
10753                         }
10754                 }
10755                 
10756                 /**
10757                  * Returns an array containing current margins:
10758                  * <ul>
10759                                 <li>$ret['left'] = left  margin</li>
10760                                 <li>$ret['right'] = right margin</li>
10761                                 <li>$ret['top'] = top margin</li>
10762                                 <li>$ret['bottom'] = bottom margin</li>
10763                                 <li>$ret['header'] = header margin</li>
10764                                 <li>$ret['footer'] = footer margin</li>
10765                                 <li>$ret['cell'] = cell margin</li>
10766                  * </ul>
10767                  * @return array containing all margins measures 
10768                  * @access public
10769                  * @since 3.2.000 (2008-06-23)
10770                  */
10771                 public function getMargins() {
10772                         $ret = array(
10773                                 'left' => $this->lMargin,
10774                                 'right' => $this->rMargin,
10775                                 'top' => $this->tMargin,
10776                                 'bottom' => $this->bMargin,
10777                                 'header' => $this->header_margin,
10778                                 'footer' => $this->footer_margin,
10779                                 'cell' => $this->cMargin,
10780                         );
10781                         return $ret;
10782                 }
10783                 
10784                 /**
10785                  * Returns an array containing original margins:
10786                  * <ul>
10787                                 <li>$ret['left'] = left  margin</li>
10788                                 <li>$ret['right'] = right margin</li>
10789                  * </ul>
10790                  * @return array containing all margins measures 
10791                  * @access public
10792                  * @since 4.0.012 (2008-07-24)
10793                  */
10794                 public function getOriginalMargins() {
10795                         $ret = array(
10796                                 'left' => $this->original_lMargin,
10797                                 'right' => $this->original_rMargin
10798                         );
10799                         return $ret;
10800                 }
10801                 
10802                 /**
10803                  * Returns the current font size.
10804                  * @return current font size
10805                  * @access public
10806                  * @since 3.2.000 (2008-06-23)
10807                  */
10808                 public function getFontSize() {
10809                         return $this->FontSize;
10810                 }
10811                 
10812                 /**
10813                  * Returns the current font size in points unit.
10814                  * @return current font size in points unit
10815                  * @access public
10816                  * @since 3.2.000 (2008-06-23)
10817                  */
10818                 public function getFontSizePt() {
10819                         return $this->FontSizePt;
10820                 }
10821
10822                 /**
10823                  * Returns the current font family name.
10824                  * @return string current font family name
10825                  * @access public
10826                  * @since 4.3.008 (2008-12-05)
10827                  */
10828                 public function getFontFamily() {
10829                         return $this->FontFamily;
10830                 }
10831
10832                 /**
10833                  * Returns the current font style.
10834                  * @return string current font style
10835                  * @access public
10836                  * @since 4.3.008 (2008-12-05)
10837                  */
10838                 public function getFontStyle() {
10839                         return $this->FontStyle;
10840                 }
10841                 
10842                 /**
10843                  * Prints a cell (rectangular area) with optional borders, background color and html text string. 
10844                  * 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 />
10845                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
10846                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
10847                  * @param float $h Cell minimum height. The cell extends automatically if needed.
10848                  * @param float $x upper-left corner X coordinate
10849                  * @param float $y upper-left corner Y coordinate
10850                  * @param string $html html text to print. Default value: empty string.
10851                  * @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>
10852                  * @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>
10853         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
10854                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
10855                  * @param boolean $reseth if true reset the last cell height (default true).
10856                  * @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>
10857                  * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
10858                  * @access public
10859                  * @uses MultiCell()
10860                  * @see Multicell(), writeHTML()
10861                  */
10862                 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
10863                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
10864                 }
10865                 
10866                 /**
10867                  * Returns the HTML DOM array.
10868                  * <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>
10869                  * @param string $html html code
10870                  * @return array
10871                  * @access protected
10872                  * @since 3.2.000 (2008-06-20)
10873                  */
10874                 protected function getHtmlDomArray($html) {
10875                         // remove all unsupported tags (the line below lists all supported tags)
10876                         $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>');
10877                         //replace some blank characters
10878                         $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
10879                         $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);
10880                         $html = preg_replace('@(\r\n|\r)@', "\n", $html);
10881                         $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
10882                         $html = strtr($html, $repTable);
10883                         while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html)) {
10884                                 // preserve newlines on <pre> tag
10885                                 $html = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html);
10886                         }
10887                         $html = str_replace("\n", ' ', $html);
10888                         // remove extra spaces from code
10889                         $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
10890                         $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
10891                         $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);
10892                         $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
10893                         $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
10894                         $html = preg_replace('/<img/', ' <img', $html);
10895                         $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span></span>', $html);
10896                         $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
10897                         // trim string
10898                         $html = preg_replace('/^[\s]+/', '', $html);
10899                         $html = preg_replace('/[\s]+$/', '', $html);
10900                         // pattern for generic tag
10901                         $tagpattern = '/(<[^>]+>)/';
10902                         // explodes the string
10903                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
10904                         // count elements
10905                         $maxel = count($a);
10906                         $elkey = 0;
10907                         $key = 0;
10908                         // create an array of elements
10909                         $dom = array();
10910                         $dom[$key] = array();
10911                         // set first void element
10912                         $dom[$key]['tag'] = false;
10913                         $dom[$key]['value'] = '';
10914                         $dom[$key]['parent'] = 0;
10915                         $dom[$key]['fontname'] = $this->FontFamily;
10916                         $dom[$key]['fontstyle'] = $this->FontStyle;
10917                         $dom[$key]['fontsize'] = $this->FontSizePt;
10918                         $dom[$key]['bgcolor'] = false;
10919                         $dom[$key]['fgcolor'] = $this->fgcolor;
10920                         $dom[$key]['align'] = '';
10921                         $dom[$key]['listtype'] = '';
10922                         $thead = false; // true when we are inside the THEAD tag
10923                         ++$key;
10924                         $level = array();
10925                         array_push($level, 0); // root
10926                         while ($elkey < $maxel) {
10927                                 $dom[$key] = array();
10928                                 $element = $a[$elkey];
10929                                 $dom[$key]['elkey'] = $elkey;
10930                                 if (preg_match($tagpattern, $element)) {
10931                                         // html tag
10932                                         $element = substr($element, 1, -1);
10933                                         // get tag name
10934                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
10935                                         $tagname = strtolower($tag[1]);
10936                                         // check if we are inside a table header
10937                                         if ($tagname == 'thead') {
10938                                                 if ($element{0} == '/') {
10939                                                         $thead = false;
10940                                                 } else {
10941                                                         $thead = true;
10942                                                 }
10943                                                 ++$elkey;
10944                                                 continue;
10945                                         }
10946                                         $dom[$key]['tag'] = true;
10947                                         $dom[$key]['value'] = $tagname;
10948                                         if ($element{0} == '/') {
10949                                                 // closing html tag
10950                                                 $dom[$key]['opening'] = false;
10951                                                 $dom[$key]['parent'] = end($level);
10952                                                 array_pop($level);
10953                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
10954                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
10955                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
10956                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
10957                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
10958                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
10959                                                 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
10960                                                         $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
10961                                                 }
10962                                                 // set the number of columns in table tag
10963                                                 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
10964                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
10965                                                 }
10966                                                 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
10967                                                         $dom[($dom[$key]['parent'])]['content'] = '';
10968                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
10969                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
10970                                                         }
10971                                                         $key = $i;
10972                                                 }
10973                                                 // store header rows on a new table
10974                                                 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) {
10975                                                         if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
10976                                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
10977                                                         }
10978                                                         for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
10979                                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
10980                                                         }
10981                                                 }
10982                                                 if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
10983                                                         $dom[($dom[$key]['parent'])]['thead'] .= '</table>';
10984                                                 }
10985                                         } else {
10986                                                 // opening html tag
10987                                                 $dom[$key]['opening'] = true;
10988                                                 $dom[$key]['parent'] = end($level);
10989                                                 if (substr($element, -1, 1) != '/') {
10990                                                         // not self-closing tag
10991                                                         array_push($level, $key);
10992                                                         $dom[$key]['self'] = false;
10993                                                 } else {
10994                                                         $dom[$key]['self'] = true;
10995                                                 }
10996                                                 // copy some values from parent
10997                                                 $parentkey = 0;
10998                                                 if ($key > 0) {
10999                                                         $parentkey = $dom[$key]['parent'];
11000                                                         $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
11001                                                         $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
11002                                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
11003                                                         $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
11004                                                         $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
11005                                                         $dom[$key]['align'] = $dom[$parentkey]['align'];
11006                                                         $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11007                                                 }
11008                                                 // get attributes
11009                                                 preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
11010                                                 $dom[$key]['attribute'] = array(); // reset attribute array
11011                                                 while (list($id, $name) = each($attr_array[1])) {
11012                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
11013                                                 }
11014                                                 // split style attributes
11015                                                 if (isset($dom[$key]['attribute']['style'])) {
11016                                                         // get style attributes
11017                                                         preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
11018                                                         $dom[$key]['style'] = array(); // reset style attribute array
11019                                                         while (list($id, $name) = each($style_array[1])) {
11020                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
11021                                                         }
11022                                                         // --- get some style attributes ---
11023                                                         if (isset($dom[$key]['style']['font-family'])) {
11024                                                                 // font family
11025                                                                 if (isset($dom[$key]['style']['font-family'])) {
11026                                                                         $fontslist = explode(',', strtolower($dom[$key]['style']['font-family']));
11027                                                                         foreach ($fontslist as $font) {
11028                                                                                 $font = trim(strtolower($font));
11029                                                                                 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11030                                                                                         $dom[$key]['fontname'] = $font;
11031                                                                                         break;
11032                                                                                 }
11033                                                                         }
11034                                                                 }
11035                                                         }
11036                                                         // list-style-type
11037                                                         if (isset($dom[$key]['style']['list-style-type'])) {
11038                                                                 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
11039                                                                 if ($dom[$key]['listtype'] == 'inherit') {
11040                                                                         $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11041                                                                 }
11042                                                         }
11043                                                         // font size
11044                                                         if (isset($dom[$key]['style']['font-size'])) {
11045                                                                 $fsize = trim($dom[$key]['style']['font-size']);
11046                                                                 switch ($fsize) {
11047                                                                         // absolute-size
11048                                                                         case 'xx-small': {
11049                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
11050                                                                                 break;
11051                                                                         }
11052                                                                         case 'x-small': {
11053                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
11054                                                                                 break;
11055                                                                         }
11056                                                                         case 'small': {
11057                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
11058                                                                                 break;
11059                                                                         }
11060                                                                         case 'medium': {
11061                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
11062                                                                                 break;
11063                                                                         }
11064                                                                         case 'large': {
11065                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
11066                                                                                 break;
11067                                                                         }
11068                                                                         case 'x-large': {
11069                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
11070                                                                                 break;
11071                                                                         }
11072                                                                         case 'xx-large': {
11073                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
11074                                                                                 break;
11075                                                                         }
11076                                                                         // relative-size
11077                                                                         case 'smaller': {
11078                                                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
11079                                                                                 break;
11080                                                                         }
11081                                                                         case 'larger': {
11082                                                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
11083                                                                                 break;
11084                                                                         }
11085                                                                         default: {
11086                                                                                 $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
11087                                                                         }
11088                                                                 }
11089                                                         }
11090                                                         // font style
11091                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
11092                                                                 $dom[$key]['fontstyle'] .= 'B';
11093                                                         }
11094                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
11095                                                                 $dom[$key]['fontstyle'] .= '"I';
11096                                                         }
11097                                                         // font color
11098                                                         if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
11099                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
11100                                                         }
11101                                                         // background color
11102                                                         if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
11103                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
11104                                                         }
11105                                                         // text-decoration
11106                                                         if (isset($dom[$key]['style']['text-decoration'])) {
11107                                                                 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
11108                                                                 foreach ($decors as $dec) {
11109                                                                         $dec = trim($dec);
11110                                                                         if (!$this->empty_string($dec)) {
11111                                                                                 if ($dec{0} == 'u') {
11112                                                                                         $dom[$key]['fontstyle'] .= 'U';
11113                                                                                 } elseif ($dec{0} == 'l') {
11114                                                                                         $dom[$key]['fontstyle'] .= 'D';
11115                                                                                 }
11116                                                                         }
11117                                                                 }
11118                                                         }
11119                                                         // check for width attribute
11120                                                         if (isset($dom[$key]['style']['width'])) {
11121                                                                 $dom[$key]['width'] = $dom[$key]['style']['width'];
11122                                                         }
11123                                                         // check for height attribute
11124                                                         if (isset($dom[$key]['style']['height'])) {
11125                                                                 $dom[$key]['height'] = $dom[$key]['style']['height'];
11126                                                         }
11127                                                         // check for text alignment
11128                                                         if (isset($dom[$key]['style']['text-align'])) {
11129                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
11130                                                         }
11131                                                         // check for border attribute
11132                                                         if (isset($dom[$key]['style']['border'])) {
11133                                                                 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
11134                                                         }
11135                                                 }
11136                                                 // check for font tag
11137                                                 if ($dom[$key]['value'] == 'font') {
11138                                                         // font family
11139                                                         if (isset($dom[$key]['attribute']['face'])) {
11140                                                                 $fontslist = explode(',', strtolower($dom[$key]['attribute']['face']));
11141                                                                 foreach ($fontslist as $font) {
11142                                                                         $font = trim(strtolower($font));
11143                                                                         if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11144                                                                                 $dom[$key]['fontname'] = $font;
11145                                                                                 break;
11146                                                                         }
11147                                                                 }
11148                                                         }
11149                                                         // font size
11150                                                         if (isset($dom[$key]['attribute']['size'])) {
11151                                                                 if ($key > 0) {
11152                                                                         if ($dom[$key]['attribute']['size']{0} == '+') {
11153                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
11154                                                                         } elseif ($dom[$key]['attribute']['size']{0} == '-') {
11155                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
11156                                                                         } else {
11157                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11158                                                                         }
11159                                                                 } else {
11160                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11161                                                                 }
11162                                                         }
11163                                                 }
11164                                                 // force natural alignment for lists
11165                                                 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
11166                                                         AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
11167                                                         if ($this->rtl) {
11168                                                                 $dom[$key]['align'] = 'R';
11169                                                         } else {
11170                                                                 $dom[$key]['align'] = 'L';
11171                                                         }
11172                                                 }
11173                                                 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
11174                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
11175                                                 }
11176                                                 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
11177                                                         $dom[$key]['fontstyle'] .= 'B';
11178                                                 }
11179                                                 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
11180                                                         $dom[$key]['fontstyle'] .= 'I';
11181                                                 }
11182                                                 if ($dom[$key]['value'] == 'u') {
11183                                                         $dom[$key]['fontstyle'] .= 'U';
11184                                                 }
11185                                                 if ($dom[$key]['value'] == 'del') {
11186                                                         $dom[$key]['fontstyle'] .= 'D';
11187                                                 }
11188                                                 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
11189                                                         $dom[$key]['fontname'] = $this->default_monospaced_font;
11190                                                 }
11191                                                 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
11192                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
11193                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
11194                                                         $dom[$key]['fontstyle'] .= 'B';
11195                                                 }
11196                                                 if (($dom[$key]['value'] == 'table')) {
11197                                                         $dom[$key]['rows'] = 0; // number of rows
11198                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
11199                                                         $dom[$key]['thead'] = ''; // table header rows
11200                                                 }
11201                                                 if (($dom[$key]['value'] == 'tr')) {
11202                                                         $dom[$key]['cols'] = 0;
11203                                                         // store the number of rows on table element
11204                                                         ++$dom[($dom[$key]['parent'])]['rows'];
11205                                                         // store the TR elements IDs on table element
11206                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
11207                                                         if ($thead) {
11208                                                                 $dom[$key]['thead'] = true;
11209                                                         } else {
11210                                                                 $dom[$key]['thead'] = false;
11211                                                         }
11212                                                 }
11213                                                 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
11214                                                         if (isset($dom[$key]['attribute']['colspan'])) {
11215                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
11216                                                         } else {
11217                                                                 $colspan = 1;
11218                                                         }
11219                                                         $dom[$key]['attribute']['colspan'] = $colspan;
11220                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
11221                                                 }
11222                                                 // set foreground color attribute
11223                                                 if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
11224                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
11225                                                 }
11226                                                 // set background color attribute
11227                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
11228                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
11229                                                 }
11230                                                 // check for width attribute
11231                                                 if (isset($dom[$key]['attribute']['width'])) {
11232                                                         $dom[$key]['width'] = $dom[$key]['attribute']['width'];
11233                                                 }
11234                                                 // check for height attribute
11235                                                 if (isset($dom[$key]['attribute']['height'])) {
11236                                                         $dom[$key]['height'] = $dom[$key]['attribute']['height'];
11237                                                 }
11238                                                 // check for text alignment
11239                                                 if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
11240                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
11241                                                 }
11242                                         } // end opening tag
11243                                 } else {
11244                                         // text
11245                                         $dom[$key]['tag'] = false;
11246                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
11247                                         $dom[$key]['parent'] = end($level);
11248                                 }
11249                                 ++$elkey;
11250                                 ++$key;
11251                         }
11252                         return $dom;
11253                 }
11254                 
11255                 /**
11256                  * Allows to preserve some HTML formatting (limited support).<br />
11257                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
11258                  * 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
11259                  * @param string $html text to display
11260                  * @param boolean $ln if true add a new line after text (default = true)
11261                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
11262                  * @param boolean $reseth if true reset the last cell height (default false).
11263                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
11264                  * @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>
11265                  * @access public
11266                  */
11267                 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
11268                         $gvars = $this->getGraphicVars();
11269                         // store current values
11270                         $prevPage = $this->page;
11271                         $prevlMargin = $this->lMargin;
11272                         $prevrMargin = $this->rMargin;
11273                         $curfontname = $this->FontFamily;
11274                         $curfontstyle = $this->FontStyle;
11275                         $curfontsize = $this->FontSizePt;       
11276                         $this->newline = true;
11277                         $minstartliney = $this->y;
11278                         $yshift = 0;
11279                         $startlinepage = $this->page;
11280                         $newline = true;
11281                         $loop = 0;
11282                         $curpos = 0;
11283                         $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf');
11284                         $this->premode = false;
11285                         if (isset($this->PageAnnots[$this->page])) {
11286                                 $pask = count($this->PageAnnots[$this->page]);
11287                         } else {
11288                                 $pask = 0;
11289                         }
11290                         if (isset($this->footerlen[$this->page])) {
11291                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11292                         } else {
11293                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11294                         }
11295                         $startlinepos = $this->footerpos[$this->page];
11296                         $lalign = $align;
11297                         $plalign = $align;
11298                         if ($this->rtl) {
11299                                 $w = $this->x - $this->lMargin;
11300                         } else {
11301                                 $w = $this->w - $this->rMargin - $this->x;
11302                         }
11303                         $w -= (2 * $this->cMargin);
11304                         if ($cell) {
11305                                 if ($this->rtl) {
11306                                         $this->x -= $this->cMargin;
11307                                 } else {
11308                                         $this->x += $this->cMargin;
11309                                 }
11310                         }
11311                         if ($this->customlistindent >= 0) {
11312                                 $this->listindent = $this->customlistindent;
11313                         } else {
11314                                 $this->listindent = $this->GetStringWidth('0000');
11315                         }
11316                         $this->listnum = 0;
11317                         if (($this->empty_string($this->lasth)) OR ($reseth)) {
11318                                 //set row height
11319                                 $this->lasth = $this->FontSize * $this->cell_height_ratio; 
11320                         }
11321                         $dom = $this->getHtmlDomArray($html);
11322                         $maxel = count($dom);
11323                         $key = 0;
11324                         while ($key < $maxel) {
11325                                 if ($dom[$key]['tag'] OR ($key == 0)) {
11326                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
11327                                                 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
11328                                         }
11329                                         // vertically align image in line
11330                                         if ((!$this->newline)
11331                                                 AND ($dom[$key]['value'] == 'img')
11332                                                 AND (isset($dom[$key]['attribute']['height']))
11333                                                 AND ($dom[$key]['attribute']['height'] > 0)) {
11334                                                 // get image height
11335                                                 $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
11336                                                 if (!$this->InFooter) {
11337                                                         // check for page break
11338                                                         $this->checkPageBreak($imgh);
11339                                                 }
11340                                                 if ($this->page > $startlinepage) {
11341                                                         // fix lines splitted over two pages
11342                                                         if (isset($this->footerlen[$startlinepage])) {
11343                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11344                                                         }
11345                                                         // line to be moved one page forward
11346                                                         $pagebuff = $this->getPageBuffer($startlinepage);
11347                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11348                                                         $tstart = substr($pagebuff, 0, $startlinepos);
11349                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11350                                                         // remove line start from previous page
11351                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11352                                                         $pagebuff = $this->getPageBuffer($this->page);
11353                                                         $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11354                                                         $tend = substr($pagebuff, $this->intmrk[$this->page]);
11355                                                         // add line start to current page
11356                                                         $yshift = $minstartliney - $this->y;
11357                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11358                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11359                                                         // shift the annotations and links
11360                                                         if (isset($this->PageAnnots[$startlinepage])) {
11361                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11362                                                                         if ($pak >= $pask) {
11363                                                                                 $this->PageAnnots[$this->page][] = $pac;
11364                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11365                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11366                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11367                                                                         }
11368                                                                 }
11369                                                         }
11370                                                         $startlinepos = $this->intmrk[$this->page];
11371                                                         $startlinepage = $this->page;
11372                                                         $startliney = $this->y;
11373                                                 }
11374                                                 
11375                                                 $this->y += (($curfontsize / $this->k) - $imgh);
11376                                                 $minstartliney = min($this->y, $minstartliney);
11377                                                 
11378                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
11379                                                 // account for different font size
11380                                                 $pfontname = $curfontname;
11381                                                 $pfontstyle = $curfontstyle;
11382                                                 $pfontsize = $curfontsize;
11383                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
11384                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
11385                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
11386                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
11387                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
11388                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
11389                                                         if (is_numeric($fontsize) AND ($fontsize > 0)
11390                                                                 AND is_numeric($curfontsize) AND ($curfontsize > 0)
11391                                                                 AND ($fontsize != $curfontsize) AND (!$this->newline)
11392                                                                 AND ($key < ($maxel - 1))
11393                                                                 ) {
11394                                                                 if ((!$this->newline) AND ($this->page > $startlinepage)) {
11395                                                                         // fix lines splitted over two pages
11396                                                                         if (isset($this->footerlen[$startlinepage])) {
11397                                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11398                                                                         }
11399                                                                         // line to be moved one page forward
11400                                                                         $pagebuff = $this->getPageBuffer($startlinepage);
11401                                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11402                                                                         $tstart = substr($pagebuff, 0, $startlinepos);
11403                                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11404                                                                         // remove line start from previous page
11405                                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11406                                                                         $pagebuff = $this->getPageBuffer($this->page);
11407                                                                         $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11408                                                                         $tend = substr($pagebuff, $this->intmrk[$this->page]);
11409                                                                         // add line start to current page
11410                                                                         $yshift = $minstartliney - $this->y;
11411                                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11412                                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11413                                                                         // shift the annotations and links
11414                                                                         if (isset($this->PageAnnots[$startlinepage])) {
11415                                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11416                                                                                         if ($pak >= $pask) {
11417                                                                                                 $this->PageAnnots[$this->page][] = $pac;
11418                                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11419                                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11420                                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11421                                                                                         }
11422                                                                                 }
11423                                                                         }
11424                                                                 }
11425                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
11426                                                                 $minstartliney = min($this->y, $minstartliney);
11427                                                         }
11428                                                         $curfontname = $fontname;
11429                                                         $curfontstyle = $fontstyle;
11430                                                         $curfontsize = $fontsize;
11431                                                 }
11432                                         }
11433                                         if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
11434                                                 $plalign = '';
11435                                         }
11436                                         // get current position on page buffer
11437                                         $curpos = $this->pagelen[$startlinepage];
11438                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
11439                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
11440                                                 $wfill = true;
11441                                         } else {
11442                                                 $wfill = $fill | false;
11443                                         }
11444                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
11445                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
11446                                         }
11447                                         if (isset($dom[$key]['align'])) {
11448                                                 $lalign = $dom[$key]['align'];
11449                                         }
11450                                         if ($this->empty_string($lalign)) {
11451                                                 $lalign = $align;
11452                                         }
11453                                 }
11454                                 // align lines
11455                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
11456                                         $newline = true;
11457                                         // we are at the beginning of a new line
11458                                         if (isset($startlinex)) {
11459                                                 $yshift = $minstartliney - $startliney;
11460                                                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
11461                                                         $yshift = 0;
11462                                                 }
11463                                                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11464                                                         // the last line must be shifted to be aligned as requested
11465                                                         $linew = abs($this->endlinex - $startlinex);
11466                                                         $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11467                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11468                                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11469                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11470                                                         } elseif (isset($opentagpos)) {
11471                                                                 $midpos = $opentagpos;
11472                                                         } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11473                                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11474                                                                 $midpos = $this->footerpos[$startlinepage];
11475                                                         } else {
11476                                                                 $midpos = 0;
11477                                                         }
11478                                                         if ($midpos > 0) {
11479                                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11480                                                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11481                                                         } else {
11482                                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11483                                                                 $pend = '';
11484                                                         }
11485                                                         // calculate shifting amount
11486                                                         $tw = $w;
11487                                                         if ($this->lMargin != $prevlMargin) {
11488                                                                 $tw += ($prevlMargin - $this->lMargin);
11489                                                         }
11490                                                         if ($this->rMargin != $prevrMargin) {
11491                                                                 $tw += ($prevrMargin - $this->rMargin);
11492                                                         }
11493                                                         $mdiff = abs($tw - $linew);
11494                                                         $t_x = 0;
11495                                                         if ($plalign == 'C') {
11496                                                                 if ($this->rtl) {
11497                                                                         $t_x = -($mdiff / 2);
11498                                                                 } else {
11499                                                                         $t_x = ($mdiff / 2);
11500                                                                 }
11501                                                         } elseif (($plalign == 'R') AND (!$this->rtl)) {
11502                                                                 // right alignment on LTR document
11503                                                                 $t_x = $mdiff;  
11504                                                         } elseif (($plalign == 'L') AND ($this->rtl)) {
11505                                                                 // left alignment on RTL document
11506                                                                 $t_x = -$mdiff;
11507                                                         } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
11508                                                                 // Justification
11509                                                                 if ($this->rtl OR $this->tmprtl) {
11510                                                                         $t_x = $this->lMargin - $this->endlinex;
11511                                                                 }
11512                                                                 $no = 0;
11513                                                                 $ns = 0;
11514                                                                 $pmidtemp = $pmid;
11515                                                                 // escape special characters
11516                                                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11517                                                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11518                                                                 // search spaces
11519                                                                 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
11520                                                                         $maxkk = count($lnstring[1]) - 1;
11521                                                                         for ($kk=0; $kk <= $maxkk; ++$kk) {
11522                                                                                 // restore special characters
11523                                                                                 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
11524                                                                                 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
11525                                                                                 if ($kk == $maxkk) {
11526                                                                                         if ($this->rtl OR $this->tmprtl) {
11527                                                                                                 $tvalue = ltrim($lnstring[1][$kk]);
11528                                                                                         } else {
11529                                                                                                 $tvalue = rtrim($lnstring[1][$kk]);
11530                                                                                         }
11531                                                                                 } else {
11532                                                                                         $tvalue = $lnstring[1][$kk];
11533                                                                                 }
11534                                                                                 // count spaces on line
11535                                                                                 $no += substr_count($lnstring[1][$kk], chr(32));
11536                                                                                 $ns += substr_count($tvalue, chr(32));
11537                                                                         }
11538                                                                         if ($this->rtl OR $this->tmprtl) {
11539                                                                                 $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
11540                                                                         }
11541                                                                         // calculate additional space to add to each space
11542                                                                         $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k;
11543                                                                         $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k;
11544                                                                         $nsmax = $ns;
11545                                                                         $ns = 0;
11546                                                                         reset($lnstring);
11547                                                                         $offset = 0;
11548                                                                         $strcount = 0;
11549                                                                         $prev_epsposbeg = 0;
11550                                                                         global $spacew;
11551                                                                         while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
11552                                                                                 if ($this->rtl OR $this->tmprtl) {
11553                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
11554                                                                                 } else {
11555                                                                                         $spacew = ($spacewidth * $ns);
11556                                                                                 }
11557                                                                                 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
11558                                                                                 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
11559                                                                                 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
11560                                                                                 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
11561                                                                                         OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
11562                                                                                         // shift EPS images
11563                                                                                         $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
11564                                                                                         $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
11565                                                                                         $pmid_b = substr($pmid, 0, $epsposbeg);
11566                                                                                         $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
11567                                                                                         $pmid_e = substr($pmid, $epsposend);
11568                                                                                         $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
11569                                                                                         $offset = $epsposend;
11570                                                                                         continue;
11571                                                                                 }
11572                                                                                 $prev_epsposbeg = $epsposbeg;
11573                                                                                 $currentxpos = 0;
11574                                                                                 // shift blocks of code
11575                                                                                 switch ($strpiece[2][0]) {
11576                                                                                         case 'Td':
11577                                                                                         case 'cm':
11578                                                                                         case 'm':
11579                                                                                         case 'l': {
11580                                                                                                 // get current X position
11581                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11582                                                                                                 $currentxpos = $xmatches[1];
11583                                                                                                 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
11584                                                                                                         if ($strcount == $maxkk) {
11585                                                                                                                 if ($this->rtl OR $this->tmprtl) {
11586                                                                                                                         $tvalue = $lnstring[1][$strcount];
11587                                                                                                                 } else {
11588                                                                                                                         $tvalue = rtrim($lnstring[1][$strcount]);
11589                                                                                                                 }
11590                                                                                                         } else {
11591                                                                                                                 $tvalue = $lnstring[1][$strcount];
11592                                                                                                         }
11593                                                                                                         $ns += substr_count($tvalue, chr(32));
11594                                                                                                         ++$strcount;
11595                                                                                                 }
11596                                                                                                 if ($this->rtl OR $this->tmprtl) {
11597                                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
11598                                                                                                 }
11599                                                                                                 // justify block
11600                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11601                                                                                                         create_function('$matches', 'global $spacew;
11602                                                                                                         $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11603                                                                                                         return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
11604                                                                                                 break;
11605                                                                                         }
11606                                                                                         case 're': {
11607                                                                                                 // get current X position
11608                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11609                                                                                                 $currentxpos = $xmatches[1];
11610                                                                                                 // justify block
11611                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11612                                                                                                         create_function('$matches', 'global $spacew;
11613                                                                                                         $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11614                                                                                                         return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
11615                                                                                                 break;
11616                                                                                         }
11617                                                                                         case 'c': {
11618                                                                                                 // get current X position
11619                                                                                                 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);
11620                                                                                                 $currentxpos = $xmatches[1];
11621                                                                                                 // justify block
11622                                                                                                 $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',
11623                                                                                                         create_function('$matches', 'global $spacew;
11624                                                                                                         $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
11625                                                                                                         $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
11626                                                                                                         $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
11627                                                                                                         return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
11628                                                                                                 break;
11629                                                                                         }
11630                                                                                 }
11631                                                                                 // shift the annotations and links
11632                                                                                 if (isset($this->PageAnnots[$this->page])) {
11633                                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11634                                                                                                 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
11635                                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
11636                                                                                                         $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
11637                                                                                                         break;
11638                                                                                                 }
11639                                                                                         }
11640                                                                                 }
11641                                                                         } // end of while
11642                                                                         // remove markers
11643                                                                         $pmid = str_replace('x*#!#*x', '', $pmid);
11644                                                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11645                                                                                 // multibyte characters
11646                                                                                 $spacew = $spacewidthu;
11647                                                                                 $pmidtemp = $pmid;
11648                                                                                 // escape special characters
11649                                                                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11650                                                                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11651                                                                                 $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
11652                                                                                                         create_function('$matches', 'global $spacew;
11653                                                                                                         $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
11654                                                                                                         $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
11655                                                                                                         return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp);
11656                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
11657                                                                                 $endlinepos = strlen($pstart."\n".$pmid."\n");
11658                                                                         } else {
11659                                                                                 // non-unicode (single-byte characters)
11660                                                                                 $rs = sprintf("%.3F Tw", $spacewidth);
11661                                                                                 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
11662                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
11663                                                                                 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
11664                                                                         }
11665                                                                 }
11666                                                         } // end of J
11667                                                         if (($t_x != 0) OR ($yshift < 0)) {
11668                                                                 // shift the line
11669                                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11670                                                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11671                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11672                                                                 // shift the annotations and links
11673                                                                 if (isset($this->PageAnnots[$this->page])) {
11674                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11675                                                                                 if ($pak >= $pask) {
11676                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
11677                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
11678                                                                                 }
11679                                                                         }
11680                                                                 }
11681                                                                 $this->y -= $yshift;
11682                                                         }
11683                                                 }
11684                                         }
11685                                         $this->newline = false;
11686                                         $pbrk = $this->checkPageBreak($this->lasth);
11687                                         $this->SetFont($fontname, $fontstyle, $fontsize);
11688                                         if ($wfill) {
11689                                                 $this->SetFillColorArray($this->bgcolor);
11690                                         }
11691                                         $startlinex = $this->x;
11692                                         $startliney = $this->y;
11693                                         $minstartliney = $this->y;
11694                                         $startlinepage = $this->page;
11695                                         if (isset($endlinepos) AND (!$pbrk)) {
11696                                                 $startlinepos = $endlinepos;
11697                                                 unset($endlinepos);
11698                                         } else {
11699                                                 if (isset($this->footerlen[$this->page])) {
11700                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11701                                                 } else {
11702                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11703                                                 }
11704                                                 $startlinepos = $this->footerpos[$this->page];
11705                                         }
11706                                         $plalign = $lalign;
11707                                         if (isset($this->PageAnnots[$this->page])) {
11708                                                 $pask = count($this->PageAnnots[$this->page]);
11709                                         } else {
11710                                                 $pask = 0;
11711                                         }
11712                                 }
11713                                 if (isset($opentagpos)) {
11714                                         unset($opentagpos);
11715                                 }
11716                                 if ($dom[$key]['tag']) {
11717                                         if ($dom[$key]['opening']) {
11718                                                 if ($dom[$key]['value'] == 'table') {
11719                                                         if ($this->rtl) {
11720                                                                 $wtmp = $this->x - $this->lMargin;
11721                                                         } else {
11722                                                                 $wtmp = $this->w - $this->rMargin - $this->x;
11723                                                         }
11724                                                         $wtmp -= (2 * $this->cMargin);
11725                                                         // calculate cell width
11726                                                         if (isset($dom[$key]['width'])) {
11727                                                                 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11728                                                         } else {
11729                                                                 $table_width = $wtmp;
11730                                                         }
11731                                                 }
11732                                                 // table content is handled in a special way
11733                                                 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11734                                                         $trid = $dom[$key]['parent'];
11735                                                         $table_el = $dom[$trid]['parent'];
11736                                                         if (!isset($dom[$table_el]['cols'])) {
11737                                                                 $dom[$table_el]['cols'] = $trid['cols'];
11738                                                         }
11739                                                         $oldmargin = $this->cMargin;
11740                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
11741                                                                 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
11742                                                         } else {
11743                                                                 $currentcmargin = 0;            
11744                                                         }
11745                                                         $this->cMargin = $currentcmargin;
11746                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
11747                                                                 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
11748                                                         } else {
11749                                                                 $cellspacing = 0;
11750                                                         }
11751                                                         if ($this->rtl) {
11752                                                                 $cellspacingx = -$cellspacing;
11753                                                         } else {
11754                                                                 $cellspacingx = $cellspacing;
11755                                                         }
11756                                                         $colspan = $dom[$key]['attribute']['colspan'];
11757                                                         $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
11758                                                         if (isset($dom[$key]['width'])) {
11759                                                                 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11760                                                         } else {
11761                                                                 $cellw = $wtmp;
11762                                                         }
11763                                                         if (isset($dom[$key]['height'])) {
11764                                                                 // minimum cell height
11765                                                                 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
11766                                                         } else {
11767                                                                 $cellh = 0;
11768                                                         }
11769                                                         $cellw -= $cellspacing;
11770                                                         if (isset($dom[$key]['content'])) {
11771                                                                 $cell_content = $dom[$key]['content'];
11772                                                         } else {
11773                                                                 $cell_content = '&nbsp;';
11774                                                         }
11775                                                         $tagtype = $dom[$key]['value'];
11776                                                         $parentid = $key;
11777                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
11778                                                                 // move $key index forward
11779                                                                 ++$key;
11780                                                         }
11781                                                         if (!isset($dom[$trid]['startpage'])) {
11782                                                                 $dom[$trid]['startpage'] = $this->page;
11783                                                         } else {
11784                                                                 $this->setPage($dom[$trid]['startpage']);
11785                                                         }
11786                                                         if (!isset($dom[$trid]['starty'])) {
11787                                                                 $dom[$trid]['starty'] = $this->y;
11788                                                         } else {
11789                                                                 $this->y = $dom[$trid]['starty'];
11790                                                         }
11791                                                         if (!isset($dom[$trid]['startx'])) {
11792                                                                 $dom[$trid]['startx'] = $this->x;
11793                                                         }
11794                                                         $this->x += ($cellspacingx / 2);                                                
11795                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
11796                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
11797                                                         } else {
11798                                                                 $rowspan = 1;
11799                                                         }
11800                                                         // skip row-spanned cells started on the previous rows
11801                                                         if (isset($dom[$table_el]['rowspans'])) {
11802                                                                 $rsk = 0;
11803                                                                 $rskmax = count($dom[$table_el]['rowspans']);
11804                                                                 while ($rsk < $rskmax) {
11805                                                                         $trwsp = $dom[$table_el]['rowspans'][$rsk];
11806                                                                         $rsstartx = $trwsp['startx'];
11807                                                                         $rsendx = $trwsp['endx'];
11808                                                                         // account for margin changes
11809                                                                         if ($trwsp['startpage'] < $this->page) {
11810                                                                                 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
11811                                                                                         $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
11812                                                                                         $rsstartx -= $dl;
11813                                                                                         $rsendx -= $dl;
11814                                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
11815                                                                                         $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
11816                                                                                         $rsstartx += $dl;
11817                                                                                         $rsendx += $dl;
11818                                                                                 }
11819                                                                         }
11820                                                                         if  (($trwsp['rowspan'] > 0)
11821                                                                                 AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
11822                                                                                 AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
11823                                                                                 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
11824                                                                                 // set the starting X position of the current cell
11825                                                                                 $this->x = $rsendx + $cellspacingx;
11826                                                                                 if (($trwsp['rowspan'] == 1)
11827                                                                                         AND (isset($dom[$trid]['endy']))
11828                                                                                         AND (isset($dom[$trid]['endpage']))
11829                                                                                         AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
11830                                                                                         // set ending Y position for row
11831                                                                                         $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11832                                                                                         $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
11833                                                                                 }
11834                                                                                 $rsk = 0;
11835                                                                         } else {
11836                                                                                 ++$rsk;
11837                                                                         }
11838                                                                 }
11839                                                         }
11840                                                         // add rowspan information to table element
11841                                                         if ($rowspan > 1) {
11842                                                                 if (isset($this->footerlen[$this->page])) {
11843                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11844                                                                 } else {
11845                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11846                                                                 }
11847                                                                 $trintmrkpos = $this->footerpos[$this->page];
11848                                                                 $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));
11849                                                         }
11850                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
11851                                                         if ($rowspan > 1) {
11852                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
11853                                                         }
11854                                                         // push background colors
11855                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
11856                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
11857                                                         }
11858                                                         $prevLastH = $this->lasth;
11859                                                         // ****** write the cell content ******
11860                                                         $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
11861                                                         $this->lasth = $prevLastH;
11862                                                         $this->cMargin = $oldmargin;
11863                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
11864                                                         // update the end of row position
11865                                                         if ($rowspan <= 1) {
11866                                                                 if (isset($dom[$trid]['endy'])) {
11867                                                                         if ($this->page == $dom[$trid]['endpage']) {
11868                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
11869                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
11870                                                                                 $dom[$trid]['endy'] = $this->y;
11871                                                                         }
11872                                                                 } else {
11873                                                                         $dom[$trid]['endy'] = $this->y;
11874                                                                 }
11875                                                                 if (isset($dom[$trid]['endpage'])) {
11876                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
11877                                                                 } else {
11878                                                                         $dom[$trid]['endpage'] = $this->page;
11879                                                                 }                                                               
11880                                                         } else {
11881                                                                 // account for row-spanned cells
11882                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
11883                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
11884                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
11885                                                         }
11886                                                         if (isset($dom[$table_el]['rowspans'])) {
11887                                                                 // update endy and endpage on rowspanned cells
11888                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
11889                                                                         if ($trwsp['rowspan'] > 0) {
11890                                                                                 if (isset($dom[$trid]['endpage'])) {
11891                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
11892                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11893                                                                                         } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
11894                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
11895                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
11896                                                                                         } else {
11897                                                                                                 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
11898                                                                                         }
11899                                                                                 }
11900                                                                         }
11901                                                                 }
11902                                                         }
11903                                                         $this->x += ($cellspacingx / 2);                                                        
11904                                                 } else {
11905                                                         // opening tag (or self-closing tag)
11906                                                         if (!isset($opentagpos)) {
11907                                                                 if (!$this->InFooter) {
11908                                                                         if (isset($this->footerlen[$this->page])) {
11909                                                                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11910                                                                         } else {
11911                                                                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11912                                                                         }
11913                                                                         $opentagpos = $this->footerpos[$this->page];
11914                                                                 }
11915                                                         }
11916                                                         $this->openHTMLTagHandler($dom, $key, $cell);
11917                                                 }
11918                                         } else {
11919                                                 // closing tag
11920                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
11921                                         }
11922                                 } elseif (strlen($dom[$key]['value']) > 0) {
11923                                         // print list-item
11924                                         if (!$this->empty_string($this->lispacer)) {
11925                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
11926                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
11927                                                 $minstartliney = $this->y;
11928                                                 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
11929                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
11930                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
11931                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
11932                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
11933                                                         $minstartliney = min($this->y, $minstartliney);
11934                                                 }
11935                                         }
11936                                         // text
11937                                         $this->htmlvspace = 0;
11938                                         if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
11939                                                 // reverse spaces order
11940                                                 $len1 = strlen($dom[$key]['value']);
11941                                                 $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
11942                                                 $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
11943                                                 $tmpstr = '';
11944                                                 if ($rsp > 0) {
11945                                                         $tmpstr .= substr($dom[$key]['value'], -$rsp);
11946                                                 }
11947                                                 $tmpstr .= trim($dom[$key]['value']);
11948                                                 if ($lsp > 0) {
11949                                                         $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
11950                                                 }
11951                                                 $dom[$key]['value'] = $tmpstr;
11952                                         }
11953                                         if ($newline) {
11954                                                 if (!$this->premode) {
11955                                                         if (($this->rtl OR $this->tmprtl)) {
11956                                                                 $dom[$key]['value'] = rtrim($dom[$key]['value']);
11957                                                         } else {
11958                                                                 $dom[$key]['value'] = ltrim($dom[$key]['value']);
11959                                                         }
11960                                                 }
11961                                                 $newline = false;
11962                                                 $firstblock = true;
11963                                         } else {
11964                                                 $firstblock = false;
11965                                         }
11966                                         $strrest = '';
11967                                         if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
11968                                                 // HTML <a> Link
11969                                                 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
11970                                         } else {
11971                                                 $ctmpmargin = $this->cMargin;
11972                                                 $this->cMargin = 0;
11973                                                 // ****** write only until the end of the line and get the rest ******
11974                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
11975                                                 $this->cMargin = $ctmpmargin;
11976                                         }
11977                                         if (strlen($strrest) > 0) {
11978                                                 // store the remaining string on the previous $key position
11979                                                 $this->newline = true;
11980                                                 if ($cell) {
11981                                                         if ($this->rtl) {
11982                                                                 $this->x -= $this->cMargin;
11983                                                         } else {
11984                                                                 $this->x += $this->cMargin;
11985                                                         }
11986                                                 }
11987                                                 if ($strrest == $dom[$key]['value']) {
11988                                                         // used to avoid infinite loop
11989                                                         ++$loop;
11990                                                 } else {
11991                                                         $loop = 0;
11992                                                 }
11993                                                 $dom[$key]['value'] = ltrim($strrest);
11994                                                 if ($loop < 3) {
11995                                                         --$key;
11996                                                 }
11997                                         } else {
11998                                                 $loop = 0;
11999                                         }
12000                                 }
12001                                 ++$key;
12002                         } // end for each $key
12003                         // align the last line
12004                         if (isset($startlinex)) {
12005                                 $yshift = $minstartliney - $startliney;
12006                                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
12007                                         $yshift = 0;
12008                                 }
12009                                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
12010                                         // the last line must be shifted to be aligned as requested
12011                                         $linew = abs($this->endlinex - $startlinex);
12012                                         $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
12013                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12014                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12015                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
12016                                         } elseif (isset($opentagpos)) {
12017                                                 $midpos = $opentagpos;
12018                                         } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12019                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12020                                                 $midpos = $this->footerpos[$startlinepage];
12021                                         } else {
12022                                                 $midpos = 0;
12023                                         }
12024                                         if ($midpos > 0) {
12025                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
12026                                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
12027                                         } else {
12028                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
12029                                                 $pend = '';
12030                                         }       
12031                                         // calculate shifting amount
12032                                         $tw = $w;
12033                                         if ($this->lMargin != $prevlMargin) {
12034                                                 $tw += ($prevlMargin - $this->lMargin);
12035                                         }
12036                                         if ($this->rMargin != $prevrMargin) {
12037                                                 $tw += ($prevrMargin - $this->rMargin);
12038                                         }
12039                                         $mdiff = abs($tw - $linew);
12040                                         if ($plalign == 'C') {
12041                                                 if ($this->rtl) {
12042                                                         $t_x = -($mdiff / 2);
12043                                                 } else {
12044                                                         $t_x = ($mdiff / 2);
12045                                                 }
12046                                         } elseif (($plalign == 'R') AND (!$this->rtl)) {
12047                                                 // right alignment on LTR document
12048                                                 $t_x = $mdiff;
12049                                         } elseif (($plalign == 'L') AND ($this->rtl)) {
12050                                                 // left alignment on RTL document
12051                                                 $t_x = -$mdiff;
12052                                         } else {
12053                                                 $t_x = 0;
12054                                         }
12055                                         if (($t_x != 0) OR ($yshift < 0)) {
12056                                                 // shift the line
12057                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
12058                                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
12059                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
12060                                                 // shift the annotations and links
12061                                                 if (isset($this->PageAnnots[$this->page])) {
12062                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
12063                                                                 if ($pak >= $pask) {
12064                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
12065                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
12066                                                                 }
12067                                                         }
12068                                                 }
12069                                                 $this->y -= $yshift;
12070                                         }
12071                                 }
12072                         }
12073                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
12074                                 $this->Ln($this->lasth);
12075                         }
12076                         // restore previous values
12077                         $this->setGraphicVars($gvars);
12078                         if ($this->page > $prevPage) {
12079                                 $this->lMargin = $this->pagedim[$this->page]['olm'];
12080                                 $this->rMargin = $this->pagedim[$this->page]['orm'];
12081                         }
12082                         unset($dom);
12083                 }
12084                 
12085                 /**
12086                  * Process opening tags.
12087                  * @param array $dom html dom array 
12088                  * @param int $key current element id
12089                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12090                  * @access protected
12091                  */
12092                 protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
12093                         $tag = $dom[$key];
12094                         $parent = $dom[($dom[$key]['parent'])];
12095                         $firstorlast = ($key == 1);
12096                         // check for text direction attribute
12097                         if (isset($tag['attribute']['dir'])) {
12098                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
12099                         } else {
12100                                 $this->tmprtl = false;
12101                         }
12102                         //Opening tag
12103                         switch($tag['value']) {
12104                                 case 'table': {
12105                                         $cp = 0;
12106                                         $cs = 0;
12107                                         $dom[$key]['rowspans'] = array();
12108                                         if (!$this->empty_string($dom[$key]['thead'])) {
12109                                                 // set table header
12110                                                 $this->thead = $dom[$key]['thead'];
12111                                         }
12112                                         if (isset($tag['attribute']['cellpadding'])) {
12113                                                 $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
12114                                                 $this->oldcMargin = $this->cMargin;
12115                                                 $this->cMargin = $cp;
12116                                         }
12117                                         if (isset($tag['attribute']['cellspacing'])) {
12118                                                 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
12119                                         }
12120                                         $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
12121                                         break;
12122                                 }
12123                                 case 'tr': {
12124                                         // array of columns positions
12125                                         $dom[$key]['cellpos'] = array();
12126                                         break;
12127                                 }
12128                                 case 'hr': {
12129                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12130                                         $this->htmlvspace = 0;
12131                                         $wtmp = $this->w - $this->lMargin - $this->rMargin;
12132                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
12133                                                 $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
12134                                         } else {
12135                                                 $hrWidth = $wtmp;
12136                                         }
12137                                         $x = $this->GetX();
12138                                         $y = $this->GetY();
12139                                         $prevlinewidth = $this->GetLineWidth();
12140                                         $this->Line($x, $y, $x + $hrWidth, $y);
12141                                         $this->SetLineWidth($prevlinewidth);
12142                                         $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
12143                                         break;
12144                                 }
12145                                 case 'a': {
12146                                         if (array_key_exists('href', $tag['attribute'])) {
12147                                                 $this->HREF['url'] = $tag['attribute']['href'];
12148                                         }
12149                                         $this->HREF['color'] = $this->htmlLinkColorArray;
12150                                         $this->HREF['style'] = $this->htmlLinkFontStyle;
12151                                         if (array_key_exists('style', $tag['attribute'])) {
12152                                                 // get style attributes
12153                                                 preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12154                                                 $astyle = array();
12155                                                 while (list($id, $name) = each($style_array[1])) {
12156                                                         $name = strtolower($name);
12157                                                         $astyle[$name] = trim($style_array[2][$id]);
12158                                                 }
12159                                                 if (isset($astyle['color'])) {
12160                                                         $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
12161                                                 }
12162                                                 if (isset($astyle['text-decoration'])) {
12163                                                         $this->HREF['style'] = '';
12164                                                         $decors = explode(' ', strtolower($astyle['text-decoration']));
12165                                                         foreach ($decors as $dec) {
12166                                                                 $dec = trim($dec);
12167                                                                 if (!$this->empty_string($dec)) {
12168                                                                         if ($dec{0} == 'u') {
12169                                                                                 $this->HREF['style'] .= 'U';
12170                                                                         } elseif ($dec{0} == 'l') {
12171                                                                                 $this->HREF['style'] .= 'D';
12172                                                                         }
12173                                                                 }
12174                                                         }
12175                                                 }
12176                                         }               
12177                                         break;
12178                                 }
12179                                 case 'img': {
12180                                         if (isset($tag['attribute']['src'])) {
12181                                                 // replace relative path with real server path
12182                                                 if ($tag['attribute']['src'][0] == '/') {
12183                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
12184                                                 }
12185                                                 $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
12186                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
12187                                                 if (!isset($tag['attribute']['width'])) {
12188                                                         $tag['attribute']['width'] = 0;
12189                                                 }
12190                                                 if (!isset($tag['attribute']['height'])) {
12191                                                         $tag['attribute']['height'] = 0;
12192                                                 }
12193                                                 //if (!isset($tag['attribute']['align'])) {
12194                                                         // the only alignment supported is "bottom"
12195                                                         // further development is required for other modes.
12196                                                         $tag['attribute']['align'] = 'bottom';
12197                                                 //} 
12198                                                 switch($tag['attribute']['align']) {
12199                                                         case 'top': {
12200                                                                 $align = 'T';
12201                                                                 break;
12202                                                         }
12203                                                         case 'middle': {
12204                                                                 $align = 'M';
12205                                                                 break;
12206                                                         }
12207                                                         case 'bottom': {
12208                                                                 $align = 'B';
12209                                                                 break;
12210                                                         }
12211                                                         default: {
12212                                                                 $align = 'B';
12213                                                                 break;
12214                                                         }
12215                                                 }
12216                                                 $fileinfo = pathinfo($tag['attribute']['src']);
12217                                                 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
12218                                                         $type = strtolower($fileinfo['extension']);
12219                                                 }
12220                                                 $prevy = $this->y;
12221                                                 $xpos = $this->GetX();
12222                                                 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
12223                                                         if ($this->rtl) {
12224                                                                 $xpos += $this->GetStringWidth(' ');
12225                                                         } else {
12226                                                                 $xpos -= $this->GetStringWidth(' ');
12227                                                         }
12228                                                 }
12229                                                 $imglink = '';
12230                                                 if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
12231                                                         $imglink = $this->HREF['url'];
12232                                                         if ($imglink{0} == '#') {
12233                                                                 // convert url to internal link
12234                                                                 $page = intval(substr($imglink, 1));
12235                                                                 $imglink = $this->AddLink();
12236                                                                 $this->SetLink($imglink, 0, $page);
12237                                                         }
12238                                                 }
12239                                                 $border = 0;
12240                                                 if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
12241                                                         // currently only support 1 (frame) or a combination of 'LTRB'
12242                                                         $border = $tag['attribute']['border'];
12243                                                 }
12244                                                 $iw = '';
12245                                                 if (isset($tag['attribute']['width'])) {
12246                                                         $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
12247                                                 }
12248                                                 $ih = '';
12249                                                 if (isset($tag['attribute']['height'])) {
12250                                                         $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
12251                                                 }
12252                                                 if (($type == 'eps') OR ($type == 'ai')) {
12253                                                         $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
12254                                                 } else {
12255                                                         $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
12256                                                 }
12257                                                 switch($align) {
12258                                                         case 'T': {
12259                                                                 $this->y = $prevy;
12260                                                                 break;
12261                                                         }
12262                                                         case 'M': {
12263                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
12264                                                                 break;
12265                                                         }
12266                                                         case 'B': {
12267                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
12268                                                                 break;
12269                                                         }
12270                                                 }
12271                                         }
12272                                         break;
12273                                 }
12274                                 case 'dl': {
12275                                         ++$this->listnum;
12276                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12277                                         break;
12278                                 }
12279                                 case 'dt': {
12280                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12281                                         break;
12282                                 }
12283                                 case 'dd': {
12284                                         if ($this->rtl) {
12285                                                 $this->rMargin += $this->listindent;
12286                                         } else {
12287                                                 $this->lMargin += $this->listindent;
12288                                         }
12289                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12290                                         break;
12291                                 }
12292                                 case 'ul':
12293                                 case 'ol': {
12294                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12295                                         $this->htmlvspace = 0;
12296                                         ++$this->listnum;
12297                                         if ($tag['value'] == 'ol') {
12298                                                 $this->listordered[$this->listnum] = true;
12299                                         } else {
12300                                                 $this->listordered[$this->listnum] = false;
12301                                         }
12302                                         if (isset($tag['attribute']['start'])) {
12303                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
12304                                         } else {
12305                                                 $this->listcount[$this->listnum] = 0;
12306                                         }
12307                                         if ($this->rtl) {
12308                                                 $this->rMargin += $this->listindent;
12309                                         } else {
12310                                                 $this->lMargin += $this->listindent;
12311                                         }
12312                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12313                                         $this->htmlvspace = 0;
12314                                         break;
12315                                 }
12316                                 case 'li': {
12317                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12318                                         if ($this->listordered[$this->listnum]) {
12319                                                 // ordered item
12320                                                 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12321                                                         $this->lispacer = $parent['attribute']['type'];
12322                                                 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12323                                                         $this->lispacer = $parent['listtype'];
12324                                                 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12325                                                         $this->lispacer = $this->lisymbol;
12326                                                 } else {
12327                                                         $this->lispacer = '#';
12328                                                 }
12329                                                 ++$this->listcount[$this->listnum];
12330                                                 if (isset($tag['attribute']['value'])) {
12331                                                         $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
12332                                                 }
12333                                         } else {
12334                                                 // unordered item
12335                                                 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12336                                                         $this->lispacer = $parent['attribute']['type'];
12337                                                 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12338                                                         $this->lispacer = $parent['listtype'];
12339                                                 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12340                                                         $this->lispacer = $this->lisymbol;
12341                                                 } else {
12342                                                         $this->lispacer = '!';
12343                                                 }
12344                                         }
12345                                         break;
12346                                 }
12347                                 case 'blockquote': {
12348                                         if ($this->rtl) {
12349                                                 $this->rMargin += $this->listindent;
12350                                         } else {
12351                                                 $this->lMargin += $this->listindent;
12352                                         }
12353                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12354                                         break;
12355                                 }
12356                                 case 'br': {
12357                                         $this->Ln('', $cell);
12358                                         break;
12359                                 }
12360                                 case 'div': {
12361                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12362                                         break;
12363                                 }
12364                                 case 'p': {
12365                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12366                                         break;
12367                                 }
12368                                 case 'pre': {
12369                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12370                                         $this->premode = true;
12371                                         break;
12372                                 }
12373                                 case 'sup': {
12374                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
12375                                         break;
12376                                 }
12377                                 case 'sub': {
12378                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
12379                                         break;
12380                                 }
12381                                 case 'h1': 
12382                                 case 'h2': 
12383                                 case 'h3': 
12384                                 case 'h4': 
12385                                 case 'h5': 
12386                                 case 'h6': {
12387                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
12388                                         break;
12389                                 }
12390                                 case 'tcpdf': {
12391                                         if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
12392                                                  // Special tag used to call TCPDF methods
12393                         if (isset($tag['attribute']['method'])) {
12394                             $tcpdf_method = $tag['attribute']['method'];
12395                             if (method_exists($this, $tcpdf_method)) {
12396                                 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
12397                                     $params = unserialize(urldecode($tag['attribute']['params']));
12398                                     call_user_func_array(array($this, $tcpdf_method), $params);
12399                                 } else {
12400                                     $this->$tcpdf_method();
12401                                 }
12402                                 $this->newline = true;
12403                             }
12404                         }
12405                     }
12406                                 }
12407                                 default: {
12408                                         break;
12409                                 }
12410                         }
12411                 }
12412                 
12413                 /**
12414                  * Process closing tags.
12415                  * @param array $dom html dom array 
12416                  * @param int $key current element id
12417                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12418                  * @access protected
12419                  */
12420                 protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
12421                         $tag = $dom[$key];
12422                         $parent = $dom[($dom[$key]['parent'])];
12423                         $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
12424                         //Closing tag
12425                         switch($tag['value']) {
12426                                 case 'tr': {
12427                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
12428                                         if(!isset($parent['endy'])) {
12429                                                 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
12430                                                 $parent['endy'] = $this->y;
12431                                         }
12432                                         if(!isset($parent['endpage'])) {
12433                                                 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
12434                                                 $parent['endpage'] = $this->page;
12435                                         }
12436                                         // update row-spanned cells
12437                                         if (isset($dom[$table_el]['rowspans'])) {
12438                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12439                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
12440                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12441                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
12442                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
12443                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
12444                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12445                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12446                                                                 }
12447                                                         }
12448                                                 }
12449                                                 // report new endy and endpage to the rowspanned cells
12450                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12451                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12452                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
12453                                                                 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12454                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
12455                                                                 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12456                                                         }
12457                                                 }
12458                                                 // update remaining rowspanned cells
12459                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12460                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12461                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
12462                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
12463                                                         }
12464                                                 }
12465                                         }
12466                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
12467                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];                                        
12468                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
12469                                                 $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
12470                                                 $this->y += $cellspacing;
12471                                         }                               
12472                                         $this->Ln(0, $cell);
12473                                         $this->x = $parent['startx'];
12474                                         // account for booklet mode
12475                                         if ($this->page > $parent['startpage']) {
12476                                                 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
12477                                                         $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
12478                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
12479                                                         $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
12480                                                 }
12481                                         }
12482                                         break;
12483                                 }
12484                                 case 'table': {
12485                                         // draw borders
12486                                         $table_el = $parent;
12487                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 
12488                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
12489                                                         $border = 1;
12490                                         } else {
12491                                                 $border = 0;
12492                                         }
12493                                         // fix bottom line alignment of last line before page break
12494                                         foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
12495                                                 // update row-spanned cells
12496                                                 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12497                                                         foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12498                                                                 if ($trwsp['trid'] == $trkey) {
12499                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
12500                                                                 }
12501                                                                 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
12502                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
12503                                                                 }
12504                                                         }
12505                                                 }
12506                                                 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
12507                                                         $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
12508                                                         $dom[$prevtrkey]['endy'] = $pgendy;
12509                                                         // update row-spanned cells
12510                                                         if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12511                                                                 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12512                                                                         if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
12513                                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
12514                                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
12515                                                                         }
12516                                                                 }
12517                                                         }
12518                                                 }
12519                                                 $prevtrkey = $trkey;
12520                                                 $table_el = $dom[($dom[$key]['parent'])];
12521                                         }
12522                                         // for each row
12523                                         foreach ($table_el['trids'] as $j => $trkey) {
12524                                                 $parent = $dom[$trkey];
12525                                                 // for each cell on the row
12526                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
12527                                                         if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
12528                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
12529                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
12530                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
12531                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
12532                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
12533                                                         } else {
12534                                                                 $endy = $parent['endy'];
12535                                                                 $startpage = $parent['startpage'];
12536                                                                 $endpage = $parent['endpage'];
12537                                                         }
12538                                                         if ($endpage > $startpage) {
12539                                                                 // design borders around HTML cells.
12540                                                                 for ($page=$startpage; $page <= $endpage; ++$page) {
12541                                                                         $this->setPage($page);
12542                                                                         if ($page == $startpage) {
12543                                                                                 $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
12544                                                                                 $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
12545                                                                                 $cborder = $this->getBorderMode($border, $position='start');
12546                                                                         } elseif ($page == $endpage) {
12547                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of last page
12548                                                                                 $ch = $endy - $this->tMargin;
12549                                                                                 $cborder = $this->getBorderMode($border, $position='end');
12550                                                                         } else {
12551                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of the current page
12552                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
12553                                                                                 $cborder = $this->getBorderMode($border, $position='middle');
12554                                                                         }
12555                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12556                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
12557                                                                                 $fill = true;
12558                                                                         } else {
12559                                                                                 $fill = false;
12560                                                                         }
12561                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
12562                                                                         $this->x = $cellpos['startx'];
12563                                                                         // account for margin changes
12564                                                                         if ($page > $startpage) {
12565                                                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
12566                                                                                         $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
12567                                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
12568                                                                                         $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
12569                                                                                 }
12570                                                                         }
12571                                                                         // design a cell around the text
12572                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
12573                                                                         if ($cborder OR $fill) {
12574                                                                                 $pagebuff = $this->getPageBuffer($this->page);
12575                                                                                 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
12576                                                                                 $pend = substr($pagebuff, $this->intmrk[$this->page]);
12577                                                                                 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12578                                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
12579                                                                         }
12580                                                                 }
12581                                                         } else {
12582                                                                 $this->setPage($startpage);
12583                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12584                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
12585                                                                         $fill = true;
12586                                                                 } else {
12587                                                                         $fill = false;
12588                                                                 }
12589                                                                 $this->x = $cellpos['startx'];
12590                                                                 $this->y = $parent['starty'];
12591                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
12592                                                                 $ch = $endy - $parent['starty'];
12593                                                                 // design a cell around the text
12594                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
12595                                                                 if ($border OR $fill) {
12596                                                                         if (end($this->transfmrk[$this->page]) !== false) {
12597                                                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
12598                                                                                 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
12599                                                                         } elseif ($this->InFooter) {
12600                                                                                 $pagemark = &$this->footerpos[$this->page];
12601                                                                         } else {
12602                                                                                 $pagemark = &$this->intmrk[$this->page];
12603                                                                         }
12604                                                                         $pagebuff = $this->getPageBuffer($this->page);
12605                                                                         $pstart = substr($pagebuff, 0, $pagemark);
12606                                                                         $pend = substr($pagebuff, $pagemark);
12607                                                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12608                                                                         $pagemark += strlen($ccode."\n");
12609                                                                 }                                       
12610                                                         }
12611                                                 }                                       
12612                                                 if (isset($table_el['attribute']['cellspacing'])) {
12613                                                         $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
12614                                                         $this->y += $cellspacing;
12615                                                 }                               
12616                                                 $this->Ln(0, $cell);
12617                                                 $this->x = $parent['startx'];
12618                                                 if ($endpage > $startpage) {
12619                                                         if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
12620                                                                 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
12621                                                         } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
12622                                                                 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
12623                                                         }
12624                                                 }
12625                                         }
12626                                         if (isset($parent['cellpadding'])) {
12627                                                 $this->cMargin = $this->oldcMargin;
12628                                         }
12629                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
12630                                         if (!$this->empty_string($this->theadMargin)) {
12631                                                 // restore top margin
12632                                                 $this->tMargin = $this->theadMargin;
12633                                                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
12634                                         }
12635                                         // reset table header
12636                                         $this->thead = '';
12637                                         $this->theadMargin = '';
12638                                         break;
12639                                 }
12640                                 case 'a': {
12641                                         $this->HREF = '';
12642                                         break;
12643                                 }
12644                                 case 'sup': {
12645                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
12646                                         break;
12647                                 }
12648                                 case 'sub': {
12649                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
12650                                         break;
12651                                 }
12652                                 case 'div': {
12653                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12654                                         break;
12655                                 }
12656                                 case 'blockquote': {
12657                                         if ($this->rtl) {
12658                                                 $this->rMargin -= $this->listindent;
12659                                         } else {
12660                                                 $this->lMargin -= $this->listindent;
12661                                         }
12662                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12663                                         break;
12664                                 }
12665                                 case 'p': {
12666                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12667                                         break;
12668                                 }
12669                                 case 'pre': {
12670                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12671                                         $this->premode = false;
12672                                         break;
12673                                 }
12674                                 case 'dl': {
12675                                         --$this->listnum;
12676                                         if ($this->listnum <= 0) {
12677                                                 $this->listnum = 0;
12678                                                 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12679                                         }
12680                                         break;
12681                                 }
12682                                 case 'dt': {
12683                                         $this->lispacer = '';
12684                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12685                                         break;
12686                                 }
12687                                 case 'dd': {
12688                                         $this->lispacer = '';
12689                                         if ($this->rtl) {
12690                                                 $this->rMargin -= $this->listindent;
12691                                         } else {
12692                                                 $this->lMargin -= $this->listindent;
12693                                         }
12694                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12695                                         break;
12696                                 }
12697                                 case 'ul':
12698                                 case 'ol': {
12699                                         --$this->listnum;
12700                                         $this->lispacer = '';
12701                                         if ($this->rtl) {
12702                                                 $this->rMargin -= $this->listindent;
12703                                         } else {
12704                                                 $this->lMargin -= $this->listindent;
12705                                         }
12706                                         if ($this->listnum <= 0) {
12707                                                 $this->listnum = 0;
12708                                                 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12709                                         }
12710                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
12711                                         break;
12712                                 }
12713                                 case 'li': {
12714                                         $this->lispacer = '';
12715                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12716                                         break;
12717                                 }
12718                                 case 'h1': 
12719                                 case 'h2': 
12720                                 case 'h3': 
12721                                 case 'h4': 
12722                                 case 'h5': 
12723                                 case 'h6': {
12724                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
12725                                         break;
12726                                 }
12727                                 default : {
12728                                         break;
12729                                 }
12730                         }
12731                         $this->tmprtl = false;
12732                 }
12733                 
12734                 /**
12735                  * Add vertical spaces if needed.
12736                  * @param int $n number of spaces to add
12737                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12738                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
12739                  * @param boolean $firstorlast if true do not print additional empty lines.
12740                  * @param string $tag HTML tag to which this space will be applied
12741                  * @param boolean $closing true if this space will be applied to a closing tag, false otherwise
12742                  * @access protected
12743                  */
12744                 protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
12745                         if ($firstorlast) {
12746                                 $this->Ln(0, $cell);
12747                                 $this->htmlvspace = 0;
12748                                 return;
12749                         }
12750                         if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
12751                                 $n = $this->tagvspaces[$tag][intval($closing)]['n'];
12752                         }
12753                         if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
12754                                 $h = $this->tagvspaces[$tag][intval($closing)]['h'];
12755                         }
12756                         if (is_string($h)) {
12757                                 $vsize = $n * $this->lasth;
12758                         } else {
12759                                 $vsize = $n * $h;
12760                         }
12761                         if ($vsize > $this->htmlvspace) {
12762                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
12763                                 $this->htmlvspace = $vsize;
12764                         }
12765                 }
12766                 
12767                 /**
12768                  * Set the default bullet to be used as LI bullet symbol
12769                  * @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')
12770                  * @access public
12771                  * @since 4.0.028 (2008-09-26)
12772                  */
12773                 public function setLIsymbol($symbol='!') {
12774                         $symbol = strtolower($symbol);
12775                         switch ($symbol) {
12776                                 case '!' :
12777                                 case '#' :
12778                                 case 'disc' :
12779                                 case 'disc' :
12780                                 case 'circle' :
12781                                 case 'square' :
12782                                 case '1':
12783                                 case 'decimal':
12784                                 case 'decimal-leading-zero':
12785                                 case 'i':
12786                                 case 'lower-roman':
12787                                 case 'I':
12788                                 case 'upper-roman':
12789                                 case 'a':
12790                                 case 'lower-alpha':
12791                                 case 'lower-latin':
12792                                 case 'A':
12793                                 case 'upper-alpha':
12794                                 case 'upper-latin':
12795                                 case 'lower-greek': {
12796                                         $this->lisymbol = $symbol;
12797                                         break;
12798                                 }
12799                                 default : {
12800                                         $this->lisymbol = '';
12801                                 }
12802                         }
12803                 }
12804                 
12805                 /**
12806                 * Set the booklet mode for double-sided pages.
12807                 * @param boolean $booklet true set the booklet mode on, fals eotherwise.
12808                 * @param float $inner Inner page margin.
12809                 * @param float $outer Outer page margin.
12810                 * @access public
12811                 * @since 4.2.000 (2008-10-29)
12812                 */
12813                 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
12814                         $this->booklet = $booklet;
12815                         if ($inner >= 0) {
12816                                 $this->lMargin = $inner;
12817                         }
12818                         if ($outer >= 0) {
12819                                 $this->rMargin = $outer;
12820                         }
12821                 }
12822                 
12823                 /**
12824                 * Swap the left and right margins.
12825                 * @param boolean $reverse if true swap left and right margins.
12826                 * @access protected
12827                 * @since 4.2.000 (2008-10-29)
12828                 */
12829                 protected function swapMargins($reverse=true) {
12830                         if ($reverse) {
12831                                 // swap left and right margins
12832                                 $mtemp = $this->original_lMargin;
12833                                 $this->original_lMargin = $this->original_rMargin;
12834                                 $this->original_rMargin = $mtemp;
12835                                 $deltam = $this->original_lMargin - $this->original_rMargin;
12836                                 $this->lMargin += $deltam;
12837                                 $this->rMargin -= $deltam;
12838                         }
12839                 }
12840
12841                 /**
12842                 * Set the vertical spaces for HTML tags.
12843                 * The array must have the following structure (example):
12844                 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
12845                 * The first array level contains the tag names,
12846                 * the second level contains 0 for opening tags or 1 for closing tags,
12847                 * the third level contains the vertical space unit (h) and the number spaces to add (n).
12848                 * If the h parameter is not specified, default values are used.
12849                 * @param array $tagvs array of tags and relative vertical spaces.
12850                 * @access public
12851                 * @since 4.2.001 (2008-10-30)
12852                 */
12853                 public function setHtmlVSpace($tagvs) {
12854                         $this->tagvspaces = $tagvs;
12855                 }
12856
12857         /**
12858                 * Set custom width for list indentation.
12859                 * @param float $width width of the indentation. Use negative value to disable it.
12860                 * @access public
12861                 * @since 4.2.007 (2008-11-12)
12862                 */
12863                 public function setListIndentWidth($width) {
12864                         return $this->customlistindent = floatval($width);
12865         }
12866
12867         /**
12868                 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
12869                 * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page.
12870                 * @access public
12871                 * @since 4.2.010 (2008-11-14)
12872                 */
12873                 public function setOpenCell($isopen) {
12874                         $this->opencell = $isopen;
12875         }
12876
12877         /**
12878                 * Set the color and font style for HTML links.
12879                 * @param array $color RGB array of colors
12880                 * @param string $fontstyle additional font styles to add
12881                 * @access public
12882                 * @since 4.4.003 (2008-12-09)
12883                 */
12884                 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
12885                         $this->htmlLinkColorArray = $color;
12886                         $this->htmlLinkFontStyle = $fontstyle;
12887         }
12888
12889         /**
12890                 * convert html string containing value and unit of measure to user's units or points.
12891                 * @param string $htmlval string containing values and unit
12892                 * @param string $refsize reference value in points
12893                 * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
12894                 * @param boolean $point if true returns points, otherwise returns value in user's units
12895                 * @return float value in user's unit or point if $points=true
12896                 * @access public
12897                 * @since 4.4.004 (2008-12-10)
12898                 */
12899         public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
12900                         $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
12901                         $retval = 0;
12902                         $value = 0;
12903                         $unit = 'px';
12904                         $k = $this->k;
12905                         if ($points) {
12906                                 $k = 1;
12907                         }
12908                         if (in_array($defaultunit, $supportedunits)) {
12909                                 $unit = $defaultunit;
12910                         }
12911                         if (is_numeric($htmlval)) {
12912                                 $value = floatval($htmlval);
12913                         } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
12914                                 $value = floatval($mnum[1]);
12915                                 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
12916                                         if (in_array($munit[1], $supportedunits)) {
12917                                                 $unit = $munit[1];
12918                                         }
12919                                 }
12920                         }
12921                         switch ($unit) {
12922                                 // percentage
12923                                 case '%': {
12924                                         $retval = (($value * $refsize) / 100);
12925                                         break;
12926                                 }
12927                                 // relative-size
12928                                 case 'em': {
12929                                         $retval = ($value * $refsize);
12930                                         break;
12931                                 }
12932                                 case 'ex': {
12933                                         $retval = $value * ($refsize / 2);
12934                                         break;
12935                                 }
12936                                 // absolute-size
12937                                 case 'in': {
12938                                         $retval = ($value * $this->dpi) / $k;
12939                                         break;
12940                                 }
12941                                 case 'cm': {
12942                                         $retval = ($value / 2.54 * $this->dpi) / $k;
12943                                         break;
12944                                 }
12945                                 case 'mm': {
12946                                         $retval = ($value / 25.4 * $this->dpi) / $k;
12947                                         break;
12948                                 }
12949                                 case 'pc': {
12950                                         // one pica is 12 points
12951                                         $retval = ($value * 12) / $k;
12952                                         break;
12953                                 }
12954                                 case 'pt': {
12955                                         $retval = $value / $k;
12956                                         break;
12957                                 }
12958                                 case 'px': {
12959                                         $retval = $this->pixelsToUnits($value);
12960                                         break;
12961                                 }
12962                         }
12963                         return $retval;
12964                 }
12965
12966                 /**
12967                 * Returns the Roman representation of an integer number
12968                 * @param int number to convert
12969                 * @return string roman representation of the specified number
12970                 * @access public
12971                 * @since 4.4.004 (2008-12-10)
12972                 */
12973                 public function intToRoman($number) {
12974                         $roman = '';
12975                         while ($number >= 1000) {
12976                                 $roman .= 'M';
12977                                 $number -= 1000;
12978                         }
12979                         while ($number >= 900) {
12980                                 $roman .= 'CM';
12981                                 $number -= 900;
12982                         }
12983                         while ($number >= 500) {
12984                                 $roman .= 'D';
12985                                 $number -= 500;
12986                         }
12987                         while ($number >= 400) {
12988                                 $roman .= 'CD';
12989                                 $number -= 400;
12990                         }
12991                         while ($number >= 100) {
12992                                 $roman .= 'C';
12993                                 $number -= 100;
12994                         }
12995                         while ($number >= 90) {
12996                         $roman .= 'XC';
12997                         $number -= 90;
12998                         }
12999                         while ($number >= 50) {
13000                                 $roman .= 'L';
13001                                 $number -= 50;
13002                         }
13003                         while ($number >= 40) {
13004                                 $roman .= 'XL';
13005                                 $number -= 40;
13006                         }
13007                         while ($number >= 10) {
13008                         $roman .= 'X';
13009                         $number -= 10;
13010                         }
13011                         while ($number >= 9) {
13012                                 $roman .= 'IX';
13013                                 $number -= 9;
13014                         }
13015                         while ($number >= 5) {
13016                                 $roman .= 'V';
13017                                 $number -= 5;
13018                         }
13019                         while ($number >= 4) {
13020                         $roman .= 'IV';
13021                         $number -= 4;
13022                         }
13023                         while ($number >= 1) {
13024                                 $roman .= 'I';
13025                                 --$number;
13026                         }
13027                         return $roman;
13028                 }
13029
13030                 /**
13031                 * Output an HTML list bullet or ordered item symbol
13032                 * @param int $listdepth list nesting level
13033                 * @param string $listtype type of list
13034                 * @param float $size current font size
13035                 * @access protected
13036                 * @since 4.4.004 (2008-12-10)
13037                 */
13038                 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
13039                     $size /= $this->k;
13040                     $fill = '';
13041                     $color = $this->fgcolor;
13042                     $width = 0;
13043                     $textitem = '';
13044                     $tmpx = $this->x;           
13045                         $lspace = $this->GetStringWidth('  ');
13046                         if ($listtype == '!') {
13047                                 // set default list type for unordered list
13048                                 $deftypes = array('disc', 'circle', 'square');
13049                                 $listtype = $deftypes[($listdepth - 1) % 3];
13050                         } elseif ($listtype == '#') {
13051                                 // set default list type for ordered list
13052                                 $listtype = 'decimal';
13053                         }
13054                 switch ($listtype) {
13055                         // unordered types
13056                                 case 'none': {
13057                                         break;
13058                                 }
13059                                 case 'disc': {
13060                                         $fill = 'F';
13061                                 }
13062                                 case 'circle': {
13063                                         $fill .= 'D';
13064                                         $r = $size / 6;
13065                                         $lspace += (2 * $r);
13066                                         if ($this->rtl) {
13067                                                 $this->x = $this->w - $this->x - $lspace;
13068                                         } else {
13069                                                 $this->x -= $lspace;
13070                                         }
13071                                         $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
13072                                         break;
13073                                 }
13074                                 case 'square': {
13075                                         $l = $size / 3;
13076                                         $lspace += $l;
13077                                         if ($this->rtl) {
13078                                                 $this->x = $this->w - $this->x - $lspace;
13079                                         } else {
13080                                                 $this->x -= $lspace;
13081                                         }
13082                                         $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
13083                                         break;
13084                                 }
13085                                 // ordered types
13086
13087                                 // $this->listcount[$this->listnum];
13088                                 // $textitem
13089                                 case '1':
13090                                 case 'decimal': {
13091                                         $textitem = $this->listcount[$this->listnum];
13092                                         break;
13093                                 }
13094                                 case 'decimal-leading-zero': {
13095                                         $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
13096                                         break;
13097                                 }
13098                                 case 'i':
13099                                 case 'lower-roman': {
13100                                         $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
13101                                         break;
13102                                 }
13103                                 case 'I':
13104                                 case 'upper-roman': {
13105                                         $textitem = $this->intToRoman($this->listcount[$this->listnum]);
13106                                         break;
13107                                 }
13108                                 case 'a':
13109                                 case 'lower-alpha':
13110                                 case 'lower-latin': {
13111                                         $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
13112                                         break;
13113                                 }
13114                                 case 'A':
13115                                 case 'upper-alpha':
13116                                 case 'upper-latin': {
13117                                         $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
13118                                         break;
13119                                 }
13120                                 case 'lower-greek': {
13121                                         $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
13122                                         break;
13123                                 }
13124                                 /*
13125                                 // Types to be implemented (special handling)
13126                                 case 'hebrew': {
13127                                         break;
13128                                 }
13129                                 case 'armenian': {
13130                                         break;
13131                                 }
13132                                 case 'georgian': {
13133                                         break;
13134                                 }
13135                                 case 'cjk-ideographic': {
13136                                         break;
13137                                 }
13138                                 case 'hiragana': {
13139                                         break;
13140                                 }
13141                                 case 'katakana': {
13142                                         break;
13143                                 }
13144                                 case 'hiragana-iroha': {
13145                                         break;
13146                                 }
13147                                 case 'katakana-iroha': {
13148                                         break;
13149                                 }
13150                                 */
13151                                 default: {
13152                                         $textitem = $this->listcount[$this->listnum];
13153                                 }
13154                         }
13155                         if (!$this->empty_string($textitem)) {
13156                                 // print ordered item
13157                                 if ($this->rtl) {
13158                                         $textitem = '.'.$textitem;
13159                                 } else {
13160                                         $textitem = $textitem.'.';
13161                                 }
13162                                 $lspace += $this->GetStringWidth($textitem);
13163                                 if ($this->rtl) {
13164                                         $this->x += $lspace;
13165                                 } else {
13166                                         $this->x -= $lspace;
13167                                 }
13168                                 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
13169                         }
13170                         $this->x = $tmpx;
13171                         $this->lispacer = '';
13172                 }
13173
13174         /**
13175                 * Returns current graphic variables as array.
13176                 * @return array graphic variables
13177                 * @access protected
13178                 * @since 4.2.010 (2008-11-14)
13179                 */
13180                 protected function getGraphicVars() {
13181                         $grapvars = array(
13182                                 'FontFamily' => $this->FontFamily,
13183                                 'FontStyle' => $this->FontStyle,
13184                                 'FontSizePt' => $this->FontSizePt,
13185                                 'rMargin' => $this->rMargin,
13186                                 'lMargin' => $this->lMargin,
13187                                 'cMargin' => $this->cMargin,
13188                                 'LineWidth' => $this->LineWidth,
13189                                 'linestyleWidth' => $this->linestyleWidth,
13190                                 'linestyleCap' => $this->linestyleCap,
13191                                 'linestyleJoin' => $this->linestyleJoin,
13192                                 'linestyleDash' => $this->linestyleDash,
13193                                 'DrawColor' => $this->DrawColor,
13194                                 'FillColor' => $this->FillColor,
13195                                 'TextColor' => $this->TextColor,
13196                                 'ColorFlag' => $this->ColorFlag,
13197                                 'bgcolor' => $this->bgcolor,
13198                                 'fgcolor' => $this->fgcolor,
13199                                 'htmlvspace' => $this->htmlvspace,
13200                                 'lasth' => $this->lasth
13201                                 );
13202                         return $grapvars;
13203                 }
13204
13205         /**
13206                 * Set graphic variables.
13207                 * @param $gvars array graphic variables
13208                 * @access protected
13209                 * @since 4.2.010 (2008-11-14)
13210                 */
13211                 protected function setGraphicVars($gvars) {
13212                         $this->FontFamily = $gvars['FontFamily'];
13213                         $this->FontStyle = $gvars['FontStyle'];
13214                         $this->FontSizePt = $gvars['FontSizePt'];
13215                         $this->rMargin = $gvars['rMargin'];
13216                         $this->lMargin = $gvars['lMargin'];
13217                         $this->cMargin = $gvars['cMargin'];
13218                         $this->LineWidth = $gvars['LineWidth'];
13219                         $this->linestyleWidth = $gvars['linestyleWidth'];
13220                         $this->linestyleCap = $gvars['linestyleCap'];
13221                         $this->linestyleJoin = $gvars['linestyleJoin'];
13222                         $this->linestyleDash = $gvars['linestyleDash'];
13223                         $this->DrawColor = $gvars['DrawColor'];
13224                         $this->FillColor = $gvars['FillColor'];
13225                         $this->TextColor = $gvars['TextColor'];
13226                         $this->ColorFlag = $gvars['ColorFlag'];
13227                         $this->bgcolor = $gvars['bgcolor'];
13228                         $this->fgcolor = $gvars['fgcolor'];
13229                         $this->htmlvspace = $gvars['htmlvspace'];
13230                         //$this->lasth = $gvars['lasth'];
13231                         $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
13232                         if (!$this->empty_string($this->FontFamily)) {
13233                                 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
13234                         }
13235                 }
13236
13237                 /**
13238                 * Returns a temporary filename for caching object on filesystem.
13239                 * @param string $prefix prefix to add to filename
13240                 * return string filename.
13241                 * @access protected
13242                 * @since 4.5.000 (2008-12-31)
13243                 */
13244                 protected function getObjFilename($name) {
13245                         return tempnam(K_PATH_CACHE, $name.'_');
13246                 }
13247
13248         /**
13249                 * Writes data to a temporary file on filesystem.
13250                 * @param string $file file name
13251                 * @param mixed $data data to write on file
13252                 * @param boolean $append if true append data, false replace.
13253                 * @access protected
13254                 * @since 4.5.000 (2008-12-31)
13255                 */
13256                 protected function writeDiskCache($filename, $data, $append=false) {
13257                         if ($append) {
13258                                 $fmode = 'ab+';
13259                         } else {
13260                                 $fmode = 'wb+';
13261                         }
13262                         $f = @fopen($filename, $fmode);
13263                         if (!$f) {
13264                                 $this->Error('Unable to write cache file: '.$filename);
13265                         } else {
13266                                 fwrite($f, $data);
13267                                 fclose($f);
13268                         }
13269                         // update file lenght (needed for transactions)
13270                         if (!isset($this->cache_file_lenght['_'.$filename])) {
13271                                 $this->cache_file_lenght['_'.$filename] = strlen($data);
13272                         } else {
13273                                 $this->cache_file_lenght['_'.$filename] += strlen($data);
13274                         }
13275                 }
13276
13277         /**
13278                 * Read data from a temporary file on filesystem.
13279                 * @param string $file file name
13280                 * @return mixed retrieved data
13281                 * @access protected
13282                 * @since 4.5.000 (2008-12-31)
13283                 */
13284                 protected function readDiskCache($filename) {
13285                         return file_get_contents($filename);
13286                 }
13287
13288                 /**
13289                 * Set buffer content (always append data).
13290                 * @param string $data data
13291                 * @access protected
13292                 * @since 4.5.000 (2009-01-02)
13293                 */
13294                 protected function setBuffer($data) {
13295                         $this->bufferlen += strlen($data);
13296                         if ($this->diskcache) {
13297                                 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
13298                                         $this->buffer = $this->getObjFilename('buffer');
13299                                 }
13300                                 $this->writeDiskCache($this->buffer, $data, true);
13301                         } else {
13302                                 $this->buffer .= $data;
13303                         }
13304                 }
13305
13306         /**
13307                 * Get buffer content.
13308                 * @return string buffer content
13309                 * @access protected
13310                 * @since 4.5.000 (2009-01-02)
13311                 */
13312                 protected function getBuffer() {
13313                         if ($this->diskcache) {
13314                                 return $this->readDiskCache($this->buffer);
13315                         } else {
13316                                 return $this->buffer;
13317                         }
13318                 }
13319
13320         /**
13321                 * Set page buffer content.
13322                 * @param int $page page number
13323                 * @param string $data page data
13324                 * @param boolean $append if true append data, false replace.
13325                 * @access protected
13326                 * @since 4.5.000 (2008-12-31)
13327                 */
13328                 protected function setPageBuffer($page, $data, $append=false) {
13329                         if ($this->diskcache) {
13330                                 if (!isset($this->pages[$page])) {
13331                                         $this->pages[$page] = $this->getObjFilename('page'.$page);
13332                                 }
13333                                 $this->writeDiskCache($this->pages[$page], $data, $append);
13334                         } else {
13335                                 if ($append) {
13336                                         $this->pages[$page] .= $data;
13337                                 } else {
13338                                         $this->pages[$page] = $data;
13339                                 }
13340                         }
13341                         if ($append AND isset($this->pagelen[$page])) {
13342                                 $this->pagelen[$page] += strlen($data);
13343                         } else {
13344                                 $this->pagelen[$page] = strlen($data);
13345                         }
13346                 }
13347
13348         /**
13349                 * Get page buffer content.
13350                 * @param int $page page number
13351                 * @return string page buffer content or false in case of error
13352                 * @access protected
13353                 * @since 4.5.000 (2008-12-31)
13354                 */
13355                 protected function getPageBuffer($page) {
13356                         if ($this->diskcache) {
13357                                 return $this->readDiskCache($this->pages[$page]);
13358                         } elseif (isset($this->pages[$page])) {
13359                                 return $this->pages[$page];
13360                         }
13361                         return false;
13362                 }
13363
13364         /**
13365                 * Set image buffer content.
13366                 * @param string $image image key
13367                 * @param array $data image data
13368                 * @access protected
13369                 * @since 4.5.000 (2008-12-31)
13370                 */
13371                 protected function setImageBuffer($image, $data) {
13372                         if ($this->diskcache) {
13373                                 if (!isset($this->images[$image])) {
13374                                         $this->images[$image] = $this->getObjFilename('image'.$image);
13375                                 }
13376                                 $this->writeDiskCache($this->images[$image], serialize($data));
13377                         } else {
13378                                 $this->images[$image] = $data;
13379                         }
13380                         if (!in_array($image, $this->imagekeys)) {
13381                                 $this->imagekeys[] = $image;
13382                         }
13383                         ++$this->numimages;
13384                 }
13385
13386         /**
13387                 * Set image buffer content.
13388                 * @param string $image image key
13389                 * @param string $key image sub-key
13390                 * @param array $data image data
13391                 * @access protected
13392                 * @since 4.5.000 (2008-12-31)
13393                 */
13394                 protected function setImageSubBuffer($image, $key, $data) {
13395                         if (!isset($this->images[$image])) {
13396                                 $this->setImageBuffer($image, array());
13397                         }
13398                         if ($this->diskcache) {
13399                                 $tmpimg = $this->getImageBuffer($image);
13400                                 $tmpimg[$key] = $data;
13401                                 $this->writeDiskCache($this->images[$image], serialize($tmpimg));
13402                         } else {
13403                                 $this->images[$image][$key] = $data;
13404                         }
13405                 }
13406
13407         /**
13408                 * Get page buffer content.
13409                 * @param string $image image key
13410                 * @return string image buffer content or false in case of error
13411                 * @access protected
13412                 * @since 4.5.000 (2008-12-31)
13413                 */
13414                 protected function getImageBuffer($image) {
13415                         if ($this->diskcache AND isset($this->images[$image])) {
13416                                 return unserialize($this->readDiskCache($this->images[$image]));
13417                         } elseif (isset($this->images[$image])) {
13418                                 return $this->images[$image];
13419                         }
13420                         return false;
13421                 }
13422
13423                 /**
13424                 * Set font buffer content.
13425                 * @param string $font font key
13426                 * @param array $data font data
13427                 * @access protected
13428                 * @since 4.5.000 (2009-01-02)
13429                 */
13430                 protected function setFontBuffer($font, $data) {
13431                         if ($this->diskcache) {
13432                                 if (!isset($this->fonts[$font])) {
13433                                         $this->fonts[$font] = $this->getObjFilename('font');
13434                                 }
13435                                 $this->writeDiskCache($this->fonts[$font], serialize($data));
13436                         } else {
13437                                 $this->fonts[$font] = $data;
13438                         }
13439                         if (!in_array($font, $this->fontkeys)) {
13440                                 $this->fontkeys[] = $font;
13441                         }
13442                 }
13443
13444         /**
13445                 * Set font buffer content.
13446                 * @param string $font font key
13447                 * @param string $key font sub-key
13448                 * @param array $data font data
13449                 * @access protected
13450                 * @since 4.5.000 (2009-01-02)
13451                 */
13452                 protected function setFontSubBuffer($font, $key, $data) {
13453                         if (!isset($this->fonts[$font])) {
13454                                 $this->setFontBuffer($font, array());
13455                         }
13456                         if ($this->diskcache) {
13457                                 $tmpfont = $this->getFontBuffer($font);
13458                                 $tmpfont[$key] = $data;
13459                                 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
13460                         } else {
13461                                 $this->fonts[$font][$key] = $data;
13462                         }
13463                 }
13464
13465         /**
13466                 * Get font buffer content.
13467                 * @param string $font font key
13468                 * @return string font buffer content or false in case of error
13469                 * @access protected
13470                 * @since 4.5.000 (2009-01-02)
13471                 */
13472                 protected function getFontBuffer($font) {
13473                         if ($this->diskcache AND isset($this->fonts[$font])) {
13474                                 return unserialize($this->readDiskCache($this->fonts[$font]));
13475                         } elseif (isset($this->fonts[$font])) {
13476                                 return $this->fonts[$font];
13477                         }
13478                         return false;
13479                 }
13480
13481         /**
13482                 * Move a page to a previous position.
13483                 * @param int $frompage number of the source page
13484                 * @param int $topage number of the destination page (must be less than $frompage)
13485                 * @return true in case of success, false in case of error.
13486                 * @access public
13487                 * @since 4.5.000 (2009-01-02)
13488                 */
13489                 public function movePage($frompage, $topage) {
13490                         if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
13491                                 return false;
13492                         }
13493                         if ($frompage == $this->page) {
13494                                 // close the page before moving it
13495                                 $this->endPage();
13496                         }
13497                         // move all page-related states
13498                         $tmppage = $this->pages[$frompage];
13499                         $tmppagedim = $this->pagedim[$frompage];
13500                         $tmppagelen = $this->pagelen[$frompage];
13501                         $tmpintmrk = $this->intmrk[$frompage];
13502                         if (isset($this->footerpos[$frompage])) {
13503                                 $tmpfooterpos = $this->footerpos[$frompage];
13504                         }
13505                         if (isset($this->footerlen[$frompage])) {
13506                                 $tmpfooterlen = $this->footerlen[$frompage];
13507                         }
13508                         if (isset($this->transfmrk[$frompage])) {
13509                                 $tmptransfmrk = $this->transfmrk[$frompage];
13510                         }
13511                         if (isset($this->PageAnnots[$frompage])) {
13512                                 $tmpannots = $this->PageAnnots[$frompage];
13513                         }
13514                         if (isset($this->newpagegroup[$frompage])) {
13515                                 $tmpnewpagegroup = $this->newpagegroup[$frompage];
13516                         }
13517                         for ($i = $frompage; $i > $topage; --$i) {
13518                                 $j = $i - 1;
13519                                 // shift pages down
13520                                 $this->pages[$i] = $this->pages[$j];
13521                                 $this->pagedim[$i] = $this->pagedim[$j];
13522                                 $this->pagelen[$i] = $this->pagelen[$j];
13523                                 $this->intmrk[$i] = $this->intmrk[$j];
13524                                 if (isset($this->footerpos[$j])) {
13525                                         $this->footerpos[$i] = $this->footerpos[$j];
13526                                 } elseif (isset($this->footerpos[$i])) {
13527                                         unset($this->footerpos[$i]);
13528                                 }
13529                                 if (isset($this->footerlen[$j])) {
13530                                         $this->footerlen[$i] = $this->footerlen[$j];
13531                                 } elseif (isset($this->footerlen[$i])) {
13532                                         unset($this->footerlen[$i]);
13533                                 }
13534                                 if (isset($this->transfmrk[$j])) {
13535                                         $this->transfmrk[$i] = $this->transfmrk[$j];
13536                                 } elseif (isset($this->transfmrk[$i])) {
13537                                         unset($this->transfmrk[$i]);
13538                                 }
13539                                 if (isset($this->PageAnnots[$j])) {
13540                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
13541                                 } elseif (isset($this->PageAnnots[$i])) {
13542                                         unset($this->PageAnnots[$i]);
13543                                 }
13544                                 if (isset($this->newpagegroup[$j])) {
13545                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
13546                                 } elseif (isset($this->newpagegroup[$i])) {
13547                                         unset($this->newpagegroup[$i]);
13548                                 }
13549                         }
13550                         $this->pages[$topage] = $tmppage;
13551                         $this->pagedim[$topage] = $tmppagedim;
13552                         $this->pagelen[$topage] = $tmppagelen;
13553                         $this->intmrk[$topage] = $tmpintmrk;
13554                         if (isset($tmpfooterpos)) {
13555                                 $this->footerpos[$topage] = $tmpfooterpos;
13556                         } elseif (isset($this->footerpos[$topage])) {
13557                                 unset($this->footerpos[$topage]);
13558                         }
13559                         if (isset($tmpfooterlen)) {
13560                                 $this->footerlen[$topage] = $tmpfooterlen;
13561                         } elseif (isset($this->footerlen[$topage])) {
13562                                 unset($this->footerlen[$topage]);
13563                         }
13564                         if (isset($tmptransfmrk)) {
13565                                 $this->transfmrk[$topage] = $tmptransfmrk;
13566                         } elseif (isset($this->transfmrk[$topage])) {
13567                                 unset($this->transfmrk[$topage]);
13568                         }
13569                         if (isset($tmpannots)) {
13570                                 $this->PageAnnots[$topage] = $tmpannots;
13571                         } elseif (isset($this->PageAnnots[$topage])) {
13572                                 unset($this->PageAnnots[$topage]);
13573                         }
13574                         if (isset($tmpnewpagegroup)) {
13575                                 $this->newpagegroup[$topage] = $tmpnewpagegroup;
13576                         } elseif (isset($this->newpagegroup[$topage])) {
13577                                 unset($this->newpagegroup[$topage]);
13578                         }
13579                         // adjust outlines
13580                         $tmpoutlines = $this->outlines;
13581                         foreach ($tmpoutlines as $key => $outline) {
13582                                 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
13583                                         $this->outlines[$key]['p'] = $outline['p'] + 1;
13584                                 } elseif ($outline['p'] == $frompage) {
13585                                         $this->outlines[$key]['p'] = $topage;
13586                                 }
13587                         }
13588                         // adjust links
13589                         $tmplinks = $this->links;
13590                         foreach ($tmplinks as $key => $link) {
13591                                 if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
13592                                         $this->links[$key][0] = $link[0] + 1;
13593                                 } elseif ($link[0] == $frompage) {
13594                                         $this->links[$key][0] = $topage;
13595                                 }
13596                         }
13597                         // adjust javascript
13598                         $tmpjavascript = $this->javascript;
13599                         global $jfrompage, $jtopage;
13600                         $jfrompage = $frompage;
13601                         $jtopage = $topage;
13602                         $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13603                                 create_function('$matches', 'global $jfrompage, $jtopage;
13604                                 $pagenum = intval($matches[3]) + 1;
13605                                 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
13606                                         $newpage = ($pagenum + 1);
13607                                 } elseif ($pagenum == $jfrompage) {
13608                                         $newpage = $jtopage;
13609                                 } else {
13610                                         $newpage = $pagenum;
13611                                 }
13612                                 --$newpage;
13613                                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13614                         // return to last page
13615                         $this->lastPage(true);
13616                         return true;
13617                 }
13618
13619         /**
13620                 * Remove the specified page.
13621                 * @param int $page page to remove
13622                 * @return true in case of success, false in case of error.
13623                 * @access public
13624                 * @since 4.6.004 (2009-04-23)
13625                 */
13626                 public function deletePage($page) {
13627                         if ($page > $this->numpages) {
13628                                 return false;
13629                         }
13630                         // delete current page
13631                         unset($this->pages[$page]);
13632                         unset($this->pagedim[$page]);
13633                         unset($this->pagelen[$page]);
13634                         unset($this->intmrk[$page]);
13635                         if (isset($this->footerpos[$page])) {
13636                                 unset($this->footerpos[$page]);
13637                         }
13638                         if (isset($this->footerlen[$page])) {
13639                                 unset($this->footerlen[$page]);
13640                         }
13641                         if (isset($this->transfmrk[$page])) {
13642                                 unset($this->transfmrk[$page]);
13643                         }
13644                         if (isset($this->PageAnnots[$page])) {
13645                                 unset($this->PageAnnots[$page]);
13646                         }
13647                         if (isset($this->newpagegroup[$page])) {
13648                                 unset($this->newpagegroup[$page]);
13649                         }
13650                         if (isset($this->pageopen[$page])) {
13651                                 unset($this->pageopen[$page]);
13652                         }
13653                         // update remaining pages
13654                         for ($i = $page; $i < $this->numpages; ++$i) {
13655                                 $j = $i + 1;
13656                                 // shift pages
13657                                 $this->pages[$i] = $this->pages[$j];
13658                                 $this->pagedim[$i] = $this->pagedim[$j];
13659                                 $this->pagelen[$i] = $this->pagelen[$j];
13660                                 $this->intmrk[$i] = $this->intmrk[$j];
13661                                 if (isset($this->footerpos[$j])) {
13662                                         $this->footerpos[$i] = $this->footerpos[$j];
13663                                 } elseif (isset($this->footerpos[$i])) {
13664                                         unset($this->footerpos[$i]);
13665                                 }
13666                                 if (isset($this->footerlen[$j])) {
13667                                         $this->footerlen[$i] = $this->footerlen[$j];
13668                                 } elseif (isset($this->footerlen[$i])) {
13669                                         unset($this->footerlen[$i]);
13670                                 }
13671                                 if (isset($this->transfmrk[$j])) {
13672                                         $this->transfmrk[$i] = $this->transfmrk[$j];
13673                                 } elseif (isset($this->transfmrk[$i])) {
13674                                         unset($this->transfmrk[$i]);
13675                                 }
13676                                 if (isset($this->PageAnnots[$j])) {
13677                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
13678                                 } elseif (isset($this->PageAnnots[$i])) {
13679                                         unset($this->PageAnnots[$i]);
13680                                 }
13681                                 if (isset($this->newpagegroup[$j])) {
13682                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
13683                                 } elseif (isset($this->newpagegroup[$i])) {
13684                                         unset($this->newpagegroup[$i]);
13685                                 }
13686                                 if (isset($this->pageopen[$j])) {
13687                                         $this->pageopen[$i] = $this->pageopen[$j];
13688                                 } elseif (isset($this->pageopen[$i])) {
13689                                         unset($this->pageopen[$i]);
13690                                 }
13691                         }
13692                         // remove last page
13693                         unset($this->pages[$this->numpages]);
13694                         unset($this->pagedim[$this->numpages]);
13695                         unset($this->pagelen[$this->numpages]);
13696                         unset($this->intmrk[$this->numpages]);
13697                         if (isset($this->footerpos[$this->numpages])) {
13698                                 unset($this->footerpos[$this->numpages]);
13699                         }
13700                         if (isset($this->footerlen[$this->numpages])) {
13701                                 unset($this->footerlen[$this->numpages]);
13702                         }
13703                         if (isset($this->transfmrk[$this->numpages])) {
13704                                 unset($this->transfmrk[$this->numpages]);
13705                         }
13706                         if (isset($this->PageAnnots[$this->numpages])) {
13707                                 unset($this->PageAnnots[$this->numpages]);
13708                         }
13709                         if (isset($this->newpagegroup[$this->numpages])) {
13710                                 unset($this->newpagegroup[$this->numpages]);
13711                         }
13712                         if (isset($this->pageopen[$this->numpages])) {
13713                                 unset($this->pageopen[$this->numpages]);
13714                         }
13715                         --$this->numpages;
13716                         $this->page = $this->numpages;
13717                         // adjust outlines
13718                         $tmpoutlines = $this->outlines;
13719                         foreach ($tmpoutlines as $key => $outline) {
13720                                 if ($outline['p'] > $page) {
13721                                         $this->outlines[$key]['p'] = $outline['p'] - 1;
13722                                 } elseif ($outline['p'] == $page) {
13723                                         unset($this->outlines[$key]);
13724                                 }
13725                         }
13726                         // adjust links
13727                         $tmplinks = $this->links;
13728                         foreach ($tmplinks as $key => $link) {
13729                                 if ($link[0] > $page) {
13730                                         $this->links[$key][0] = $link[0] - 1;
13731                                 } elseif ($link[0] == $page) {
13732                                         unset($this->links[$key]);
13733                                 }
13734                         }
13735                         // adjust javascript
13736                         $tmpjavascript = $this->javascript;
13737                         global $jpage;
13738                         $jpage = $page;
13739                         $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13740                                 create_function('$matches', 'global $jpage;
13741                                 $pagenum = intval($matches[3]) + 1;
13742                                 if ($pagenum >= $jpage) {
13743                                         $newpage = ($pagenum - 1);
13744                                 } elseif ($pagenum == $jpage) {
13745                                         $newpage = 1;
13746                                 } else {
13747                                         $newpage = $pagenum;
13748                                 }
13749                                 --$newpage;
13750                                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13751                         // return to last page
13752                         $this->lastPage(true);
13753                         return true;
13754                 }
13755
13756                 /**
13757                 * Output a Table of Content Index (TOC).
13758                 * You can override this method to achieve different styles.
13759                 * @param int $page page number where this TOC should be inserted (leave empty for current page).
13760                 * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment).
13761                 * @param string $filler string used to fill the space between text and page number.
13762                 * @access public
13763                 * @author Nicola Asuni
13764                 * @since 4.5.000 (2009-01-02)
13765                 */
13766                 public function addTOC($page='', $numbersfont='', $filler='.') {
13767                         $fontsize = $this->FontSizePt;
13768                         $fontfamily = $this->FontFamily;
13769                         $fontstyle = $this->FontStyle;
13770                         $w = $this->w - $this->lMargin - $this->rMargin;
13771                         $spacer = $this->GetStringWidth(' ') * 4;
13772                         $page_first = $this->getPage();
13773                         $lmargin = $this->lMargin;
13774                         $rmargin = $this->rMargin;
13775                         $x_start = $this->GetX();
13776                         if ($this->empty_string($numbersfont)) {
13777                                 $numbersfont = $this->default_monospaced_font;
13778                         }
13779                         if ($this->empty_string($filler)) {
13780                                 $filler = ' ';
13781                         }
13782                         if ($this->empty_string($page)) {
13783                                 $gap = ' ';
13784                         } else {
13785                                 $gap = '';
13786                         }
13787                         foreach ($this->outlines as $key => $outline) {
13788                                 if ($this->rtl) {
13789                                         $aligntext = 'R';
13790                                         $alignnum = 'L';
13791                                 } else {
13792                                         $aligntext = 'L';
13793                                         $alignnum = 'R';
13794                                 }
13795                                 if ($outline['l'] == 0) {
13796                                         $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
13797                                 } else {
13798                                         $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
13799                                 }
13800                                 $indent = ($spacer * $outline['l']);
13801                                 if ($this->rtl) {
13802                                         $this->rMargin += $indent;
13803                                         $this->x -= $indent;
13804                                 } else {
13805                                         $this->lMargin += $indent;
13806                                         $this->x += $indent;
13807                                 }
13808                                 $link = $this->AddLink();
13809                                 $this->SetLink($link, 0, $outline['p']);
13810                                 // write the text
13811                                 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
13812                                 $this->SetFont($numbersfont, $fontstyle, $fontsize);
13813                                 if ($this->empty_string($page)) {
13814                                         $pagenum = $outline['p'];
13815                                 } else {
13816                                         // placemark to be replaced with the correct number
13817                                         $pagenum = '{#'.($outline['p']).'}';
13818                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13819                                                 $pagenum = '{'.$pagenum.'}';
13820                                     }
13821                                 }
13822                                 $numwidth = $this->GetStringWidth($pagenum);
13823                                 if ($this->rtl) {
13824                                         $tw = $this->x - $this->lMargin;
13825                                 } else {
13826                                         $tw = $this->w - $this->rMargin - $this->x;
13827                                 }
13828                                 $fw = $tw - $numwidth - $this->GetStringWidth(' ');
13829                                 $numfills = floor($fw / $this->GetStringWidth($filler));
13830                                 if ($numfills > 0) {
13831                                         $rowfill = str_repeat($filler, $numfills);
13832                                 } else {
13833                                         $rowfill = '';
13834                                 }
13835                                 if ($this->rtl) {
13836                                         $pagenum = $pagenum.$gap.$rowfill.' ';
13837                                 } else {
13838                                         $pagenum = ' '.$rowfill.$gap.$pagenum;
13839                                 }
13840                                 // write the number
13841                                 //$this->SetX($x_start);
13842                                 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
13843                                 $this->SetX($x_start);
13844                                 $this->lMargin = $lmargin;
13845                                 $this->rMargin = $rmargin;
13846                         }
13847                         $page_last = $this->getPage();
13848                         $numpages = $page_last - $page_first + 1;
13849                         if (!$this->empty_string($page)) {
13850                                 for ($p = $page_first; $p <= $page_last; ++$p) {
13851                                         // get page data
13852                                         $temppage = $this->getPageBuffer($p);
13853                                         for ($n = 1; $n <= $this->numpages; ++$n) {
13854                                                 // update page numbers
13855                                                 $k = '{#'.$n.'}';
13856                                                 $ku = '{'.$k.'}';
13857                                                 $alias_a = $this->_escape($k);
13858                                                 $alias_au = $this->_escape('{'.$k.'}');
13859                                                 if ($this->isunicode) {
13860                                                         $alias_b = $this->_escape($this->UTF8ToLatin1($k));
13861                                                         $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
13862                                                         $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
13863                                                         $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
13864                                                 }
13865                                                 if ($n >= $page) {
13866                                                         $np = $n + $numpages;
13867                                                 } else {
13868                                                         $np = $n;
13869                                                 }
13870                                                 $ns = $this->formatTOCPageNumber($np);
13871                                                 $nu = $ns;
13872                                                 $sdiff = strlen($k) - strlen($ns) - 1;
13873                                                 $sdiffu = strlen($ku) - strlen($ns) - 1;
13874                                                 $sfill = str_repeat($filler, $sdiff);
13875                                                 $sfillu = str_repeat($filler, $sdiffu);
13876                                                 if ($this->rtl) {
13877                                                         $ns = $ns.' '.$sfill;
13878                                                         $nu = $nu.' '.$sfillu;
13879                                                 } else {
13880                                                         $ns = $sfill.' '.$ns;
13881                                                         $nu = $sfillu.' '.$nu;
13882                                                 }
13883                                                 $nu = $this->UTF8ToUTF16BE($nu, false);
13884                                                 $temppage = str_replace($alias_au, $nu, $temppage);
13885                                                 if ($this->isunicode) {
13886                                                         $temppage = str_replace($alias_bu, $nu, $temppage);
13887                                                         $temppage = str_replace($alias_cu, $nu, $temppage);
13888                                                         $temppage = str_replace($alias_b, $ns, $temppage);
13889                                                         $temppage = str_replace($alias_c, $ns, $temppage);
13890                                                 }
13891                                                 $temppage = str_replace($alias_a, $ns, $temppage);
13892                                         }
13893                                         // save changes
13894                                         $this->setPageBuffer($p, $temppage);
13895                                 }
13896                                 // move pages
13897                                 for ($i = 0; $i < $numpages; ++$i) {
13898                                         $this->movePage($page_last, $page);
13899                                 }
13900                         }
13901                         $this->SetFont($fontfamily, $fontstyle, $fontsize);
13902                 }
13903
13904                 /**
13905                 * Stores a copy of the current TCPDF object used for undo operation.
13906                 * @access public
13907                 * @since 4.5.029 (2009-03-19)
13908                 */
13909                 public function startTransaction() {
13910                         if (isset($this->objcopy)) {
13911                                 // remove previous copy
13912                                 $this->commitTransaction();
13913                         }
13914                         // clone current object
13915                         $this->objcopy = $this->objclone($this);
13916                 }
13917
13918                 /**
13919                 * Delete the copy of the current TCPDF object used for undo operation.
13920                 * @access public
13921                 * @since 4.5.029 (2009-03-19)
13922                 */
13923                 public function commitTransaction() {
13924                         if (isset($this->objcopy)) {
13925                                 $this->objcopy->_destroy(true, true);
13926                                 unset($this->objcopy);
13927                         }
13928                 }
13929
13930                 /**
13931                 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
13932                 * @return TCPDF object.
13933                 * @access public
13934                 * @since 4.5.029 (2009-03-19)
13935                 */
13936                 public function rollbackTransaction() {
13937                         if (isset($this->objcopy)) {
13938                                 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
13939                                         // truncate files to previous values
13940                                         foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
13941                                                 $file = substr($file, 1);
13942                                                 $handle = fopen($file, 'r+');
13943                                                 ftruncate($handle, $lenght);
13944                                         }
13945                                 }
13946                                 $this->_destroy(true, true);
13947                                 return $this->objcopy;
13948                         }
13949                         return $this;
13950                 }
13951
13952                 /**
13953                 * Creates a copy of a class object
13954                 * @param object $object class object to be cloned
13955                 * @return cloned object
13956                 * @access public
13957                 * @since 4.5.029 (2009-03-19)
13958                 */
13959                 public function objclone($object) {
13960                         return @clone($object);
13961                 }
13962
13963                 /**
13964                 * Determine whether a string is empty.
13965                 * @param srting $str string to be checked
13966                 * @return boolean true if string is empty
13967                 * @access public
13968                 * @since 4.5.044 (2009-04-16)
13969                 */
13970                 public function empty_string($str) {
13971                         return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
13972                 }
13973                 
13974         } // END OF TCPDF CLASS
13975 }
13976 //============================================================+
13977 // END OF FILE
13978 //============================================================+
13979 ?>