]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/tcpdf/tcpdf.php
Release 6.2.3
[Github/sugarcrm.git] / include / tcpdf / tcpdf.php
1 <?php
2
3 /*
4
5 Modification information for LGPL compliance
6
7 Mon May 30 16:16:13 2011 +0300 - alex-vlasov - Bug 41216 - currency is displayed on 2 lines instead of one when printing a quote to PDF
8
9 r57813 - 2010-08-19 10:34:44 -0700 (Thu, 19 Aug 2010) - kjing - Author: John Mertic <jmertic@sugarcrm.com>
10     Bug 39085 - When loading the opposite search panel via ajax on the ListViews, call the index action instead of the ListView action to avoid touching pre-MVC code by accident.
11
12 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
13
14 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
15
16 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
17
18 r53409 - 2010-01-03 19:31:15 -0800 (Sun, 03 Jan 2010) - roger - merge -r50376:HEAD from fuji_newtag_tmp
19
20 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
21
22 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
23
24 r51443 - 2009-10-12 13:34:36 -0700 (Mon, 12 Oct 2009) - jmertic - Bug 33332 - Made application PHP 5.3 compliant with E_DEPRECATED warnings on by:
25 - Changing all ereg function to either preg or simple string based ones
26 - No more references to magic quotes.
27 - Change all the session_unregister() functions to just unset() the correct session variable instead.
28
29 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
30
31 r47904 - 2009-06-02 11:54:10 -0700 (Tue, 02 Jun 2009) - jenny - Adding changes that were made to the earlier version of this file to support jpg images.
32
33 r47900 - 2009-06-02 11:26:55 -0700 (Tue, 02 Jun 2009) - jenny - Updating with changes from bsoufflet.
34
35 r46662 - 2009-04-29 10:48:07 -0700 (Wed, 29 Apr 2009) - jenny - Invoking native jpg image support which isnt' automatically there.
36
37 r46617 - 2009-04-28 16:08:45 -0700 (Tue, 28 Apr 2009) - jenny - Fixing a bug in TCPDF that checks to see if the GD library is installed.
38
39 r46451 - 2009-04-23 16:57:40 -0700 (Thu, 23 Apr 2009) - jenny - tcpdf initial checkin.
40
41
42 */
43
44
45 //============================================================+
46 // File name   : tcpdf.php
47 // Begin       : 2002-08-03
48 // Last Update : 2009-05-28
49 // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
50 // Version     : 4.6.013
51 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
52 //      ----------------------------------------------------------------------------
53 //  Copyright (C) 2002-2009  Nicola Asuni - Tecnick.com S.r.l.
54 //      
55 //      This program is free software: you can redistribute it and/or modify
56 //      it under the terms of the GNU Lesser General Public License as published by
57 //      the Free Software Foundation, either version 2.1 of the License, or
58 //      (at your option) any later version.
59 //      
60 //      This program is distributed in the hope that it will be useful,
61 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
62 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
63 //      GNU Lesser General Public License for more details.
64 //      
65 //      You should have received a copy of the GNU Lesser General Public License
66 //      along with this program.  If not, see <http://www.gnu.org/licenses/>.
67 //      
68 //      See LICENSE.TXT file for more information.
69 //  ----------------------------------------------------------------------------
70 //
71 // Description : This is a PHP class for generating PDF documents without 
72 //               requiring external extensions.
73 //
74 // NOTE:
75 // This class was originally derived in 2002 from the Public 
76 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org), 
77 // but now is almost entirely rewritten.
78 //
79 // Main features:
80 //  * no external libraries are required for the basic functions;
81 //      * supports all ISO page formats;
82 //      * supports custom page formats, margins and units of measure;
83 //      * supports UTF-8 Unicode and Right-To-Left languages;
84 //      * supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
85 //      * supports document encryption;
86 //      * includes methods to publish some XHTML code;
87 //      * includes graphic (geometric) and transformation methods;
88 //      * includes Javascript and forms support;
89 //      * includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;
90 //      * includes methods to set Bookmarks and print a Table of Content;
91 //      * includes methods to move and delete pages;
92 //      * includes methods for automatic page header and footer management;
93 //      * supports automatic page break;
94 //      * supports automatic page numbering and page groups;
95 //      * supports automatic line break and text justification;
96 //      * supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
97 //      * supports stroke and clipping mode for text;
98 //      * supports clipping masks;
99 //      * supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;
100 //      * supports several annotations, including links, text and file attachments;
101 //      * supports page compression (requires zlib extension);
102 //  * supports text hyphenation.
103 //  * supports transactions to UNDO commands.
104 //
105 // -----------------------------------------------------------
106 // THANKS TO:
107 // 
108 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
109 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
110 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
111 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
112 // dullus for text Justification.
113 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
114 // Patrick Benny for text stretch suggestion on Cell().
115 // Johannes G�ntert for JavaScript support.
116 // Denis Van Nuffelen for Dynamic Form.
117 // Jacek Czekaj for multibyte justification
118 // Anthony Ferrara for the reintroduction of legacy image methods.
119 // Sourceforge user 1707880 (hucste) for line-trough mode.
120 // Larry Stanbery for page groups.
121 // Martin Hall-May for transparency.
122 // Aaron C. Spike for Polycurve method.
123 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
124 // Moritz Wagner and Andreas Wurmser for graphic functions.
125 // Andrew Whitehead for core fonts support.
126 // Esteban Jo�l Mar�n for OpenType font conversion.
127 // Teus Hagen for several suggestions and fixes.
128 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
129 // Kosmas Papachristos for some CSS improvements.
130 // Marcel Partap for some fixes.
131 // Won Kyu Park for several suggestions, fixes and patches.
132 // Anyone that has reported a bug or sent a suggestion.
133 //============================================================+
134
135 /**
136  * This is a PHP class for generating PDF documents without requiring external extensions.<br>
137  * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
138  * <h3>TCPDF main features are:</h3>
139  * <ul>
140 * <li>no external libraries are required for the basic functions;</li>
141 * <li>supports all ISO page formats;</li>
142 * <li>supports custom page formats, margins and units of measure;</li>
143 * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
144 * <li>supports TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>
145 * <li>supports document encryption;</li>
146 * <li>includes methods to publish some XHTML code;</li>
147 * <li>includes graphic (geometric) and transformation methods;</li>
148 * <li>includes Javascript and forms support;</li>
149 * <li>includes a method to print various barcode formats: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS;</li>
150 * <li>includes methods to set Bookmarks and print a Table of Content;</li>
151 * <li>includes methods to move and delete pages;</li>
152 * <li>includes methods for automatic page header and footer management;</li>
153 * <li>supports automatic page break;</li>
154 * <li>supports automatic page numbering and page groups;</li>
155 * <li>supports automatic line break and text justification;</li>
156 * <li>supports JPEG and PNG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li>
157 * <li>supports stroke and clipping mode for text;</li>
158 * <li>supports clipping masks;</li>
159 * <li>supports Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
160 * <li>supports several annotations, including links, text and file attachments;</li>
161 * <li>supports page compression (requires zlib extension);</li>
162 * <li>supports text hyphenation.</li>
163 * <li>supports transactions to UNDO commands.</li>
164  * </ul>
165  * Tools to encode your unicode fonts are on fonts/utils directory.</p>
166  * @package com.tecnick.tcpdf
167  * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
168  * @author Nicola Asuni
169  * @copyright 2002-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
170  * @link http://www.tcpdf.org
171  * @license http://www.gnu.org/copyleft/lesser.html LGPL
172  * @version 4.6.013
173  */
174
175 /**
176  * main configuration file
177  */
178 require_once(dirname(__FILE__).'/config/tcpdf_config.php');
179
180 // includes some support files
181
182 /**
183  * unicode data
184  */
185 require_once(dirname(__FILE__).'/unicode_data.php');
186
187 /**
188  * html colors table
189  */
190 require_once(dirname(__FILE__).'/htmlcolors.php');
191
192 if (!class_exists('TCPDF', false)) {
193         /**
194          * define default PDF document producer
195          */ 
196         define('PDF_PRODUCER', 'TCPDF 4.6.013 (http://www.tcpdf.org)');
197         
198         /**
199         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
200         * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
201         * @name TCPDF
202         * @package com.tecnick.tcpdf
203         * @version 4.6.013
204         * @author Nicola Asuni - info@tecnick.com
205         * @link http://www.tcpdf.org
206         * @license http://www.gnu.org/copyleft/lesser.html LGPL
207         */
208         class TCPDF {
209                 
210                 // protected or Protected properties
211
212                 /**
213                 * @var current page number
214                 * @access protected
215                 */
216                 protected $page;
217                 
218                 /**
219                 * @var current object number
220                 * @access protected
221                 */
222                 protected $n;
223
224                 /**
225                 * @var array of object offsets
226                 * @access protected
227                 */
228                 protected $offsets;
229
230                 /**
231                 * @var buffer holding in-memory PDF
232                 * @access protected
233                 */
234                 protected $buffer;
235
236                 /**
237                 * @var array containing pages
238                 * @access protected
239                 */
240                 protected $pages = array();
241
242                 /**
243                 * @var current document state
244                 * @access protected
245                 */
246                 protected $state;
247
248                 /**
249                 * @var compression flag
250                 * @access protected
251                 */
252                 protected $compress;
253                 
254                 /**
255                 * @var current page orientation (P = Portrait, L = Landscape)
256                 * @access protected
257                 */
258                 protected $CurOrientation;
259
260                 /**
261                 * @var array that stores page dimensions and graphic status.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>
262                 * @access protected
263                 */
264                 protected $pagedim = array();
265
266                 /**
267                 * @var scale factor (number of points in user unit)
268                 * @access protected
269                 */
270                 protected $k;
271
272                 /**
273                 * @var width of page format in points
274                 * @access protected
275                 */
276                 protected $fwPt;
277
278                 /**
279                 * @var height of page format in points
280                 * @access protected
281                 */
282                 protected $fhPt;
283
284                 /**
285                 * @var current width of page in points
286                 * @access protected
287                 */
288                 protected $wPt;
289
290                 /**
291                 * @var current height of page in points
292                 * @access protected
293                 */
294                 protected $hPt;
295
296                 /**
297                 * @var current width of page in user unit
298                 * @access protected
299                 */
300                 protected $w;
301
302                 /**
303                 * @var current height of page in user unit
304                 * @access protected
305                 */
306                 protected $h;
307
308                 /**
309                 * @var left margin
310                 * @access protected
311                 */
312                 protected $lMargin;
313
314                 /**
315                 * @var top margin
316                 * @access protected
317                 */
318                 protected $tMargin;
319
320                 /**
321                 * @var right margin
322                 * @access protected
323                 */
324                 protected $rMargin;
325
326                 /**
327                 * @var page break margin
328                 * @access protected
329                 */
330                 protected $bMargin;
331
332                 /**
333                 * @var cell internal padding
334                 * @access protected
335                 */
336                 //protected
337                 public $cMargin;
338                 
339                 /**
340                 * @var cell internal padding (previous value)
341                 * @access protected
342                 */
343                 protected $oldcMargin;
344
345                 /**
346                 * @var current horizontal position in user unit for cell positioning
347                 * @access protected
348                 */
349                 protected $x;
350
351                 /**
352                 * @var current vertical position in user unit for cell positioning
353                 * @access protected
354                 */
355                 protected $y;
356
357                 /**
358                 * @var height of last cell printed
359                 * @access protected
360                 */
361                 protected $lasth;
362
363                 /**
364                 * @var line width in user unit
365                 * @access protected
366                 */
367                 protected $LineWidth;
368
369                 /**
370                 * @var array of standard font names
371                 * @access protected
372                 */
373                 protected $CoreFonts;
374
375                 /**
376                 * @var array of used fonts
377                 * @access protected
378                 */
379                 protected $fonts = array();
380
381                 /**
382                 * @var array of font files
383                 * @access protected
384                 */
385                 protected $FontFiles = array();
386
387                 /**
388                 * @var array of encoding differences
389                 * @access protected
390                 */
391                 protected $diffs = array();
392
393                 /**
394                 * @var array of used images
395                 * @access protected
396                 */
397                 protected $images = array();
398
399                 /**
400                 * @var array of Annotations in pages
401                 * @access protected
402                 */
403                 protected $PageAnnots = array();
404
405                 /**
406                 * @var array of internal links
407                 * @access protected
408                 */
409                 protected $links = array();
410
411                 /**
412                 * @var current font family
413                 * @access protected
414                 */
415                 protected $FontFamily;
416
417                 /**
418                 * @var current font style
419                 * @access protected
420                 */
421                 protected $FontStyle;
422                 
423                 /**
424                 * @var current font ascent (distance between font top and baseline)
425                 * @access protected
426                 * @since 2.8.000 (2007-03-29)
427                 */
428                 protected $FontAscent;
429                 
430                 /**
431                 * @var current font descent (distance between font bottom and baseline)
432                 * @access protected
433                 * @since 2.8.000 (2007-03-29)
434                 */
435                 protected $FontDescent;
436
437                 /**
438                 * @var underlining flag
439                 * @access protected
440                 */
441                 protected $underline;
442
443                 /**
444                 * @var current font info
445                 * @access protected
446                 */
447                 protected $CurrentFont;
448
449                 /**
450                 * @var current font size in points
451                 * @access protected
452                 */
453                 protected $FontSizePt;
454
455                 /**
456                 * @var current font size in user unit
457                 * @access protected
458                 */
459                 protected $FontSize;
460
461                 /**
462                 * @var commands for drawing color
463                 * @access protected
464                 */
465                 protected $DrawColor;
466
467                 /**
468                 * @var commands for filling color
469                 * @access protected
470                 */
471                 protected $FillColor;
472
473                 /**
474                 * @var commands for text color
475                 * @access protected
476                 */
477                 protected $TextColor;
478
479                 /**
480                 * @var indicates whether fill and text colors are different
481                 * @access protected
482                 */
483                 protected $ColorFlag;
484
485                 /**
486                 * @var automatic page breaking
487                 * @access protected
488                 */
489                 protected $AutoPageBreak;
490
491                 /**
492                 * @var threshold used to trigger page breaks
493                 * @access protected
494                 */
495                 protected $PageBreakTrigger;
496
497                 /**
498                 * @var flag set when processing footer
499                 * @access protected
500                 */
501                 protected $InFooter = false;
502
503                 /**
504                 * @var zoom display mode
505                 * @access protected
506                 */
507                 protected $ZoomMode;
508
509                 /**
510                 * @var layout display mode
511                 * @access protected
512                 */
513                 protected $LayoutMode;
514
515                 /**
516                 * @var title
517                 * @access protected
518                 */
519                 protected $title = '';
520
521                 /**
522                 * @var subject
523                 * @access protected
524                 */
525                 protected $subject = '';
526
527                 /**
528                 * @var author
529                 * @access protected
530                 */
531                 protected $author = '';
532
533                 /**
534                 * @var keywords
535                 * @access protected
536                 */
537                 protected $keywords = '';
538
539                 /**
540                 * @var creator
541                 * @access protected
542                 */
543                 protected $creator = '';
544
545                 /**
546                 * @var alias for total number of pages
547                 * @access protected
548                 */
549                 protected $AliasNbPages = '{nb}';
550                 
551                 /**
552                 * @var alias for page number
553                 * @access protected
554                 */
555                 protected $AliasNumPage = '{pnb}';
556                 
557                 /**
558                 * @var right-bottom corner X coordinate of inserted image
559                 * @since 2002-07-31
560                 * @author Nicola Asuni
561                 * @access protected
562                 */
563                 protected $img_rb_x;
564
565                 /**
566                 * @var right-bottom corner Y coordinate of inserted image
567                 * @since 2002-07-31
568                 * @author Nicola Asuni
569                 * @access protected
570                 */
571                 protected $img_rb_y;
572
573                 /**
574                 * @var adjusting factor to convert pixels to user units.
575                 * @since 2004-06-14
576                 * @author Nicola Asuni
577                 * @access protected
578                 */
579                 protected $imgscale = 1;
580
581                 /**
582                 * @var boolean set to true when the input text is unicode (require unicode fonts)
583                 * @since 2005-01-02
584                 * @author Nicola Asuni
585                 * @access protected
586                 */
587                 protected $isunicode = false;
588
589                 /**
590                 * @var PDF version
591                 * @since 1.5.3
592                 * @access protected
593                 */
594                 protected $PDFVersion = '1.7';
595                 
596                 
597                 // ----------------------
598                 
599                 /**
600                  * @var Minimum distance between header and top page margin.
601                  * @access protected
602                  */
603                 protected $header_margin;
604                 
605                 /**
606                  * @var Minimum distance between footer and bottom page margin.
607                  * @access protected
608                  */
609                 protected $footer_margin;
610                 
611                 /**
612                  * @var original left margin value
613                  * @access protected
614                  * @since 1.53.0.TC013
615                  */
616                 protected $original_lMargin;
617                 
618                 /**
619                  * @var original right margin value
620                  * @access protected
621                  * @since 1.53.0.TC013
622                  */
623                 protected $original_rMargin;
624                         
625                 /**
626                  * @var Header font.
627                  * @access protected
628                  */
629                 protected $header_font;
630                 
631                 /**
632                  * @var Footer font.
633                  * @access protected
634                  */
635                 protected $footer_font;
636                 
637                 /**
638                  * @var Language templates.
639                  * @access protected
640                  */
641                 protected $l;
642                 
643                 /**
644                  * @var Barcode to print on page footer (only if set).
645                  * @access protected
646                  */
647                 protected $barcode = false;
648                 
649                 /**
650                  * @var If true prints header
651                  * @access protected
652                  */
653                 protected $print_header = true;
654                 
655                 /**
656                  * @var If true prints footer.
657                  * @access protected
658                  */
659                 protected $print_footer = true;
660                         
661                 /**
662                  * @var Header image logo.
663                  * @access protected
664                  */
665                 protected $header_logo = '';
666                 
667                 /**
668                  * @var Header image logo width in mm.
669                  * @access protected
670                  */
671                 protected $header_logo_width = 30;
672                 
673                 /**
674                  * @var String to print as title on document header.
675                  * @access protected
676                  */
677                 protected $header_title = '';
678                 
679                 /**
680                  * @var String to print on document header.
681                  * @access protected
682                  */
683                 protected $header_string = '';
684                 
685                 /**
686                  * @var Default number of columns for html table.
687                  * @access protected
688                  */
689                 protected $default_table_columns = 4;
690                 
691                 
692                 // variables for html parser
693                 
694                 /**
695                  * @var HTML PARSER: array to store current link and rendering styles.
696                  * @access protected
697                  */
698                 protected $HREF = array();
699                 
700                 /**
701                  * @var store a list of available fonts on filesystem.
702                  * @access protected
703                  */
704                 protected $fontlist = array();
705                 
706                 /**
707                  * @var current foreground color
708                  * @access protected
709                  */
710                 protected $fgcolor;
711                                                 
712                 /**
713                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
714                  * @access protected
715                  */
716                 protected $listordered = array();
717                 
718                 /**
719                  * @var HTML PARSER: array count list items on nested lists.
720                  * @access protected
721                  */
722                 protected $listcount = array();
723                 
724                 /**
725                  * @var HTML PARSER: current list nesting level.
726                  * @access protected
727                  */
728                 protected $listnum = 0;
729                 
730                 /**
731                  * @var HTML PARSER: indent amount for lists.
732                  * @access protected
733                  */
734                 protected $listindent;
735                 
736                 /**
737                  * @var current background color
738                  * @access protected
739                  */
740                 protected $bgcolor;
741                 
742                 /**
743                  * @var Store temporary font size in points.
744                  * @access protected
745                  */
746                 protected $tempfontsize = 10;
747                 
748                 /**
749                  * @var spacer for LI tags.
750                  * @access protected
751                  */
752                 protected $lispacer = '';
753                 
754                 /**
755                  * @var default encoding
756                  * @access protected
757                  * @since 1.53.0.TC010
758                  */
759                 protected $encoding = 'UTF-8';
760                 
761                 /**
762                  * @var PHP internal encoding
763                  * @access protected
764                  * @since 1.53.0.TC016
765                  */
766                 protected $internal_encoding;
767                 
768                 /**
769                  * @var indicates if the document language is Right-To-Left
770                  * @access protected
771                  * @since 2.0.000
772                  */
773                 protected $rtl = false;
774                 
775                 /**
776                  * @var used to force RTL or LTR string inversion
777                  * @access protected
778                  * @since 2.0.000
779                  */
780                 protected $tmprtl = false;
781                 
782                 // --- Variables used for document encryption:
783                 
784                 /**
785                  * Indicates whether document is protected
786                  * @access protected
787                  * @since 2.0.000 (2008-01-02)
788                  */
789                 protected $encrypted;
790                 
791                 /**
792                  * U entry in pdf document
793                  * @access protected
794                  * @since 2.0.000 (2008-01-02)
795                  */
796                 protected $Uvalue;
797                 
798                 /**
799                  * O entry in pdf document
800                  * @access protected
801                  * @since 2.0.000 (2008-01-02)
802                  */
803                 protected $Ovalue;
804                 
805                 /**
806                  * P entry in pdf document
807                  * @access protected
808                  * @since 2.0.000 (2008-01-02)
809                  */
810                 protected $Pvalue;
811                 
812                 /**
813                  * encryption object id
814                  * @access protected
815                  * @since 2.0.000 (2008-01-02)
816                  */
817                 protected $enc_obj_id;
818                 
819                 /**
820                  * last RC4 key encrypted (cached for optimisation)
821                  * @access protected
822                  * @since 2.0.000 (2008-01-02)
823                  */
824                 protected $last_rc4_key;
825                 
826                 /**
827                  * last RC4 computed key
828                  * @access protected
829                  * @since 2.0.000 (2008-01-02)
830                  */
831                 protected $last_rc4_key_c;
832                 
833                 /**
834                  * RC4 padding
835                  * @access protected
836                  */
837                 protected $padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
838                 
839                 /**
840                  * RC4 encryption key
841                  * @access protected
842                  */
843                 protected $encryption_key;
844                 
845                 // --- bookmark ---
846                 
847                 /**
848                  * Outlines for bookmark
849                  * @access protected
850                  * @since 2.1.002 (2008-02-12)
851                  */
852                 protected $outlines = array();
853                 
854                 /**
855                  * Outline root for bookmark
856                  * @access protected
857                  * @since 2.1.002 (2008-02-12)
858                  */
859                 protected $OutlineRoot;
860                 
861                 
862                 // --- javascript and form ---
863                 
864                 /**
865                  * javascript code
866                  * @access protected
867                  * @since 2.1.002 (2008-02-12)
868                  */
869                 protected $javascript = '';
870                 
871                 /**
872                  * javascript counter
873                  * @access protected
874                  * @since 2.1.002 (2008-02-12)
875                  */
876                 protected $n_js;
877
878                 /**
879                  * line trough state
880                  * @access protected
881                  * @since 2.8.000 (2008-03-19)
882                  */
883                 protected $linethrough;
884
885                 // --- Variables used for User's Rights ---
886                 // See PDF reference chapter 8.7 Digital Signatures
887
888                 /**
889                  * If true enables user's rights on PDF reader
890                  * @access protected
891                  * @since 2.9.000 (2008-03-26)
892                  */
893                 protected $ur;
894
895                 /**
896                  * Names specifying additional document-wide usage rights for the document.
897                  * @access protected
898                  * @since 2.9.000 (2008-03-26)
899                  */
900                 protected $ur_document;
901
902                 /**
903                  * Names specifying additional annotation-related usage rights for the document.
904                  * @access protected
905                  * @since 2.9.000 (2008-03-26)
906                  */
907                 protected $ur_annots;
908
909                 /**
910                  * Names specifying additional form-field-related usage rights for the document.
911                  * @access protected
912                  * @since 2.9.000 (2008-03-26)
913                  */
914                 protected $ur_form;
915
916                 /**
917                  * Names specifying additional signature-related usage rights for the document.
918                  * @access protected
919                  * @since 2.9.000 (2008-03-26)
920                  */
921                 protected $ur_signature;
922
923                 /**
924                  * Dot Per Inch Document Resolution (do not change)
925                  * @access protected
926                  * @since 3.0.000 (2008-03-27)
927                  */
928                 protected $dpi = 72;
929                 
930                 /**
931                  * Array of page numbers were a new page group was started
932                  * @access protected
933                  * @since 3.0.000 (2008-03-27)
934                  */
935                 protected $newpagegroup = array();
936                 
937                 /**
938                  * Contains the number of pages of the groups
939                  * @access protected
940                  * @since 3.0.000 (2008-03-27)
941                  */
942                 protected $pagegroups;
943                 
944                 /**
945                  * Contains the alias of the current page group
946                  * @access protected
947                  * @since 3.0.000 (2008-03-27)
948                  */
949                 protected $currpagegroup; 
950                 
951                 /**
952                  * Restrict the rendering of some elements to screen or printout.
953                  * @access protected
954                  * @since 3.0.000 (2008-03-27)
955                  */
956                 protected $visibility = 'all';
957                 
958                 /**
959                  * Print visibility.
960                  * @access protected
961                  * @since 3.0.000 (2008-03-27)
962                  */
963                 protected $n_ocg_print;
964                 
965                 /**
966                  * View visibility.
967                  * @access protected
968                  * @since 3.0.000 (2008-03-27)
969                  */
970                 protected $n_ocg_view;
971                 
972                 /**
973                  * Array of transparency objects and parameters.
974                  * @access protected
975                  * @since 3.0.000 (2008-03-27)
976                  */
977                 protected $extgstates;
978                 
979                 /**
980                  * Set the default JPEG compression quality (1-100)
981                  * @access protected
982                  * @since 3.0.000 (2008-03-27)
983                  */
984                 protected $jpeg_quality;
985                 
986                 /**
987                  * Default cell height ratio.
988                  * @access protected
989                  * @since 3.0.014 (2008-05-23)
990                  */
991                 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
992                 
993                 /**
994                  * PDF viewer preferences.
995                  * @access protected
996                  * @since 3.1.000 (2008-06-09)
997                  */
998                 protected $viewer_preferences;
999                 
1000                 /**
1001                  * A name object specifying how the document should be displayed when opened.
1002                  * @access protected
1003                  * @since 3.1.000 (2008-06-09)
1004                  */
1005                 protected $PageMode;
1006                 
1007                 /**
1008                  * Array for storing gradient information.
1009                  * @access protected
1010                  * @since 3.1.000 (2008-06-09)
1011                  */
1012                 protected $gradients = array();
1013                 
1014                 /**
1015                  * Array used to store positions inside the pages buffer.
1016                  * keys are the page numbers
1017                  * @access protected
1018                  * @since 3.2.000 (2008-06-26)
1019                  */
1020                 protected $intmrk = array();
1021                 
1022                 /**
1023                  * Array used to store footer positions of each page.
1024                  * @access protected
1025                  * @since 3.2.000 (2008-07-01)
1026                  */
1027                 protected $footerpos = array();
1028                 
1029                 
1030                 /**
1031                  * Array used to store footer lenght of each page.
1032                  * @access protected
1033                  * @since 4.0.014 (2008-07-29)
1034                  */
1035                 protected $footerlen = array();
1036                 
1037                 /**
1038                  * True if a newline is created.
1039                  * @access protected
1040                  * @since 3.2.000 (2008-07-01)
1041                  */
1042                 protected $newline = true;
1043                 
1044                 /**
1045                  * End position of the latest inserted line
1046                  * @access protected
1047                  * @since 3.2.000 (2008-07-01)
1048                  */
1049                 protected $endlinex = 0;
1050                 
1051                 /**
1052                  * PDF string for last line width
1053                  * @access protected
1054                  * @since 4.0.006 (2008-07-16)
1055                  */
1056                 protected $linestyleWidth = '';
1057                 
1058                 /**
1059                  * PDF string for last line width
1060                  * @access protected
1061                  * @since 4.0.006 (2008-07-16)
1062                  */
1063                 protected $linestyleCap = '0 J';
1064                 
1065                 /**
1066                  * PDF string for last line width
1067                  * @access protected
1068                  * @since 4.0.006 (2008-07-16)
1069                  */
1070                 protected $linestyleJoin = '0 j';
1071                 
1072                 /**
1073                  * PDF string for last line width
1074                  * @access protected
1075                  * @since 4.0.006 (2008-07-16)
1076                  */
1077                 protected $linestyleDash = '[] 0 d';
1078                 
1079                 /**
1080                  * True if marked-content sequence is open
1081                  * @access protected
1082                  * @since 4.0.013 (2008-07-28)
1083                  */
1084                 protected $openMarkedContent = false;
1085                 
1086                 /**
1087                  * Count the latest inserted vertical spaces on HTML
1088                  * @access protected
1089                  * @since 4.0.021 (2008-08-24)
1090                  */
1091                 protected $htmlvspace = 0;
1092                 
1093                 /**
1094                  * Array of Spot colors
1095                  * @access protected
1096                  * @since 4.0.024 (2008-09-12)
1097                  */
1098                 protected $spot_colors = array();
1099                 
1100                 /**
1101                  * Symbol used for HTML unordered list items
1102                  * @access protected
1103                  * @since 4.0.028 (2008-09-26)
1104                  */
1105                 protected $lisymbol = '';
1106                 
1107                 /**
1108                  * String used to mark the beginning and end of EPS image blocks
1109                  * @access protected
1110                  * @since 4.1.000 (2008-10-18)
1111                  */
1112                 protected $epsmarker = 'x#!#EPS#!#x';
1113                 
1114                 /**
1115                  * Array of transformation matrix
1116                  * @access protected
1117                  * @since 4.2.000 (2008-10-29)
1118                  */
1119                 protected $transfmatrix = array();
1120                 
1121                 /**
1122                  * Booklet mode for double-sided pages
1123                  * @access protected
1124                  * @since 4.2.000 (2008-10-29)
1125                  */
1126                 protected $booklet = false;
1127                 
1128                 /**
1129                  * Epsilon value used for float calculations
1130                  * @access protected
1131                  * @since 4.2.000 (2008-10-29)
1132                  */
1133                 protected $feps = 0.001;
1134                 
1135                 /**
1136                  * Array used for custom vertical spaces for HTML tags
1137                  * @access protected
1138                  * @since 4.2.001 (2008-10-30)
1139                  */
1140                 protected $tagvspaces = array();
1141                 
1142                 /**
1143                  * @var HTML PARSER: custom indent amount for lists.
1144                  * Negative value means disabled.
1145                  * @access protected
1146                  * @since 4.2.007 (2008-11-12)
1147                  */
1148                 protected $customlistindent = -1;
1149                 
1150                 /**
1151                  * @var if true keeps the border open for the cell sides that cross the page.
1152                  * @access protected
1153                  * @since 4.2.010 (2008-11-14)
1154                  */
1155                 protected $opencell = true;
1156
1157                 /**
1158                  * @var array of files to embedd
1159                  * @access protected
1160                  * @since 4.4.000 (2008-12-07)
1161                  */
1162                 protected $embeddedfiles = array();
1163
1164                 /**
1165                  * @var boolean true when inside html pre tag
1166                  * @access protected
1167                  * @since 4.4.001 (2008-12-08)
1168                  */
1169                 protected $premode = false;
1170
1171                 /**
1172                  * Array used to store positions of graphics transformation blocks inside the page buffer.
1173                  * keys are the page numbers
1174                  * @access protected
1175                  * @since 4.4.002 (2008-12-09)
1176                  */
1177                 protected $transfmrk = array();
1178
1179                 /**
1180                  * Default color for html links
1181                  * @access protected
1182                  * @since 4.4.003 (2008-12-09)
1183                  */
1184                 protected $htmlLinkColorArray = array(0, 0, 255);
1185
1186                 /**
1187                  * Default font style to add to html links
1188                  * @access protected
1189                  * @since 4.4.003 (2008-12-09)
1190                  */
1191                 protected $htmlLinkFontStyle = 'U';
1192
1193                 /**
1194                  * Counts the number of pages.
1195                  * @access protected
1196                  * @since 4.5.000 (2008-12-31)
1197                  */
1198                 protected $numpages = 0;
1199
1200                 /**
1201                  * Array containing page lenghts in bytes.
1202                  * @access protected
1203                  * @since 4.5.000 (2008-12-31)
1204                  */
1205                 protected $pagelen = array();
1206
1207                 /**
1208                  * Counts the number of pages.
1209                  * @access protected
1210                  * @since 4.5.000 (2008-12-31)
1211                  */
1212                 protected $numimages = 0;
1213
1214                 /**
1215                  * Store the image keys.
1216                  * @access protected
1217                  * @since 4.5.000 (2008-12-31)
1218                  */
1219                 protected $imagekeys = array();
1220
1221                 /**
1222                  * Lenght of the buffer in bytes.
1223                  * @access protected
1224                  * @since 4.5.000 (2008-12-31)
1225                  */
1226                 protected $bufferlen = 0;
1227
1228                 /**
1229                  * If true enables disk caching.
1230                  * @access protected
1231                  * @since 4.5.000 (2008-12-31)
1232                  */
1233                 protected $diskcache = false;
1234
1235                 /**
1236                  * Counts the number of fonts.
1237                  * @access protected
1238                  * @since 4.5.000 (2009-01-02)
1239                  */
1240                 protected $numfonts = 0;
1241
1242                 /**
1243                  * Store the font keys.
1244                  * @access protected
1245                  * @since 4.5.000 (2009-01-02)
1246                  */
1247                 protected $fontkeys = array();
1248
1249                 /**
1250                  * Store the fage status (true when opened, false when closed).
1251                  * @access protected
1252                  * @since 4.5.000 (2009-01-02)
1253                  */
1254                 protected $pageopen = array();
1255                 
1256                 /**
1257                  * Default monospaced font
1258                  * @access protected
1259                  * @since 4.5.025 (2009-03-10)
1260                  */
1261                 protected $default_monospaced_font = 'courier';
1262
1263                 /**
1264                  * Used to store a cloned copy of the current class object
1265                  * @access protected
1266                  * @since 4.5.029 (2009-03-19)
1267                  */
1268                 protected $objcopy;
1269
1270                 /**
1271                  * Array used to store the lenghts of cache files
1272                  * @access protected
1273                  * @since 4.5.029 (2009-03-19)
1274                  */
1275                 protected $cache_file_lenght = array();
1276
1277                 /**
1278                  * Table header content to be repeated on each new page
1279                  * @access protected
1280                  * @since 4.5.030 (2009-03-20)
1281                  */
1282                 protected $thead = '';
1283
1284                 /**
1285                  * Distance between the top of page and end of table headers on a new page.
1286                  * @access protected
1287                  * @since 4.5.030 (2009-03-20)
1288                  */
1289                 protected $theadMargin = '';
1290
1291                 /**
1292                  * Cache array for UTF8StringToArray() method.
1293                  * @access protected
1294                  * @since 4.5.037 (2009-04-07)
1295                  */
1296                 protected $cache_UTF8StringToArray = array();
1297
1298                 /**
1299                  * Maximum size of cache array used for UTF8StringToArray() method.
1300                  * @access protected
1301                  * @since 4.5.037 (2009-04-07)
1302                  */
1303                 protected $cache_maxsize_UTF8StringToArray = 8;
1304
1305                 /**
1306                  * Current size of cache array used for UTF8StringToArray() method.
1307                  * @access protected
1308                  * @since 4.5.037 (2009-04-07)
1309                  */
1310                 protected $cache_size_UTF8StringToArray = 0;
1311
1312                 /**
1313                  * If true enables document signing
1314                  * @access protected
1315                  * @since 4.6.005 (2009-04-24)
1316                  */
1317                 protected $sign = false;
1318
1319                 /**
1320                  * Signature data
1321                  * @access protected
1322                  * @since 4.6.005 (2009-04-24)
1323                  */
1324                 protected $signature_data = array();
1325
1326                 /**
1327                  * Signature max lenght
1328                  * @access protected
1329                  * @since 4.6.005 (2009-04-24)
1330                  */
1331                 protected $signature_max_lenght = 5120;
1332
1333                 /**
1334                  * Regular expression used to find blank characters used for word-wrapping.
1335                  * @access protected
1336                  * @since 4.6.006 (2009-04-28)
1337                  */
1338                 protected $re_spaces = '/[\s\p{Z}\p{Lo}]/';
1339
1340                 //------------------------------------------------------------
1341                 // METHODS
1342                 //------------------------------------------------------------
1343
1344                 /**
1345                  * This is the class constructor. 
1346                  * It allows to set up the page format, the orientation and 
1347                  * the measure unit used in all the methods (except for the font sizes).
1348                  * @since 1.0
1349                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1350                  * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1351                  * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1352                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1353                  * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1354                  * @param String $encoding charset encoding; default is UTF-8
1355                  * @access public
1356                  */
1357                 public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1358                         /* Set internal character encoding to ASCII */
1359                         if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1360                                 $this->internal_encoding = mb_internal_encoding();
1361                                 mb_internal_encoding('ASCII');
1362                         }
1363                         // set disk caching
1364                         $this->diskcache = $diskcache ? true : false;
1365                         // set language direction
1366                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1367                         $this->tmprtl = false;
1368                         //Some checks
1369                         $this->_dochecks();
1370                         //Initialization of properties
1371                         $this->isunicode = $unicode;
1372                         $this->page = 0;
1373                         $this->transfmrk[0] = array();
1374                         $this->pagedim = array();
1375                         $this->n = 2;
1376                         $this->buffer = '';
1377                         $this->pages = array();
1378                         $this->state = 0;
1379                         $this->fonts = array();
1380                         $this->FontFiles = array();
1381                         $this->diffs = array();
1382                         $this->images = array();
1383                         $this->links = array();
1384                         $this->gradients = array();
1385                         $this->InFooter = false;
1386                         $this->lasth = 0;
1387                         $this->FontFamily = 'helvetica';
1388                         $this->FontStyle = '';
1389                         $this->FontSizePt = 12;
1390                         $this->underline = false;
1391                         $this->linethrough = false;
1392                         $this->DrawColor = '0 G';
1393                         $this->FillColor = '0 g';
1394                         $this->TextColor = '0 g';
1395                         $this->ColorFlag = false;
1396                         // encryption values
1397                         $this->encrypted = false;
1398                         $this->last_rc4_key = '';
1399                         $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
1400                         //Standard Unicode fonts
1401                         $this->CoreFonts = array(
1402                                 'courier'=>'Courier',
1403                                 'courierB'=>'Courier-Bold',
1404                                 'courierI'=>'Courier-Oblique',
1405                                 'courierBI'=>'Courier-BoldOblique',
1406                                 'helvetica'=>'Helvetica',
1407                                 'helveticaB'=>'Helvetica-Bold',
1408                                 'helveticaI'=>'Helvetica-Oblique',
1409                                 'helveticaBI'=>'Helvetica-BoldOblique',
1410                                 'times'=>'Times-Roman',
1411                                 'timesB'=>'Times-Bold',
1412                                 'timesI'=>'Times-Italic',
1413                                 'timesBI'=>'Times-BoldItalic',
1414                                 'symbol'=>'Symbol',
1415                                 'zapfdingbats'=>'ZapfDingbats'
1416                         );
1417                         //Set scale factor
1418                         $this->setPageUnit($unit);
1419                         // set page format and orientation
1420                         $this->setPageFormat($format, $orientation);
1421                         //Page margins (1 cm)
1422                         $margin = 28.35 / $this->k;
1423                         $this->SetMargins($margin, $margin);
1424                         //Interior cell margin
1425                         $this->cMargin = $margin / 10;
1426                         //Line width (0.2 mm)
1427                         $this->LineWidth = 0.57 / $this->k;
1428                         $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1429                         $this->linestyleCap = '0 J';
1430                         $this->linestyleJoin = '0 j';
1431                         $this->linestyleDash = '[] 0 d';
1432                         //Automatic page break
1433                         $this->SetAutoPageBreak(true, (2 * $margin));
1434                         //Full width display mode
1435                         $this->SetDisplayMode('fullwidth');
1436                         //Compression
1437                         $this->SetCompression(true);
1438                         //Set default PDF version number
1439                         $this->PDFVersion = '1.7';
1440                         $this->encoding = $encoding;
1441                         $this->HREF = array();
1442                         $this->getFontsList();
1443                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1444                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1445                         $this->extgstates = array();
1446                         // user's rights
1447                         $this->sign = false;
1448                         $this->ur = false;
1449                         $this->ur_document = '/FullSave';
1450                         $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
1451                         $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1452                         $this->ur_signature = '/Modify';                        
1453                         // set default JPEG quality
1454                         $this->jpeg_quality = 75;
1455                         // initialize some settings
1456                         $this->utf8Bidi(array(''), '');
1457                         // set default font
1458                         $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1459                         // check if PCRE Unicode support is enabled
1460                         if (@preg_match('/\pL/u', 'a') == 1) {
1461                                 // PCRE unicode support is turned ON
1462                                 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1463                                 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1464                                 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1465                                 $this->re_spaces = '/[\s\p{Z}\p{Lo}]/';
1466                         } else {
1467                                 // PCRE unicode support is turned OFF
1468                                 $this->re_spaces = '/[\s]/';
1469                         }
1470                 }
1471                 
1472                 /**
1473                  * Default destructor.
1474                  * @access public
1475                  * @since 1.53.0.TC016
1476                  */
1477                 public function __destruct() {
1478                         // restore internal encoding
1479                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1480                                 mb_internal_encoding($this->internal_encoding);
1481                         }
1482                         // unset all class variables
1483                         $this->_destroy(true);
1484                 }
1485                 
1486                 /**
1487                  * Set the units of measure for the document.
1488                  * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1489                  * @access public
1490                  * @since 3.0.015 (2008-06-06)
1491                  */
1492                 public function setPageUnit($unit) {
1493                 //Set scale factor
1494                         switch (strtolower($unit)) {
1495                                 // points
1496                                 case 'px':
1497                                 case 'pt': {
1498                                         $this->k = 1;
1499                                         break;
1500                                 }
1501                                 // millimeters
1502                                 case 'mm': {
1503                                         $this->k = $this->dpi / 25.4;
1504                                         break;
1505                                 }
1506                                 // centimeters
1507                                 case 'cm': {
1508                                         $this->k = $this->dpi / 2.54;
1509                                         break;
1510                                 }
1511                                 // inches
1512                                 case 'in': {
1513                                         $this->k = $this->dpi;
1514                                         break;
1515                                 }
1516                                 // unsupported unit
1517                                 default : {
1518                                         $this->Error('Incorrect unit: '.$unit);
1519                                         break;
1520                                 }
1521                         }
1522                         if (isset($this->CurOrientation)) {
1523                                         $this->setPageOrientation($this->CurOrientation);
1524                         }
1525                 }
1526                 
1527                 /**
1528                 * Set the page format
1529                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1530                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1531                 * @access public
1532                 * @since 3.0.015 (2008-06-06)
1533                 */
1534                 public function setPageFormat($format, $orientation='P') {
1535                         //Page format
1536                         if (is_string($format)) {
1537                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1538                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1539                                 switch (strtoupper($format)) {
1540                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1541                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1542                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1543                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1544                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1545                                         case 'A3': {$format = array(841.89,1190.55); break;}
1546                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1547                                         case 'A5': {$format = array(419.53,595.28); break;}
1548                                         case 'A6': {$format = array(297.64,419.53); break;}
1549                                         case 'A7': {$format = array(209.76,297.64); break;}
1550                                         case 'A8': {$format = array(147.40,209.76); break;}
1551                                         case 'A9': {$format = array(104.88,147.40); break;}
1552                                         case 'A10': {$format = array(73.70,104.88); break;}
1553                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1554                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1555                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1556                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1557                                         case 'B4': {$format = array(708.66,1000.63); break;}
1558                                         case 'B5': {$format = array(498.90,708.66); break;}
1559                                         case 'B6': {$format = array(354.33,498.90); break;}
1560                                         case 'B7': {$format = array(249.45,354.33); break;}
1561                                         case 'B8': {$format = array(175.75,249.45); break;}
1562                                         case 'B9': {$format = array(124.72,175.75); break;}
1563                                         case 'B10': {$format = array(87.87,124.72); break;}
1564                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1565                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1566                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1567                                         case 'C3': {$format = array(918.43,1298.27); break;}
1568                                         case 'C4': {$format = array(649.13,918.43); break;}
1569                                         case 'C5': {$format = array(459.21,649.13); break;}
1570                                         case 'C6': {$format = array(323.15,459.21); break;}
1571                                         case 'C7': {$format = array(229.61,323.15); break;}
1572                                         case 'C8': {$format = array(161.57,229.61); break;}
1573                                         case 'C9': {$format = array(113.39,161.57); break;}
1574                                         case 'C10': {$format = array(79.37,113.39); break;}
1575                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1576                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1577                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1578                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1579                                         case 'RA4': {$format = array(609.45,864.57); break;}
1580                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1581                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1582                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1583                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1584                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1585                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1586                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1587                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1588                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1589                                 }
1590                                 $this->fwPt = $format[0];
1591                                 $this->fhPt = $format[1];
1592                         } else {
1593                                 $this->fwPt = $format[0] * $this->k;
1594                                 $this->fhPt = $format[1] * $this->k;
1595                         }
1596                         $this->setPageOrientation($orientation);
1597                 }
1598                 
1599                 /**
1600                 * Set page orientation.
1601                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1602                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1603                 * @param float $bottommargin bottom margin of the page.
1604                 * @access public
1605                 * @since 3.0.015 (2008-06-06)
1606                 */
1607                 public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1608                         $orientation = strtoupper($orientation);
1609                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1610                                 $this->CurOrientation = 'P';
1611                                 $this->wPt = $this->fwPt;
1612                                 $this->hPt = $this->fhPt;
1613                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1614                                 $this->CurOrientation = 'L';
1615                                 $this->wPt = $this->fhPt;
1616                                 $this->hPt = $this->fwPt;
1617                         } else {
1618                                 $this->Error('Incorrect orientation: '.$orientation);
1619                         }
1620                         $this->w = $this->wPt / $this->k;
1621                         $this->h = $this->hPt / $this->k;
1622                         if ($this->empty_string($autopagebreak)) {
1623                                 if (isset($this->AutoPageBreak)) {
1624                                         $autopagebreak = $this->AutoPageBreak;
1625                                 } else {
1626                                         $autopagebreak = true;
1627                                 }
1628                         }
1629                         if ($this->empty_string($bottommargin)) {
1630                                 if (isset($this->bMargin)) {
1631                                         $bottommargin = $this->bMargin;
1632                                 } else {
1633                                         // default value = 2 cm
1634                                         $bottommargin = 2 * 28.35 / $this->k;
1635                                 }
1636                         }
1637                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1638                         // store page dimensions
1639                         $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
1640                 }
1641                                 
1642                 /**
1643                  * Enable or disable Right-To-Left language mode
1644                  * @param Boolean $enable if true enable Right-To-Left language mode.
1645                  * @access public
1646                 * @since 2.0.000 (2008-01-03)
1647                  */
1648                 public function setRTL($enable) {
1649                         $this->rtl = $enable ? true : false;
1650                         $this->tmprtl = false;
1651                 }
1652                 
1653                 /**
1654                  * Return the RTL status
1655                  * @return boolean
1656                  * @access public
1657                  * @since 4.0.012 (2008-07-24)
1658                  */
1659                 public function getRTL() {
1660                         return $this->rtl;
1661                 }
1662                 
1663                 /**
1664                 * Force temporary RTL language direction
1665                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1666                 * @access public
1667                 * @since 2.1.000 (2008-01-09)
1668                 */
1669                 public function setTempRTL($mode) {
1670                         switch ($mode) {
1671                                 case false:
1672                                 case 'L':
1673                                 case 'R': {
1674                                         $this->tmprtl = $mode;
1675                                 }
1676                         }
1677                 }
1678                 
1679                 /**
1680                 * Set the last cell height.
1681                 * @param float $h cell height.
1682                 * @author Nicola Asuni
1683                 * @access public
1684                 * @since 1.53.0.TC034
1685                 */
1686                 public function setLastH($h) {
1687                         $this->lasth = $h;
1688                 }
1689                 
1690                 /**
1691                 * Get the last cell height.
1692                 * @return last cell height
1693                 * @access public
1694                 * @since 4.0.017 (2008-08-05)
1695                 */
1696                 public function getLastH() {
1697                         return $this->lasth;
1698                 }
1699                 
1700                 /**
1701                 * Set the adjusting factor to convert pixels to user units.
1702                 * @param float $scale adjusting factor to convert pixels to user units.
1703                 * @author Nicola Asuni
1704                 * @access public
1705                 * @since 1.5.2
1706                 */
1707                 public function setImageScale($scale) {
1708                         $this->imgscale = $scale;
1709                 }
1710
1711                 /**
1712                 * Returns the adjusting factor to convert pixels to user units.
1713                 * @return float adjusting factor to convert pixels to user units.
1714                 * @author Nicola Asuni
1715                 * @access public
1716                 * @since 1.5.2
1717                 */
1718                 public function getImageScale() {
1719                         return $this->imgscale;
1720                 }
1721                                 
1722                 /**
1723                 * Returns an array of page dimensions:
1724                 * <ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>
1725                 * @param int $pagenum page number (empty = current page)
1726                 * @return array of page dimensions.
1727                 * @author Nicola Asuni
1728                 * @access public
1729                 * @since 4.5.027 (2009-03-16)
1730                 */
1731                 public function getPageDimensions($pagenum='') {
1732                         if (empty($pagenum)) {
1733                                 $pagenum = $this->page;
1734                         }
1735                         return $this->pagedim[$pagenum];
1736                 }
1737                 
1738                 /**
1739                 * Returns the page width in units.
1740                 * @param int $pagenum page number (empty = current page)
1741                 * @return int page width.
1742                 * @author Nicola Asuni
1743                 * @access public
1744                 * @since 1.5.2
1745                 * @see getPageDimensions()
1746                 */
1747                 public function getPageWidth($pagenum='') {
1748                         if (empty($pagenum)) {
1749                                 return $this->w;
1750                         }
1751                         return $this->pagedim[$pagenum]['w'];
1752                 }
1753
1754                 /**
1755                 * Returns the page height in units.
1756                 * @param int $pagenum page number (empty = current page)
1757                 * @return int page height.
1758                 * @author Nicola Asuni
1759                 * @access public
1760                 * @since 1.5.2
1761                 * @see getPageDimensions()
1762                 */
1763                 public function getPageHeight($pagenum='') {
1764                         if (empty($pagenum)) {
1765                                 return $this->h;
1766                         }
1767                         return $this->pagedim[$pagenum]['h'];
1768                 }
1769
1770                 /**
1771                 * Returns the page break margin.
1772                 * @param int $pagenum page number (empty = current page)
1773                 * @return int page break margin.
1774                 * @author Nicola Asuni
1775                 * @access public
1776                 * @since 1.5.2
1777                 * @see getPageDimensions()
1778                 */
1779                 public function getBreakMargin($pagenum='') {
1780                         if (empty($pagenum)) {
1781                                 return $this->bMargin;
1782                         }
1783                         return $this->pagedim[$pagenum]['bm'];
1784                 }
1785
1786                 /**
1787                 * Returns the scale factor (number of points in user unit).
1788                 * @return int scale factor.
1789                 * @author Nicola Asuni
1790                 * @access public
1791                 * @since 1.5.2
1792                 */
1793                 public function getScaleFactor() {
1794                         return $this->k;
1795                 }
1796
1797                 /**
1798                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1799                 * @param float $left Left margin.
1800                 * @param float $top Top margin.
1801                 * @param float $right Right margin. Default value is the left one.
1802                 * @access public
1803                 * @since 1.0
1804                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1805                 */
1806                 public function SetMargins($left, $top, $right=-1) {
1807                         //Set left, top and right margins
1808                         $this->lMargin = $left;
1809                         $this->tMargin = $top;
1810                         if ($right == -1) {
1811                                 $right = $left;
1812                         }
1813                         $this->rMargin = $right;
1814                 }
1815
1816                 /**
1817                 * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
1818                 * @param float $margin The margin.
1819                 * @access public
1820                 * @since 1.4
1821                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1822                 */
1823                 public function SetLeftMargin($margin) {
1824                         //Set left margin
1825                         $this->lMargin=$margin;
1826                         if (($this->page > 0) AND ($this->x < $margin)) {
1827                                 $this->x = $margin;
1828                         }
1829                 }
1830
1831                 /**
1832                 * Defines the top margin. The method can be called before creating the first page.
1833                 * @param float $margin The margin.
1834                 * @access public
1835                 * @since 1.5
1836                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1837                 */
1838                 public function SetTopMargin($margin) {
1839                         //Set top margin
1840                         $this->tMargin=$margin;
1841                         if (($this->page > 0) AND ($this->y < $margin)) {
1842                                 $this->y = $margin;
1843                         }
1844                 }
1845
1846                 /**
1847                 * Defines the right margin. The method can be called before creating the first page.
1848                 * @param float $margin The margin.
1849                 * @access public
1850                 * @since 1.5
1851                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1852                 */
1853                 public function SetRightMargin($margin) {
1854                         $this->rMargin=$margin;
1855                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1856                                 $this->x = $this->w - $margin;
1857                         }
1858                 }
1859
1860                 /**
1861                 * Set the internal Cell padding.
1862                 * @param float $pad internal padding.
1863                 * @access public
1864                 * @since 2.1.000 (2008-01-09)
1865                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1866                 */
1867                 public function SetCellPadding($pad) {
1868                         $this->cMargin = $pad;
1869                 }
1870
1871                 /**
1872                 * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
1873                 * @param boolean $auto Boolean indicating if mode should be on or off.
1874                 * @param float $margin Distance from the bottom of the page.
1875                 * @access public
1876                 * @since 1.0
1877                 * @see Cell(), MultiCell(), AcceptPageBreak()
1878                 */
1879                 public function SetAutoPageBreak($auto, $margin=0) {
1880                         //Set auto page break mode and triggering margin
1881                         $this->AutoPageBreak = $auto;
1882                         $this->bMargin = $margin;
1883                         $this->PageBreakTrigger = $this->h - $margin;
1884                 }
1885
1886                 /**
1887                 * Defines the way the document is to be displayed by the viewer.
1888                 * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
1889                 * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
1890                 * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
1891                 * @access public
1892                 * @since 1.2
1893                 */
1894                 public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
1895                         //Set display mode in viewer
1896                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1897                                 $this->ZoomMode = $zoom;
1898                         } else {
1899                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1900                         }
1901                         switch ($layout) {
1902                                 case 'default':
1903                                 case 'single':
1904                                 case 'SinglePage': {
1905                                         $this->LayoutMode = 'SinglePage';
1906                                         break;
1907                                 }
1908                                 case 'continuous':
1909                                 case 'OneColumn': {
1910                                         $this->LayoutMode = 'OneColumn';
1911                                         break;
1912                                 }
1913                                 case 'two':
1914                                 case 'TwoColumnLeft': {
1915                                         $this->LayoutMode = 'TwoColumnLeft';
1916                                         break;
1917                                 }
1918                                 case 'TwoColumnRight': {
1919                                         $this->LayoutMode = 'TwoColumnRight';
1920                                         break;
1921                                 }
1922                                 case 'TwoPageLeft': {
1923                                         $this->LayoutMode = 'TwoPageLeft';
1924                                         break;
1925                                 }
1926                                 case 'TwoPageRight': {
1927                                         $this->LayoutMode = 'TwoPageRight';
1928                                         break;
1929                                 }
1930                                 default: {
1931                                         $this->LayoutMode = 'SinglePage';
1932                                 }
1933                         }
1934                         switch ($mode) {
1935                                 case 'UseNone': {
1936                                         $this->PageMode = 'UseNone';
1937                                         break;
1938                                 }
1939                                 case 'UseOutlines': {
1940                                         $this->PageMode = 'UseOutlines';
1941                                         break;
1942                                 }
1943                                 case 'UseThumbs': {
1944                                         $this->PageMode = 'UseThumbs';
1945                                         break;
1946                                 }
1947                                 case 'FullScreen': {
1948                                         $this->PageMode = 'FullScreen';
1949                                         break;
1950                                 }
1951                                 case 'UseOC': {
1952                                         $this->PageMode = 'UseOC';
1953                                         break;
1954                                 }
1955                                 case '': {
1956                                         $this->PageMode = 'UseAttachments';
1957                                         break;
1958                                 }
1959                                 default: {
1960                                         $this->PageMode = 'UseNone';
1961                                 }
1962                         }
1963                 }
1964
1965                 /**
1966                 * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
1967                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1968                 * @param boolean $compress Boolean indicating if compression must be enabled.
1969                 * @access public
1970                 * @since 1.4
1971                 */
1972                 public function SetCompression($compress) {
1973                         //Set page compression
1974                         if (function_exists('gzcompress')) {
1975                                 $this->compress = $compress;
1976                         } else {
1977                                 $this->compress = false;
1978                         }
1979                 }
1980
1981                 /**
1982                 * Defines the title of the document.
1983                 * @param string $title The title.
1984                 * @access public
1985                 * @since 1.2
1986                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1987                 */
1988                 public function SetTitle($title) {
1989                         //Title of document
1990                         $this->title = $title;
1991                 }
1992
1993                 /**
1994                 * Defines the subject of the document.
1995                 * @param string $subject The subject.
1996                 * @access public
1997                 * @since 1.2
1998                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1999                 */
2000                 public function SetSubject($subject) {
2001                         //Subject of document
2002                         $this->subject = $subject;
2003                 }
2004
2005                 /**
2006                 * Defines the author of the document.
2007                 * @param string $author The name of the author.
2008                 * @access public
2009                 * @since 1.2
2010                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2011                 */
2012                 public function SetAuthor($author) {
2013                         //Author of document
2014                         $this->author = $author;
2015                 }
2016
2017                 /**
2018                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2019                 * @param string $keywords The list of keywords.
2020                 * @access public
2021                 * @since 1.2
2022                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2023                 */
2024                 public function SetKeywords($keywords) {
2025                         //Keywords of document
2026                         $this->keywords = $keywords;
2027                 }
2028
2029                 /**
2030                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2031                 * @param string $creator The name of the creator.
2032                 * @access public
2033                 * @since 1.2
2034                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2035                 */
2036                 public function SetCreator($creator) {
2037                         //Creator of document
2038                         $this->creator = $creator;
2039                 }
2040                 
2041                 /**
2042                 * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.
2043                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
2044                 * @param string $msg The error message
2045                 * @access public
2046                 * @since 1.0
2047                 */
2048                 public function Error($msg) {
2049                         // unset all class variables
2050                         $this->_destroy(true);
2051                         // exit program and print error
2052                         die('<strong>TCPDF ERROR: </strong>'.$msg);
2053                 }
2054
2055                 /**
2056                 * This method begins the generation of the PDF document.
2057                 * It is not necessary to call it explicitly because AddPage() does it automatically.
2058                 * Note: no page is created by this method
2059                 * @access public
2060                 * @since 1.0
2061                 * @see AddPage(), Close()
2062                 */
2063                 public function Open() {
2064                         //Begin document
2065                         $this->state = 1;
2066                 }
2067
2068                 /**
2069                 * Terminates the PDF document.
2070                 * It is not necessary to call this method explicitly because Output() does it automatically.
2071                 * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2072                 * @access public
2073                 * @since 1.0
2074                 * @see Open(), Output()
2075                 */
2076                 public function Close() {
2077                         if ($this->state == 3) {
2078                                 return;
2079                         }
2080                         if ($this->page == 0) {
2081                                 $this->AddPage();
2082                         }
2083                         // close page
2084                         $this->endPage();
2085                         // close document
2086                         $this->_enddoc();
2087                         // unset all class variables (except critical ones)
2088                         $this->_destroy(false);
2089                 }
2090                 
2091                 /**
2092                 * Move pointer at the specified document page and update page dimensions.
2093                 * @param int $pnum page number
2094                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2095                 * @access public
2096                 * @since 2.1.000 (2008-01-07)
2097                 * @see getPage(), lastpage(), getNumPages()
2098                 */
2099                 public function setPage($pnum, $resetmargins=false) {
2100                         if ($pnum == $this->page) {
2101                                 return;
2102                         }
2103                         if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2104                                 $this->state = 2;
2105                                 // save current graphic settings
2106                                 //$gvars = $this->getGraphicVars();
2107                                 $oldpage = $this->page;
2108                                 $this->page = $pnum;
2109                                 $this->wPt = $this->pagedim[$this->page]['w'];
2110                                 $this->hPt = $this->pagedim[$this->page]['h'];
2111                                 $this->w = $this->wPt / $this->k;
2112                                 $this->h = $this->hPt / $this->k;
2113                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
2114                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
2115                                 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2116                                 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
2117                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
2118                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
2119                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
2120                                 // restore graphic settings
2121                                 //$this->setGraphicVars($gvars);
2122                                 if ($resetmargins) {
2123                                         $this->lMargin = $this->pagedim[$this->page]['olm'];
2124                                         $this->rMargin = $this->pagedim[$this->page]['orm'];
2125                                         $this->SetY($this->tMargin);
2126                                 } else {
2127                                         // account for booklet mode
2128                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
2129                                                 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
2130                                                 $this->lMargin += $deltam;
2131                                                 $this->rMargin -= $deltam;
2132                                         }
2133                                 }
2134                         } else {
2135                                 $this->Error('Wrong page number on setPage() function.');
2136                         }
2137                 }
2138                 
2139                 /**
2140                 * Reset pointer to the last document page.
2141                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2142                 * @access public
2143                 * @since 2.0.000 (2008-01-04)
2144                 * @see setPage(), getPage(), getNumPages()
2145                 */
2146                 public function lastPage($resetmargins=false) {
2147                         $this->setPage($this->getNumPages(), $resetmargins);
2148                 }
2149                 
2150                 /**
2151                 * Get current document page number.
2152                 * @return int page number
2153                 * @access public
2154                 * @since 2.1.000 (2008-01-07)
2155                 * @see setPage(), lastpage(), getNumPages()
2156                 */
2157                 public function getPage() {
2158                         return $this->page;
2159                 }
2160                 
2161                 
2162                 /**
2163                 * Get the total number of insered pages.
2164                 * @return int number of pages
2165                 * @access public
2166                 * @since 2.1.000 (2008-01-07)
2167                 * @see setPage(), getPage(), lastpage()
2168                 */
2169                 public function getNumPages() {
2170                         return $this->numpages;
2171                 }
2172
2173                 /**
2174                 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
2175                 * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
2176                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2177                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
2178                 * @access public
2179                 * @since 1.0
2180                 * @see startPage(), endPage()
2181                 */
2182                 public function AddPage($orientation='', $format='') {
2183                         if (!isset($this->original_lMargin)) {
2184                                 $this->original_lMargin = $this->lMargin;
2185                         }
2186                         if (!isset($this->original_rMargin)) {
2187                                 $this->original_rMargin = $this->rMargin;
2188                         }
2189                         // terminate previous page
2190                         $this->endPage();
2191                         // start new page
2192                         $this->startPage($orientation, $format);
2193                 }
2194
2195                 /**
2196                 * Terminate the current page
2197                 * @access protected
2198                 * @since 4.2.010 (2008-11-14)
2199                 * @see startPage(), AddPage()
2200                 */
2201                 protected function endPage() {
2202                         // check if page is already closed
2203                         if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
2204                                 return;
2205                         }
2206                         $this->InFooter = true;
2207                         // print page footer
2208                         $this->setFooter();
2209                         // close page
2210                         $this->_endpage();
2211                         // mark page as closed
2212                         $this->pageopen[$this->page] = false;
2213                         $this->InFooter = false;
2214                 }
2215
2216                 /**
2217                 * Starts a new page to the document. The page must be closed using the endPage() function.
2218                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
2219                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2220                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
2221                 * @access protected
2222                 * @since 4.2.010 (2008-11-14)
2223                 * @see endPage(), AddPage()
2224                 */
2225                 protected function startPage($orientation='', $format='') {
2226                         if ($this->numpages > $this->page) {
2227                                 // this page has been already added
2228                                 $this->setPage($this->page + 1);
2229                                 $this->SetY($this->tMargin);
2230                                 return;
2231                         }
2232                         // start a new page
2233                         if ($this->state == 0) {
2234                                 $this->Open();
2235                         }
2236                         ++$this->numpages;
2237                         $this->swapMargins($this->booklet);
2238                         // save current graphic settings
2239                         $gvars = $this->getGraphicVars();
2240                         // start new page
2241                         $this->_beginpage($orientation, $format);
2242                         // mark page as open
2243                         $this->pageopen[$this->page] = true;
2244                         // restore graphic settings
2245                         $this->setGraphicVars($gvars);
2246                         // mark this point
2247                         $this->setPageMark();
2248                         // print page header
2249                         $this->setHeader();
2250                         // restore graphic settings
2251                         $this->setGraphicVars($gvars);
2252                         // mark this point
2253                         $this->setPageMark();
2254                         // print table header (if any)
2255                         $this->setTableHeader();
2256                 }
2257                         
2258                 /**
2259                  * Set start-writing mark on current page for multicell borders and fills.
2260                  * This function must be called after calling Image() function for a background image.
2261                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
2262                  * @access public
2263                  * @since 4.0.016 (2008-07-30)
2264                  */
2265                 public function setPageMark() {
2266                         $this->intmrk[$this->page] = $this->pagelen[$this->page];
2267                 }
2268                 
2269                 /**
2270                  * Set header data.
2271                  * @param string $ln header image logo
2272                  * @param string $lw header image logo width in mm
2273                  * @param string $ht string to print as title on document header
2274                  * @param string $hs string to print on document header
2275                  * @access public
2276                  */
2277                 public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
2278                         $this->header_logo = $ln;
2279                         $this->header_logo_width = $lw;
2280                         $this->header_title = $ht;
2281                         $this->header_string = $hs;
2282                 }
2283                 
2284                 /**
2285                  * Returns header data:
2286                  * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
2287                  * @return array()
2288                  * @access public
2289                  * @since 4.0.012 (2008-07-24)
2290                  */
2291                 public function getHeaderData() {
2292                         $ret = array();
2293                         $ret['logo'] = $this->header_logo;
2294                         $ret['logo_width'] = $this->header_logo_width;
2295                         $ret['title'] = $this->header_title;
2296                         $ret['string'] = $this->header_string;
2297                         return $ret;
2298                 }
2299                 
2300                 /**
2301                  * Set header margin.
2302                  * (minimum distance between header and top page margin)
2303                  * @param int $hm distance in user units
2304                  * @access public
2305                  */
2306                 public function setHeaderMargin($hm=10) {
2307                         $this->header_margin = $hm;
2308                 }
2309                 
2310                 /**
2311                  * Returns header margin in user units.
2312                  * @return float
2313                  * @since 4.0.012 (2008-07-24)
2314                  * @access public
2315                  */
2316                 public function getHeaderMargin() {
2317                         return $this->header_margin;
2318                 }
2319                 
2320                 /**
2321                  * Set footer margin.
2322                  * (minimum distance between footer and bottom page margin)
2323                  * @param int $fm distance in user units
2324                  * @access public
2325                  */
2326                 public function setFooterMargin($fm=10) {
2327                         $this->footer_margin = $fm;
2328                 }
2329                 
2330                 /**
2331                  * Returns footer margin in user units.
2332                  * @return float
2333                  * @since 4.0.012 (2008-07-24)
2334                  * @access public
2335                  */
2336                 public function getFooterMargin() {
2337                         return $this->footer_margin;
2338                 }
2339                 /**
2340                  * Set a flag to print page header.
2341                  * @param boolean $val set to true to print the page header (default), false otherwise. 
2342                  * @access public
2343                  */
2344                 public function setPrintHeader($val=true) {
2345                         $this->print_header = $val;
2346                 }
2347                 
2348                 /**
2349                  * Set a flag to print page footer.
2350                  * @param boolean $value set to true to print the page footer (default), false otherwise. 
2351                  * @access public
2352                  */
2353                 public function setPrintFooter($val=true) {
2354                         $this->print_footer = $val;
2355                 }
2356                 
2357                 /**
2358                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2359                  * @return float 
2360                  * @access public
2361                  */
2362                 public function getImageRBX() {
2363                         return $this->img_rb_x;
2364                 }
2365                 
2366                 /**
2367                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2368                  * @return float 
2369                  * @access public
2370                  */
2371                 public function getImageRBY() {
2372                         return $this->img_rb_y;
2373                 }
2374                 
2375                 /**
2376                  * This method is used to render the page header.
2377                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2378                  * @access public
2379                  */
2380                 public function Header() {
2381                         $ormargins = $this->getOriginalMargins();
2382                         $headerfont = $this->getHeaderFont();
2383                         $headerdata = $this->getHeaderData();
2384                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2385                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2386                                 $imgy = $this->getImageRBY();
2387                         } else {
2388                                 $imgy = $this->GetY();
2389                         }
2390                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2391                         // set starting margin for text data cell
2392                         if ($this->getRTL()) {
2393                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2394                         } else {
2395                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2396                         }
2397                         $this->SetTextColor(0, 0, 0);
2398                         // header title
2399                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2400                         $this->SetX($header_x);                 
2401                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
2402                         // header string
2403                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2404                         $this->SetX($header_x);
2405                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
2406                         // print an ending header line
2407                         $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2408                         $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
2409                         if ($this->getRTL()) {
2410                                 $this->SetX($ormargins['right']);
2411                         } else {
2412                                 $this->SetX($ormargins['left']);
2413                         }
2414                         $this->Cell(0, 0, '', 'T', 0, 'C');
2415                 }
2416                 
2417                 /**
2418                  * This method is used to render the page footer. 
2419                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2420                  * @access public
2421                  */
2422                 public function Footer() {                              
2423                         $cur_y = $this->GetY();
2424                         $ormargins = $this->getOriginalMargins();
2425                         $this->SetTextColor(0, 0, 0);                   
2426                         //set style for cell border
2427                         $line_width = 0.85 / $this->getScaleFactor();
2428                         $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2429                         //print document barcode
2430                         $barcode = $this->getBarcode();
2431                         if (!empty($barcode)) {
2432                                 $this->Ln($line_width);
2433                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2434                                 $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');     
2435                         }
2436                         if (empty($this->pagegroups)) {
2437                                 $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
2438                         } else {
2439                                 $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
2440                         }               
2441                         $this->SetY($cur_y);
2442                         //Print page number
2443                         if ($this->getRTL()) {
2444                                 $this->SetX($ormargins['right']);
2445                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2446                         } else {
2447                                 $this->SetX($ormargins['left']);
2448                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2449                         }
2450                 }
2451                 
2452                 /**
2453                  * This method is used to render the page header. 
2454                  * @access protected
2455                  * @since 4.0.012 (2008-07-24)
2456                  */
2457                 protected function setHeader() {
2458                         if ($this->print_header) {
2459                                 $lasth = $this->lasth;
2460                                 $this->_out('q');
2461                                 $this->rMargin = $this->original_rMargin;
2462                                 $this->lMargin = $this->original_lMargin;
2463                                 $this->cMargin = 0;
2464                                 //set current position
2465                                 if ($this->rtl) {
2466                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2467                                 } else {
2468                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2469                                 }
2470                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2471                                 $this->Header();
2472                                 //restore position
2473                                 if ($this->rtl) {
2474                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2475                                 } else {
2476                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2477                                 }
2478                                 $this->_out('Q');
2479                                 $this->lasth = $lasth;
2480                         }
2481                 }
2482                 
2483                 /**
2484                  * This method is used to render the page footer. 
2485                  * @access protected
2486                  * @since 4.0.012 (2008-07-24)
2487                  */
2488                 protected function setFooter() {
2489                         //Page footer
2490                         // save current graphic settings
2491                         $gvars = $this->getGraphicVars();
2492                         // mark this point
2493                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
2494                         $this->_out("\n");
2495                         if ($this->print_footer) {
2496                                 $lasth = $this->lasth;
2497                                 $this->_out('q');
2498                                 $this->rMargin = $this->original_rMargin;
2499                                 $this->lMargin = $this->original_lMargin;
2500                                 $this->cMargin = 0;
2501                                 //set current position
2502                                 $footer_y = $this->h - $this->footer_margin;
2503                                 if ($this->rtl) {
2504                                         $this->SetXY($this->original_rMargin, $footer_y);
2505                                 } else {
2506                                         $this->SetXY($this->original_lMargin, $footer_y);
2507                                 }
2508                                 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
2509                                 $this->Footer();
2510                                 //restore position
2511                                 if ($this->rtl) {
2512                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2513                                 } else {
2514                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2515                                 }
2516                                 $this->_out('Q');
2517                                 $this->lasth = $lasth;
2518                         }
2519                         // restore graphic settings
2520                         $this->setGraphicVars($gvars);
2521                         // calculate footer lenght
2522                         $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
2523                 }
2524
2525                 /**
2526                  * This method is used to render the table header on new page (if any). 
2527                  * @access protected
2528                  * @since 4.5.030 (2009-03-25)
2529                  */
2530                 protected function setTableHeader() {
2531                         if (!$this->empty_string($this->theadMargin)) {
2532                                 // restore the original top-margin
2533                                 $this->tMargin = $this->theadMargin;
2534                                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
2535                                 $this->y = $this->theadMargin;
2536                         }
2537                         if (!$this->empty_string($this->thead)) {
2538                                 // print table header
2539                                 $this->writeHTML($this->thead, false, false, false, false, '');
2540                                 // set new top margin to skip the table headers
2541                                 if (!isset($this->theadMargin) OR ($this->empty_string($this->theadMargin))) {
2542                                         $this->theadMargin = $this->tMargin;
2543                                 }
2544                                 $this->tMargin = $this->y;
2545                                 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2546                         }
2547                 }
2548                 
2549                 /**
2550                 * Returns the current page number.
2551                 * @return int page number
2552                 * @access public
2553                 * @since 1.0
2554                 * @see AliasNbPages(), getAliasNbPages()
2555                 */
2556                 public function PageNo() {
2557                         return $this->page;
2558                 }
2559
2560                 /**
2561                 * Defines a new spot color. 
2562                 * It can be expressed in RGB components or gray scale. 
2563                 * The method can be called before the first page is created and the value is retained from page to page.
2564                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2565                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2566                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2567                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2568                 * @access public
2569                 * @since 4.0.024 (2008-09-12)
2570                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2571                 */
2572                 public function AddSpotColor($name, $c, $m, $y, $k) {
2573                         if (!isset($this->spot_colors[$name])) {
2574                                 $i = 1 + count($this->spot_colors);
2575                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2576                         }
2577                 }
2578
2579                 /**
2580                 * Defines the color used for all drawing operations (lines, rectangles and cell borders). 
2581                 * It can be expressed in RGB components or gray scale. 
2582                 * The method can be called before the first page is created and the value is retained from page to page.
2583                 * @param array $color array of colors
2584                 * @access public
2585                 * @since 3.1.000 (2008-06-11)
2586                 * @see SetDrawColor()
2587                 */
2588                 public function SetDrawColorArray($color) {
2589                         if (isset($color)) {
2590                                 $color = array_values($color);
2591                                 $r = isset($color[0]) ? $color[0] : -1;
2592                                 $g = isset($color[1]) ? $color[1] : -1;
2593                                 $b = isset($color[2]) ? $color[2] : -1;
2594                                 $k = isset($color[3]) ? $color[3] : -1;
2595                                 if ($r >= 0) {
2596                                         $this->SetDrawColor($r, $g, $b, $k);
2597                                 }
2598                         }
2599                 }
2600
2601                 /**
2602                 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2603                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2604                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2605                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2606                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2607                 * @access public
2608                 * @since 1.3
2609                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2610                 */
2611                 public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2612                         // set default values
2613                         if (!is_numeric($col1)) {
2614                                 $col1 = 0;
2615                         }
2616                         if (!is_numeric($col2)) {
2617                                 $col2 = -1;
2618                         }
2619                         if (!is_numeric($col3)) {
2620                                 $col3 = -1;
2621                         }
2622                         if (!is_numeric($col4)) {
2623                                 $col4 = -1;
2624                         }
2625                         //Set color for all stroking operations
2626                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2627                                 // Grey scale
2628                                 $this->DrawColor = sprintf('%.3F G', $col1/255);
2629                         } elseif ($col4 == -1) {
2630                                 // RGB
2631                                 $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
2632                         } else {
2633                                 // CMYK
2634                                 $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
2635                         }
2636                         if ($this->page > 0) {
2637                                 $this->_out($this->DrawColor);
2638                         }
2639                 }
2640                 
2641                 /**
2642                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2643                 * @param string $name name of the spot color
2644                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2645                 * @access public
2646                 * @since 4.0.024 (2008-09-12)
2647                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2648                 */
2649                 public function SetDrawSpotColor($name, $tint=100) {
2650                         if (!isset($this->spot_colors[$name])) {
2651                                 $this->Error('Undefined spot color: '.$name);
2652                         }
2653                         $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
2654                         if ($this->page > 0) {
2655                                 $this->_out($this->DrawColor);
2656                         }
2657                 }
2658                 
2659                 /**
2660                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). 
2661                 * It can be expressed in RGB components or gray scale. 
2662                 * The method can be called before the first page is created and the value is retained from page to page.
2663                 * @param array $color array of colors
2664                 * @access public
2665                 * @since 3.1.000 (2008-6-11)
2666                 * @see SetFillColor()
2667                 */
2668                 public function SetFillColorArray($color) {
2669                         if (isset($color)) {
2670                                 $color = array_values($color);
2671                                 $r = isset($color[0]) ? $color[0] : -1;
2672                                 $g = isset($color[1]) ? $color[1] : -1;
2673                                 $b = isset($color[2]) ? $color[2] : -1;
2674                                 $k = isset($color[3]) ? $color[3] : -1;
2675                                 if ($r >= 0) {
2676                                         $this->SetFillColor($r, $g, $b, $k);
2677                                 }
2678                         }
2679                 }
2680                 
2681                 /**
2682                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2683                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2684                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2685                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2686                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2687                 * @access public
2688                 * @since 1.3
2689                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2690                 */
2691                 public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2692                         // set default values
2693                         if (!is_numeric($col1)) {
2694                                 $col1 = 0;
2695                         }
2696                         if (!is_numeric($col2)) {
2697                                 $col2 = -1;
2698                         }
2699                         if (!is_numeric($col3)) {
2700                                 $col3 = -1;
2701                         }
2702                         if (!is_numeric($col4)) {
2703                                 $col4 = -1;
2704                         }
2705                         //Set color for all filling operations
2706                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2707                                 // Grey scale
2708                                 $this->FillColor = sprintf('%.3F g', $col1/255);
2709                                 $this->bgcolor = array('G' => $col1);
2710                         } elseif ($col4 == -1) {
2711                                 // RGB
2712                                 $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2713                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2714                         } else {
2715                                 // CMYK
2716                                 $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2717                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2718                         }
2719                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2720                         if ($this->page > 0) {
2721                                 $this->_out($this->FillColor);
2722                         }
2723                 }
2724                 
2725                 /**
2726                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2727                 * @param string $name name of the spot color
2728                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2729                 * @access public
2730                 * @since 4.0.024 (2008-09-12)
2731                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2732                 */
2733                 public function SetFillSpotColor($name, $tint=100) {
2734                         if (!isset($this->spot_colors[$name])) {
2735                                 $this->Error('Undefined spot color: '.$name);
2736                         }
2737                         $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2738                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2739                         if ($this->page > 0) {
2740                                 $this->_out($this->FillColor);
2741                         }
2742                 }
2743                 
2744                 /**
2745                 * Defines the color used for text. It can be expressed in RGB components or gray scale. 
2746                 * The method can be called before the first page is created and the value is retained from page to page.
2747                 * @param array $color array of colors
2748                 * @access public
2749                 * @since 3.1.000 (2008-6-11)
2750                 * @see SetFillColor()
2751                 */
2752                 public function SetTextColorArray($color) {
2753                         if (isset($color)) {
2754                                 $color = array_values($color);
2755                                 $r = isset($color[0]) ? $color[0] : -1;
2756                                 $g = isset($color[1]) ? $color[1] : -1;
2757                                 $b = isset($color[2]) ? $color[2] : -1;
2758                                 $k = isset($color[3]) ? $color[3] : -1;
2759                                 if ($r >= 0) {
2760                                         $this->SetTextColor($r, $g, $b, $k);
2761                                 }
2762                         }
2763                 }
2764
2765                 /**
2766                 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2767                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2768                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2769                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2770                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2771                 * @access public
2772                 * @since 1.3
2773                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2774                 */
2775                 public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2776                         // set default values
2777                         if (!is_numeric($col1)) {
2778                                 $col1 = 0;
2779                         }
2780                         if (!is_numeric($col2)) {
2781                                 $col2 = -1;
2782                         }
2783                         if (!is_numeric($col3)) {
2784                                 $col3 = -1;
2785                         }
2786                         if (!is_numeric($col4)) {
2787                                 $col4 = -1;
2788                         }
2789                         //Set color for text
2790                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2791                                 // Grey scale
2792                                 $this->TextColor = sprintf('%.3F g', $col1/255);
2793                                 $this->fgcolor = array('G' => $col1);
2794                         } elseif ($col4 == -1) {
2795                                 // RGB
2796                                 $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
2797                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2798                         } else {
2799                                 // CMYK
2800                                 $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
2801                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2802                         }
2803                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2804                 }
2805                 
2806                 /**
2807                 * Defines the spot color used for text.
2808                 * @param string $name name of the spot color
2809                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2810                 * @access public
2811                 * @since 4.0.024 (2008-09-12)
2812                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2813                 */
2814                 public function SetTextSpotColor($name, $tint=100) {
2815                         if (!isset($this->spot_colors[$name])) {
2816                                 $this->Error('Undefined spot color: '.$name);
2817                         }
2818                         $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
2819                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2820                         if ($this->page > 0) {
2821                                 $this->_out($this->TextColor);
2822                         }
2823                 }
2824
2825                 /**
2826                 * Returns the length of a string in user unit. A font must be selected.<br>
2827                 * @param string $s The string whose length is to be computed
2828                 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
2829                 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.
2830                 * @param float $fontsize Font size in points. The default value is the current size.
2831                 * @return int string length
2832                 * @author Nicola Asuni
2833                 * @access public
2834                 * @since 1.2
2835                 */
2836                 public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2837                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize);
2838                 }
2839                 
2840                 /**
2841                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2842                 * @param string $arr The array of chars whose total length is to be computed
2843                 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
2844                 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.
2845                 * @param float $fontsize Font size in points. The default value is the current size.
2846                 * @return int string length
2847                 * @author Nicola Asuni
2848                 * @access public
2849                 * @since 2.4.000 (2008-03-06)
2850                 */
2851                 public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2852                         // store current values
2853                         if (!$this->empty_string($fontname)) {
2854                                 $prev_FontFamily = $this->FontFamily;
2855                                 $prev_FontStyle = $this->FontStyle;
2856                                 $prev_FontSizePt = $this->FontSizePt;
2857                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2858                         }
2859                         $w = 0;
2860                         foreach ($sa as $char) {
2861                                 $w += $this->GetCharWidth($char);
2862                         }
2863                         // restore previous values
2864                         if (!$this->empty_string($fontname)) {
2865                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2866                         }
2867                         return $w;
2868                 }
2869                 
2870                 /**
2871                 * Returns the length of the char in user unit for the current font.<br>
2872                 * @param int $char The char code whose length is to be returned
2873                 * @return int char width
2874                 * @author Nicola Asuni
2875                 * @access public
2876                 * @since 2.4.000 (2008-03-06)
2877                 */
2878                 public function GetCharWidth($char) {
2879                         if ($char == 173) {
2880                                 // SHY character will not be printed
2881                                 return (0);
2882                         }
2883                         $cw = &$this->CurrentFont['cw'];
2884                         if (isset($cw[$char])) {
2885                                 $w = $cw[$char];
2886                         } elseif (isset($this->CurrentFont['dw'])) {
2887                                 // default width
2888                                 $w = $this->CurrentFont['dw'];
2889                         } elseif (isset($cw[32])) {
2890                                 // default width
2891                                 $dw = $cw[32];
2892                         } else {
2893                                 $w = 600;
2894                         }
2895                         return ($w * $this->FontSize / 1000);
2896                 }
2897                 
2898                 /**
2899                 * Returns the numbero of characters in a string.
2900                 * @param string $s The input string.
2901                 * @return int number of characters
2902                 * @access public
2903                 * @since 2.0.0001 (2008-01-07)
2904                 */
2905                 public function GetNumChars($s) {
2906                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
2907                                 return count($this->UTF8StringToArray($s));
2908                         } 
2909                         return strlen($s);
2910                 }
2911                         
2912                 /**
2913                 * Fill the list of available fonts ($this->fontlist).
2914                 * @access protected
2915                 * @since 4.0.013 (2008-07-28)
2916                 */
2917                 protected function getFontsList() {
2918                         $fontsdir = opendir($this->_getfontpath());
2919                         while (($file = readdir($fontsdir)) !== false) {
2920                                 if (substr($file, -4) == '.php') {
2921                                         array_push($this->fontlist, strtolower(basename($file, '.php')));
2922                                 }
2923                         }
2924                         closedir($fontsdir);
2925                 }
2926                 
2927                 /**
2928                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2929                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT). 
2930                 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
2931                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2932                 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
2933                 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
2934                 * @return array containing the font data, or false in case of error.
2935                 * @access public
2936                 * @since 1.5
2937                 * @see SetFont()
2938                 */
2939                 public function AddFont($family, $style='', $fontfile='') {
2940                         if ($this->empty_string($family)) {
2941                                 if (!$this->empty_string($this->FontFamily)) {
2942                                         $family = $this->FontFamily;
2943                                 } else {
2944                                         $this->Error('Empty font family');
2945                                 }
2946                         }
2947                         $family = strtolower($family);
2948                         if ((!$this->isunicode) AND ($family == 'arial')) {
2949                                 $family = 'helvetica';
2950                         }
2951                         if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
2952                                 $style = '';
2953                         }
2954                         $tempstyle = strtoupper($style);
2955                         $style = '';
2956                         // underline
2957                         if (strpos($tempstyle, 'U') !== false) {
2958                                 $this->underline = true;
2959                         } else {
2960                                 $this->underline = false;
2961                         }
2962                         // line through (deleted)
2963                         if (strpos($tempstyle, 'D') !== false) {
2964                                 $this->linethrough = true;
2965                         } else {
2966                                 $this->linethrough = false;
2967                         }
2968                         // bold
2969                         if (strpos($tempstyle, 'B') !== false) {
2970                                 $style .= 'B';
2971                         }
2972                         // oblique
2973                         if (strpos($tempstyle, 'I') !== false) {
2974                                 $style .= 'I';
2975                         }
2976                         $bistyle = $style;
2977                         $fontkey = $family.$style;
2978                         $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
2979                         $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
2980                         // check if the font has been already added
2981                         if ($this->getFontBuffer($fontkey) !== false) {
2982                                 return $fontdata;
2983                         }
2984                         if (isset($type)) {
2985                                 unset($type); 
2986                         }
2987                         if (isset($cw)) {
2988                                 unset($cw); 
2989                         }
2990                         // get specified font directory (if any)
2991                         $fontdir = '';
2992                         if (!$this->empty_string($fontfile)) {
2993                                 $fontdir = dirname($fontfile);
2994                                 if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
2995                                         $fontdir = '';
2996                                 } else {
2997                                         $fontdir .= '/';
2998                                 }
2999                         }
3000                         // search and include font file
3001                         if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
3002                                 // build a standard filenames for specified font
3003                                 $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
3004                                 $fontfile2 = str_replace(' ', '', $family).'.php';
3005                                 // search files on various directories
3006                                 if (file_exists($fontdir.$fontfile1)) {
3007                                         $fontfile = $fontdir.$fontfile1;
3008                                 } elseif (file_exists($this->_getfontpath().$fontfile1)) {
3009                                         $fontfile = $this->_getfontpath().$fontfile1;
3010                                 } elseif (file_exists($fontfile1)) {
3011                                         $fontfile = $fontfile1;
3012                                 } elseif (file_exists($fontdir.$fontfile2)) {
3013                                         $fontfile = $fontdir.$fontfile2;
3014                                 } elseif (file_exists($this->_getfontpath().$fontfile2)) {
3015                                         $fontfile = $this->_getfontpath().$fontfile2;
3016                                 } else {
3017                                         $fontfile = $fontfile2;
3018                                 }
3019                         }
3020                         // include font file
3021                         if (file_exists($fontfile)) {
3022                                 include($fontfile);
3023                         } else {
3024                                 $this->Error('Could not include font definition file: '.$family.'');
3025                         }
3026                         // check font parameters
3027                         if ((!isset($type)) OR (!isset($cw))) {
3028                                 $this->Error('The font definition file has a bad format: '.$fontfile.'');
3029                         }
3030                         if (!isset($file)) {
3031                                 $file = '';
3032                         }
3033                         if (!isset($enc)) {
3034                                 $enc = '';
3035                         }
3036                         if (!isset($dw) OR $this->empty_string($dw)) {
3037                                 // set default width
3038                                 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
3039                                         $dw = $desc['MissingWidth'];
3040                                 } elseif (isset($cw[32])) {
3041                                         $dw = $cw[32];
3042                                 } else {
3043                                         $dw = 600;
3044                                 }
3045                         }
3046                         ++$this->numfonts;                      
3047                         // register CID font (all styles at once)
3048                         if ($type == 'cidfont0') {
3049                                 $file = ''; // not embedded
3050                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
3051                                 $sname = $name.$styles[$bistyle];
3052                                 if ((strpos($bistyle, 'B') !== false) AND (isset($desc['StemV'])) AND ($desc['StemV'] == 70)) {
3053                                         $desc['StemV'] = 120;
3054                                 }
3055                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc));
3056                         } elseif ($type == 'core') {
3057                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $dw));
3058                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
3059                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'file' => $file, 'enc' => $enc, 'desc' => $desc));
3060                         } elseif ($type == 'TrueTypeUnicode') {
3061                                 $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg));
3062                         } else {
3063                                 $this->Error('Unknow font type: '.$type.'');
3064                         }
3065                         if (isset($diff) AND (!empty($diff))) {
3066                                 //Search existing encodings
3067                                 $d = 0;
3068                                 $nb = count($this->diffs);
3069                                 for ($i=1; $i <= $nb; ++$i) {
3070                                         if ($this->diffs[$i] == $diff) {
3071                                                 $d = $i;
3072                                                 break;
3073                                         }
3074                                 }
3075                                 if ($d == 0) {
3076                                         $d = $nb + 1;
3077                                         $this->diffs[$d] = $diff;
3078                                 }
3079                                 $this->setFontSubBuffer($fontkey, 'diff', $d);
3080                         }
3081                         if (!$this->empty_string($file)) {
3082                                 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
3083                                         $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
3084                                 } elseif ($type != 'core') {
3085                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
3086                                 }
3087                         }
3088                         return $fontdata;
3089                 }
3090
3091                 /**
3092                 * Sets the font used to print character strings. 
3093                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
3094                 * The method can be called before the first page is created and the font is retained from page to page. 
3095                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
3096                 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
3097                 * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
3098                 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
3099                 * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
3100                 * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
3101                 * @access public
3102                 * @since 1.0
3103                 * @see AddFont(), SetFontSize()
3104                 */
3105                 public function SetFont($family, $style='', $size=0, $fontfile='') {
3106                         //Select a font; size given in points
3107                         if ($size == 0) {
3108                                 $size = $this->FontSizePt;
3109                         }
3110                         // try to add font (if not already added)
3111                         $fontdata = $this->AddFont($family, $style, $fontfile);
3112                         $this->FontFamily = $fontdata['family'];
3113                         $this->FontStyle = $fontdata['style'];
3114                         $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
3115                         $this->SetFontSize($size);
3116                 }
3117
3118                 /**
3119                 * Defines the size of the current font.
3120                 * @param float $size The size (in points)
3121                 * @access public
3122                 * @since 1.0
3123                 * @see SetFont()
3124                 */
3125                 public function SetFontSize($size) {
3126                         //Set font size in points
3127                         $this->FontSizePt = $size;
3128                         $this->FontSize = $size / $this->k;
3129                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
3130                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
3131                         } else {
3132                                 $this->FontAscent = 0.8 * $this->FontSize;
3133                         }
3134                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
3135                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
3136                         } else {
3137                                 $this->FontDescent = 0.2 * $this->FontSize;
3138                         }
3139                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
3140                                 $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
3141                         }
3142                 }
3143
3144                 /**
3145                 * Defines the default monospaced font.
3146                 * @param string $font Font name.
3147                 * @access public
3148                 * @since 4.5.025
3149                 */
3150                 public function SetDefaultMonospacedFont($font) {
3151                         $this->default_monospaced_font = $font;
3152                 }
3153                 
3154                 /**
3155                 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
3156                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
3157                 * @access public
3158                 * @since 1.5
3159                 * @see Cell(), Write(), Image(), Link(), SetLink()
3160                 */
3161                 public function AddLink() {
3162                         //Create a new internal link
3163                         $n = count($this->links) + 1;
3164                         $this->links[$n] = array(0, 0);
3165                         return $n;
3166                 }
3167
3168                 /**
3169                 * Defines the page and position a link points to.
3170                 * @param int $link The link identifier returned by AddLink()
3171                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
3172                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
3173                 * @access public
3174                 * @since 1.5
3175                 * @see AddLink()
3176                 */
3177                 public function SetLink($link, $y=0, $page=-1) {
3178                         if ($y == -1) {
3179                                 $y = $this->y;
3180                         }
3181                         if ($page == -1) {
3182                                 $page = $this->page;
3183                         }
3184                         $this->links[$link] = array($page, $y);
3185                 }
3186
3187                 /**
3188                 * Puts a link on a rectangular area of the page.
3189                 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
3190                 * @param float $x Abscissa of the upper-left corner of the rectangle
3191                 * @param float $y Ordinate of the upper-left corner of the rectangle
3192                 * @param float $w Width of the rectangle
3193                 * @param float $h Height of the rectangle
3194                 * @param mixed $link URL or identifier returned by AddLink()
3195                 * @param int $spaces number of spaces on the text to link
3196                 * @access public
3197                 * @since 1.5
3198                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
3199                 */
3200                 public function Link($x, $y, $w, $h, $link, $spaces=0) {
3201                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
3202                 }
3203                 
3204                 /**
3205                 * Puts a markup annotation on a rectangular area of the page.
3206                 * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
3207                 * @param float $x Abscissa of the upper-left corner of the rectangle
3208                 * @param float $y Ordinate of the upper-left corner of the rectangle
3209                 * @param float $w Width of the rectangle
3210                 * @param float $h Height of the rectangle
3211                 * @param string $text annotation text or alternate content
3212                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
3213                 * @param int $spaces number of spaces on the text to link
3214                 * @access public
3215                 * @since 4.0.018 (2008-08-06)
3216                 */
3217                 public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
3218                         // recalculate coordinates to account for graphic transformations
3219                         if (isset($this->transfmatrix)) {
3220                                 $maxid = count($this->transfmatrix) - 1;
3221                                 for ($i=$maxid; $i >= 0; $i--) {
3222                                         $ctm = $this->transfmatrix[$i];
3223                                         if (isset($ctm['a'])) {
3224                                                 $x = $x * $this->k;
3225                                                 $y = ($this->h - $y) * $this->k;
3226                                                 $w = $w * $this->k;
3227                                                 $h = $h * $this->k;
3228                                                 // top left
3229                                                 $xt = $x;
3230                                                 $yt = $y;
3231                                                 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3232                                                 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3233                                                 // top right
3234                                                 $xt = $x + $w;
3235                                                 $yt = $y;
3236                                                 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3237                                                 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3238                                                 // bottom left
3239                                                 $xt = $x;
3240                                                 $yt = $y - $h;
3241                                                 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3242                                                 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3243                                                 // bottom right
3244                                                 $xt = $x + $w;
3245                                                 $yt = $y - $h;
3246                                                 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3247                                                 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3248                                                 // new coordinates (rectangle area)
3249                                                 $x = min($x1, $x2, $x3, $x4);
3250                                                 $y = max($y1, $y2, $y3, $y4);
3251                                                 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
3252                                                 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
3253                                                 $x = $x / $this->k;
3254                                                 $y = $this->h - ($y / $this->k);
3255                                         }
3256                                 }
3257                         }
3258                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
3259                         if (($opt['Subtype'] == 'FileAttachment') AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
3260                                 $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + 100000));
3261                         }
3262                 }
3263
3264                 /**
3265                 * Embedd the attached files.
3266                 * @since 4.4.000 (2008-12-07)
3267                 * @access protected
3268                 * @see Annotation()
3269                 */
3270                 protected function _putEmbeddedFiles() {
3271                         reset($this->embeddedfiles);
3272                         foreach ($this->embeddedfiles as $filename => $filedata) {
3273                                 $data = file_get_contents($filedata['file']);
3274                                 $filter = '';
3275                                 if ($this->compress) {
3276                                         $data = gzcompress($data);
3277                                         $filter = ' /Filter /FlateDecode';
3278                                 }
3279                                 $this->offsets[$filedata['n']] = $this->bufferlen;
3280                                 $this->_out($filedata['n'].' 0 obj');
3281                                 $this->_out('<</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>');
3282                                 $this->_putstream($data);
3283                                 $this->_out('endobj');
3284                         }
3285                 }
3286                 
3287                 /**
3288                 * Prints a character string.
3289                 * The origin is on the left of the first charcter, on the baseline.
3290                 * This method allows to place a string precisely on the page.
3291                 * @param float $x Abscissa of the origin
3292                 * @param float $y Ordinate of the origin
3293                 * @param string $txt String to print
3294                 * @param int $stroke outline size in points (0 = disable)
3295                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
3296                 * @access public
3297                 * @since 1.0
3298                 * @deprecated deprecated since version 4.3.005 (2008-11-25)
3299                 * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
3300                 */
3301                 public function Text($x, $y, $txt, $stroke=0, $clip=false) {
3302                         //Output a string
3303                         if ($this->rtl) {
3304                                 // bidirectional algorithm (some chars may be changed affecting the line length)
3305                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $txt, $this->tmprtl);
3306                                 $l = $this->GetArrStringWidth($s);
3307                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
3308                         } else {
3309                                 $xr = $x;
3310                         }
3311                         $opt = '';
3312                         if (($stroke > 0) AND (!$clip)) {
3313                                 $opt .= '1 Tr '.intval($stroke).' w ';
3314                         } elseif (($stroke > 0) AND $clip) {
3315                                 $opt .= '5 Tr '.intval($stroke).' w ';
3316                         } elseif ($clip) {
3317                                 $opt .= '7 Tr ';
3318                         }
3319                         $s = sprintf('BT %.2F %.2F Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
3320                         if ($this->underline AND ($txt!='')) {
3321                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
3322                         }
3323                         if ($this->linethrough AND ($txt!='')) { 
3324                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt); 
3325                         }
3326                         if ($this->ColorFlag AND (!$clip)) {
3327                                 $s='q '.$this->TextColor.' '.$s.' Q';
3328                         }
3329                         $this->_out($s);
3330                 }
3331
3332                 /**
3333                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value. 
3334                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
3335                 * This method is called automatically and should not be called directly by the application.
3336                 * @return boolean
3337                 * @access public
3338                 * @since 1.4
3339                 * @see SetAutoPageBreak()
3340                 */
3341                 public function AcceptPageBreak() {
3342                         return $this->AutoPageBreak;
3343                 }
3344                 
3345                 /**
3346                 * Add page if needed.
3347                 * @param float $h Cell height. Default value: 0.
3348                 * @param mixed $y starting y position, leave empty for current position.
3349                 * @return boolean true in case of page break, false otherwise.
3350                 * @since 3.2.000 (2008-07-01)
3351                 * @access protected
3352                 */
3353                 protected function checkPageBreak($h=0, $y='') {
3354                         if ($this->empty_string($y)) {
3355                                 $y = $this->y;
3356                         }
3357                         if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
3358                                 //Automatic page break
3359                                 $x = $this->x;
3360                                 $this->AddPage($this->CurOrientation);
3361                                 $this->y = $this->tMargin;
3362                                 $oldpage = $this->page - 1;
3363                                 if ($this->rtl) {
3364                                         if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
3365                                                 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
3366                                         } else {
3367                                                 $this->x = $x;
3368                                         }
3369                                 } else {
3370                                         if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3371                                                 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
3372                                         } else {
3373                                                 $this->x = $x;
3374                                         }
3375                                 }
3376                                 return true;
3377                         }
3378                         return false;
3379                 }
3380
3381                 /**
3382                 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
3383                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3384                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
3385                 * @param float $h Cell height. Default value: 0.
3386                 * @param string $txt String to print. Default value: empty string.
3387                 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3388                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
3389                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
3390                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3391                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3392                 * @param mixed $link URL or identifier returned by AddLink().
3393                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3394                 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3395                 * @access public
3396                 * @since 1.0
3397                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
3398                 */
3399                 public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3400                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
3401                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3402                         if ($h < $min_cell_height) {
3403                                 $h = $min_cell_height;
3404                         }
3405                         $this->checkPageBreak($h);
3406                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height));
3407                 }
3408
3409                 /**
3410                 * Removes SHY characters from text.
3411                 * @param string $txt input string
3412                 * @return string without SHY characters.
3413                 * @access public
3414                 * @since (4.5.019) 2009-02-28
3415                 */
3416                 public function removeSHY($txt='') {
3417                         /*
3418                         * Unicode Data
3419                         * Name : SOFT HYPHEN, commonly abbreviated as SHY
3420                         * HTML Entity (decimal): &#173;
3421                         * HTML Entity (hex): &#xad;
3422                         * HTML Entity (named): &shy;
3423                         * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
3424                         * UTF-8 (hex): 0xC2 0xAD (c2ad)
3425                         * UTF-8 character: chr(194).chr(173)
3426                         */
3427                         $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
3428                         if (!$this->isunicode) {
3429                                 $txt = preg_replace('/([\\xad]{1})/', '', $txt);
3430                         }
3431                         return $txt;
3432                 }
3433                 
3434                 /**
3435                 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
3436                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
3437                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
3438                 * @param float $h Cell height. Default value: 0.
3439                 * @param string $txt String to print. Default value: empty string.
3440                 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3441                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
3442                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3443                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3444                 * @param mixed $link URL or identifier returned by AddLink().
3445                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3446                 * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3447                 * @access protected
3448                 * @since 1.0
3449                 * @see Cell()
3450                 */
3451                 protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false) {
3452                         $txt = $this->removeSHY($txt);
3453                         $rs = ''; //string to be returned
3454                         if (!$ignore_min_height) {
3455                                 $min_cell_height = $this->FontSize * $this->cell_height_ratio;
3456                                 if ($h < $min_cell_height) {
3457                                         $h = $min_cell_height;
3458                                 }
3459                         }
3460                         $k = $this->k;
3461                         if ($this->empty_string($w) OR ($w <= 0)) {
3462                                 if ($this->rtl) {
3463                                         $w = $this->x - $this->lMargin;
3464                                 } else {
3465                                         $w = $this->w - $this->rMargin - $this->x;
3466                                 }
3467                         }
3468                         $s = '';                        
3469                         if (($fill == 1) OR ($border == 1)) {
3470                                 if ($fill == 1) {
3471                                         $op = ($border == 1) ? 'B' : 'f';
3472                                 } else {
3473                                         $op = 'S';
3474                                 }
3475                                 if ($this->rtl) {
3476                                         $xk = (($this->x  - $w) * $k);
3477                                 } else {
3478                                         $xk = ($this->x * $k);
3479                                 }
3480                                 $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
3481                         }
3482                         if (is_string($border)) {
3483                                 $lm = ($this->LineWidth / 2);
3484                                 $x = $this->x;
3485                                 $y = $this->y;
3486                                 if (strpos($border,'L') !== false) {
3487                                         if ($this->rtl) {
3488                                                 $xk = ($x - $w) * $k;
3489                                         } else {
3490                                                 $xk = $x * $k;
3491                                         }
3492                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
3493                                 }
3494                                 if (strpos($border,'T') !== false) {
3495                                         if ($this->rtl) {
3496                                                 $xk = ($x - $w + $lm) * $k;
3497                                                 $xwk = ($x - $lm) * $k;
3498                                         } else {
3499                                                 $xk = ($x - $lm) * $k;
3500                                                 $xwk = ($x + $w + $lm) * $k;
3501                                         }
3502                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
3503                                 }
3504                                 if (strpos($border,'R') !== false) {
3505                                         if ($this->rtl) {
3506                                                 $xk = $x * $k;
3507                                         } else {
3508                                                 $xk = ($x + $w) * $k;
3509                                         }
3510                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
3511                                 }
3512                                 if (strpos($border,'B') !== false) {
3513                                         if ($this->rtl) {
3514                                                 $xk = ($x - $w + $lm) * $k;
3515                                                 $xwk = ($x - $lm) * $k;
3516                                         } else {
3517                                                 $xk = ($x - $lm) * $k;
3518                                                 $xwk = ($x + $w + $lm) * $k;
3519                                         }
3520                                         $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
3521                                 }
3522                         }
3523                         if ($txt != '') {
3524                                 // text lenght
3525                                 $width = $this->GetStringWidth($txt);
3526                                 // ratio between cell lenght and text lenght
3527                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
3528                                 
3529                                 // stretch text if required
3530                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
3531                                         if ($stretch > 2) {
3532                                                 // spacing
3533                                                 //Calculate character spacing in points
3534                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
3535                                                 //Set character spacing
3536                                                 $rs .= sprintf('BT %.2F Tc ET ', $char_space);
3537                                         } else {
3538                                                 // scaling
3539                                                 //Calculate horizontal scaling
3540                                                 $horiz_scale = $ratio * 100.0;
3541                                                 //Set horizontal scaling
3542                                                 $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
3543                                         }
3544                                         $align = '';
3545                                         $width = $w - (2 * $this->cMargin);
3546                                 } else {
3547                                         $stretch == 0;
3548                                 }
3549                                 if ($align == 'L') {
3550                                         if ($this->rtl) {
3551                                                 $dx = $w - $width - $this->cMargin;
3552                                         } else {
3553                                                 $dx = $this->cMargin;
3554                                         }
3555                                 } elseif ($align == 'R') {
3556                                         if ($this->rtl) {
3557                                                 $dx = $this->cMargin;
3558                                         } else {
3559                                                 $dx = $w - $width - $this->cMargin;
3560                                         }
3561                                 } elseif ($align == 'C') {
3562                                         $dx = ($w - $width) / 2;
3563                                 } elseif ($align == 'J') {
3564                                         if ($this->rtl) {
3565                                                 $dx = $w - $width - $this->cMargin;
3566                                         } else {
3567                                                 $dx = $this->cMargin;
3568                                         }
3569                                 } else {
3570                                         $dx = $this->cMargin;
3571                                 }
3572                                 if ($this->ColorFlag) {
3573                                         $s .= 'q '.$this->TextColor.' ';
3574                                 }
3575                                 $txt2 = $this->_escapetext($txt);
3576                                 if ($this->rtl) {
3577                                         $xdk = ($this->x - $dx - $width) * $k;
3578                                 } else {
3579                                         $xdk = ($this->x + $dx) * $k;
3580                                 }
3581                                 // Justification
3582                                 if ($align == 'J') {
3583                                         // count number of spaces
3584                                         $ns = substr_count($txt, ' ');
3585                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
3586                                                 // get string width without spaces
3587                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3588                                                 // calculate average space width
3589                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3590                                                 // set word position to be used with TJ operator
3591                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3592                                         } else {
3593                                                 // get string width
3594                                                 $width = $this->GetStringWidth($txt);
3595                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3596                                                 $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
3597                                         }
3598                                 }
3599                                 // calculate approximate position of the font base line
3600                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3601                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3602                                 // print text
3603                                 $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3604                                 if ($this->rtl) {
3605                                         $xdx = $this->x - $dx - $width;
3606                                 } else {
3607                                         $xdx = $this->x + $dx;
3608                                 }
3609                                 if ($this->underline)  {
3610                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3611                                 }
3612                                 if ($this->linethrough) { 
3613                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3614                                 }
3615                                 if ($this->ColorFlag) {
3616                                         $s .= ' Q';
3617                                 }
3618                                 if ($link) {
3619                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, substr_count($txt, chr(32)));
3620                                 }
3621                         }
3622                         // output cell
3623                         if ($s) {
3624                                 // output cell
3625                                 $rs .= $s;
3626                                 // reset text stretching
3627                                 if ($stretch > 2) {
3628                                         //Reset character horizontal spacing
3629                                         $rs .= ' BT 0 Tc ET';
3630                                 } elseif ($stretch > 0) {
3631                                         //Reset character horizontal scaling
3632                                         $rs .= ' BT 100 Tz ET';
3633                                 }
3634                         }
3635                         // reset word spacing
3636                         if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
3637                                 $rs .= ' BT 0 Tw ET';
3638                         }
3639                         $this->lasth = $h;
3640                         if ($ln > 0) {
3641                                 //Go to the beginning of the next line
3642                                 $this->y += $h;
3643                                 if ($ln == 1) {
3644                                         if ($this->rtl) {
3645                                                 $this->x = $this->w - $this->rMargin;
3646                                         } else {
3647                                                 $this->x = $this->lMargin;
3648                                         }
3649                                 }
3650                         } else {
3651                                 // go left or right by case
3652                                 if ($this->rtl) {
3653                                         $this->x -= $w;
3654                                 } else {
3655                                         $this->x += $w;
3656                                 }
3657                         }
3658                         $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
3659                         $rs = $gstyles.$rs;
3660                         return $rs;
3661                 }
3662
3663                 /**
3664                 * This method allows printing text with line breaks. 
3665                 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
3666                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3667                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3668                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3669                 * @param string $txt String to print
3670                 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3671                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
3672                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3673                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
3674                 * @param int $x x position in user units
3675                 * @param int $y y position in user units
3676                 * @param boolean $reseth if true reset the last cell height (default true).
3677                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3678                 * @param boolean $ishtml set to true if $txt is HTML content (default = false).
3679                 * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
3680                 * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
3681                 * @return int Return the number of cells or 1 for html mode.
3682                 * @access public
3683                 * @since 1.3
3684                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3685                 */
3686                 public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $addpage=true) {
3687                         if ($this->empty_string($this->lasth) OR $reseth) {
3688                                 //set row height
3689                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3690                         }
3691                         if (!$this->empty_string($y)) {
3692                                 $this->SetY($y);
3693                         } else {
3694                                 $y = $this->GetY();
3695                         }
3696                         // should not add page for each cell
3697             if ($addpage) {
3698                             // check for page break
3699                 $this->checkPageBreak($h);
3700             }
3701                         $y = $this->GetY();
3702                         // get current page number
3703                         $startpage = $this->page;
3704                         if (!$this->empty_string($x)) {
3705                                 $this->SetX($x);
3706                         } else {
3707                                 $x = $this->GetX();
3708                         }
3709                         if ($this->empty_string($w) OR ($w <= 0)) {
3710                                 if ($this->rtl) {
3711                                         $w = $this->x - $this->lMargin;
3712                                 } else {
3713                                         $w = $this->w - $this->rMargin - $this->x;
3714                                 }
3715                         }
3716                         // store original margin values
3717                         $lMargin = $this->lMargin;
3718                         $rMargin = $this->rMargin;
3719                         if ($this->rtl) {
3720                                 $this->SetRightMargin($this->w - $this->x);
3721                                 $this->SetLeftMargin($this->x - $w);
3722                         } else {
3723                                 $this->SetLeftMargin($this->x);
3724                                 $this->SetRightMargin($this->w - $this->x - $w);
3725                         }
3726                         $starty = $this->y;
3727                         if ($autopadding) {
3728                                 // Adjust internal padding
3729                                 if ($this->cMargin < ($this->LineWidth / 2)) {
3730                                         $this->cMargin = ($this->LineWidth / 2);
3731                                 }
3732                                 // Add top space if needed
3733                                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3734                                         $this->y += $this->LineWidth / 2;
3735                                 }
3736                                 // add top padding
3737                                 $this->y += $this->cMargin;
3738                         }
3739                         if ($ishtml) {
3740                                 // ******* Write HTML text
3741                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3742                                 $nl = 1;
3743                         } else {
3744                                 // ******* Write text
3745                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, false, $maxh);
3746                         }
3747                         if ($autopadding) {
3748                                 // add bottom padding
3749                                 $this->y += $this->cMargin;
3750                                 // Add bottom space if needed
3751                                 if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3752                                         $this->y += $this->LineWidth / 2;
3753                                 }
3754                         }
3755                         // Get end-of-text Y position
3756                         $currentY = $this->y;
3757                         // get latest page number
3758                         $endpage = $this->page;
3759                         // check if a new page has been created
3760                         if ($endpage > $startpage) {
3761                                 // design borders around HTML cells.
3762                                 for ($page=$startpage; $page <= $endpage; ++$page) {
3763                                         $this->setPage($page);
3764                                         if ($page == $startpage) {
3765                                                 $this->y = $starty; // put cursor at the beginning of cell on the first page
3766                                                 $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
3767                                                 $cborder = $this->getBorderMode($border, $position='start');
3768                                         } elseif ($page == $endpage) {
3769                                                 $this->y = $this->tMargin; // put cursor at the beginning of last page
3770                                                 $h = $currentY - $this->tMargin;
3771                                                 $cborder = $this->getBorderMode($border, $position='end');
3772                                         } else {
3773                                                 $this->y = $this->tMargin; // put cursor at the beginning of the current page
3774                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3775                                                 $cborder = $this->getBorderMode($border, $position='middle');
3776                                         }
3777                                         $nx = $x;
3778                                         // account for margin changes
3779                                         if ($page > $startpage) {
3780                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
3781                                                         $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
3782                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
3783                                                         $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
3784                                                 }
3785                                         }
3786                                         $this->SetX($nx);
3787                                         $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
3788                                         if ($cborder OR $fill) {
3789                                                 $pagebuff = $this->getPageBuffer($this->page);
3790                                                 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
3791                                                 $pend = substr($pagebuff, $this->intmrk[$this->page]);
3792                                                 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3793                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3794                                         }
3795                                 }
3796                         } else {
3797                                 $h = max($h, ($currentY - $y));
3798                                 // put cursor at the beginning of text
3799                                 $this->SetY($y); 
3800                                 $this->SetX($x);
3801                                 // design a cell around the text
3802                                 $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
3803                                 if ($border OR $fill) {
3804                                         if (end($this->transfmrk[$this->page]) !== false) {
3805                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
3806                                                 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
3807                                         } elseif ($this->InFooter) {
3808                                                 $pagemark = &$this->footerpos[$this->page];
3809                                         } else {
3810                                                 $pagemark = &$this->intmrk[$this->page];
3811                                         }
3812                                         $pagebuff = $this->getPageBuffer($this->page);
3813                                         $pstart = substr($pagebuff, 0, $pagemark);
3814                                         $pend = substr($pagebuff, $pagemark);
3815                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
3816                                         $pagemark += strlen($ccode."\n");
3817                                 }
3818                         }
3819                         // Get end-of-cell Y position
3820                         $currentY = $this->GetY();
3821                         // restore original margin values
3822                         $this->SetLeftMargin($lMargin);
3823                         $this->SetRightMargin($rMargin);
3824                         if ($ln > 0) {
3825                                 //Go to the beginning of the next line
3826                                 $this->SetY($currentY);
3827                                 if ($ln == 2) {
3828                                         $this->SetX($x + $w);
3829                                 }
3830                         } else {
3831                                 // go left or right by case
3832                                 $this->setPage($startpage);
3833                                 $this->y = $y;
3834                                 $this->SetX($x + $w);
3835                         }
3836                         return $nl;
3837                 }
3838
3839                 /**
3840                 * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
3841                 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3842                 * @param string multicell position: 'start', 'middle', 'end'
3843                 * @return border mode
3844                 * @access protected
3845                 * @since 4.4.002 (2008-12-09)
3846                 */
3847                 protected function getBorderMode($border, $position='start') {
3848                         if ((!$this->opencell) AND ($border == 1)) {
3849                                 return 1;
3850                         }
3851                         $cborder = '';
3852                         switch ($position) {
3853                                 case 'start': {
3854                                         if ($border == 1) {
3855                                                 $cborder = 'LTR';
3856                                         } else {
3857                                                 if (!(false === strpos($border, 'L'))) {
3858                                                         $cborder .= 'L';
3859                                                 }
3860                                                 if (!(false === strpos($border, 'T'))) {
3861                                                         $cborder .= 'T';
3862                                                 }
3863                                                 if (!(false === strpos($border, 'R'))) {
3864                                                         $cborder .= 'R';
3865                                                 }
3866                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3867                                                         $cborder .= 'B';
3868                                                 }
3869                                         }
3870                                         break;
3871                                 }
3872                                 case 'middle': {
3873                                         if ($border == 1) {
3874                                                 $cborder = 'LR';
3875                                         } else {
3876                                                 if (!(false === strpos($border, 'L'))) {
3877                                                         $cborder .= 'L';
3878                                                 }
3879                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3880                                                         $cborder .= 'T';
3881                                                 }
3882                                                 if (!(false === strpos($border, 'R'))) {
3883                                                         $cborder .= 'R';
3884                                                 }
3885                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
3886                                                         $cborder .= 'B';
3887                                                 }
3888                                         }
3889                                         break;
3890                                 }
3891                                 case 'end': {
3892                                         if ($border == 1) {
3893                                                 $cborder = 'LRB';
3894                                         } else {
3895                                                 if (!(false === strpos($border, 'L'))) {
3896                                                         $cborder .= 'L';
3897                                                 }
3898                                                 if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
3899                                                         $cborder .= 'T';
3900                                                 }
3901                                                 if (!(false === strpos($border, 'R'))) {
3902                                                         $cborder .= 'R';
3903                                                 }
3904                                                 if (!(false === strpos($border, 'B'))) {
3905                                                         $cborder .= 'B';
3906                                                 }
3907                                         }
3908                                         break;
3909                                 }
3910                                 default: {
3911                                         $cborder = $border;
3912                                         break;
3913                                 }
3914                         }
3915                         return $cborder;
3916                 }
3917
3918                 /**
3919                 * This method returns the estimated number of lines required to print the text.
3920                 * @param string $txt text to print
3921                 * @param float $w width of cell. If 0, they extend up to the right margin of the page.
3922                 * @return int Return the estimated number of lines.
3923                 * @access public
3924                 * @since 4.5.011
3925                 */
3926                 public function getNumLines($txt, $w=0) {
3927                         $lines = 0;
3928                         if ($this->empty_string($w) OR ($w <= 0)) {
3929                                 if ($this->rtl) {
3930                                         $w = $this->x - $this->lMargin;
3931                                 } else {
3932                                         $w = $this->w - $this->rMargin - $this->x;
3933                                 }
3934                         }
3935                         // max column width
3936                         $wmax = $w - (2 * $this->cMargin);
3937                         // remove carriage returns
3938                         $txt = str_replace("\r", '', $txt);
3939                         // remove last newline (if any)
3940                         if (substr($txt,-1) == "\n") {
3941                                 $txt = substr($txt, 0, -1);
3942                         }
3943                         // divide text in blocks
3944                         $txtblocks = explode("\n", $txt);
3945                         // for each block;
3946                         foreach ($txtblocks as $block) {
3947                                 // estimate the number of lines
3948                                 $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
3949                         }
3950                         return $lines;
3951                 }
3952                         
3953                 /**
3954                 * This method prints text from the current position.<br />
3955                 * @param float $h Line height
3956                 * @param string $txt String to print
3957                 * @param mixed $link URL or identifier returned by AddLink()
3958                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3959                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3960                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3961                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3962                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3963                 * @param boolean $firstblock if true the string is the starting of a line.
3964                 * @param float $maxh maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
3965                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3966                 * @access public
3967                 * @since 1.5
3968                 */
3969                 public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
3970                         if (strlen($txt) == 0) {
3971                                 $txt = ' ';
3972                         }
3973                         // remove carriage returns
3974                         $s = str_replace("\r", '', $txt);
3975                         
3976                         // check if string contains arabic text
3977                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3978                                 $arabic = true;
3979                         } else {
3980                                 $arabic = false;
3981                         }
3982                         // check if string contains RTL text
3983                         if ($arabic OR $this->tmprtl OR preg_match(K_RE_PATTERN_RTL, $txt)) {
3984                                 $rtlmode = true;
3985                         } else {
3986                                 $rtlmode = false;
3987                         }
3988                         // get a char width
3989                         $chrwidth = $this->GetCharWidth('.');
3990                         // get array of unicode values
3991                         $chars = $this->UTF8StringToArray($s);
3992                         // get array of chars
3993                         $uchars = $this->UTF8ArrayToUniArray($chars);
3994                         // get the number of characters
3995                         $nb = count($chars);
3996                         // replacement for SHY character (minus symbol)
3997                         $shy_replacement = 45;
3998                         $shy_replacement_char = $this->unichr($shy_replacement);
3999                         // widht for SHY replacement
4000                         $shy_replacement_width = $this->GetCharWidth($shy_replacement);
4001                         // store current position
4002                         $prevx = $this->x;
4003                         $prevy = $this->y;
4004                         // max Y
4005                         $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
4006                         // calculate remaining line width ($w)
4007                         if ($this->rtl) {
4008                                 $w = $this->x - $this->lMargin;
4009                         } else {
4010                                 $w = $this->w - $this->rMargin - $this->x;
4011                         }
4012                         // max column width
4013                         $wmax = $w - (2 * $this->cMargin);
4014                         if ($chrwidth > $wmax) {
4015                                 // a single character do not fit on column
4016                                 return '';
4017                         }
4018                         $i = 0; // character position
4019                         $j = 0; // current starting position
4020                         $sep = -1; // position of the last blank space
4021                         $shy = false; // true if the last blank is a soft hypen (SHY)
4022                         $l = 0; // current string lenght
4023                         $nl = 0; //number of lines
4024                         $linebreak = false;
4025                         // for each character
4026                         while ($i < $nb) {
4027                                 if (($maxh > 0) AND ($this->y >= $maxy) ) {
4028                                         $firstline = true;
4029                                 }
4030                                 //Get the current character
4031                                 $c = $chars[$i];
4032                                 if ($c == 10) { // 10 = "\n" = new line
4033                                         //Explicit line break
4034                                         if ($align == 'J') {
4035                                                 if ($this->rtl) {
4036                                                         $talign = 'R';
4037                                                 } else {
4038                                                         $talign = 'L';
4039                                                 }
4040                                         } else {
4041                                                 $talign = $align;
4042                                         }
4043                                         $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4044                                         if ($firstline) {
4045                                                 $startx = $this->x;
4046                                                 $tmparr = array_slice($chars, $j, $i);
4047                                                 if ($rtlmode) {
4048                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4049                                                 }
4050                                                 $linew = $this->GetArrStringWidth($tmparr);
4051                                                 unset($tmparr);
4052                                                 if ($this->rtl) {
4053                                                         $this->endlinex = $startx - $linew;
4054                                                 } else {
4055                                                         $this->endlinex = $startx + $linew;
4056                                                 }
4057                                                 $w = $linew;
4058                                                 $tmpcmargin = $this->cMargin;
4059                                                 if ($maxh == 0) {
4060                                                         $this->cMargin = 0;
4061                                                 }
4062                                         }
4063                                         $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
4064                                         unset($tmpstr);
4065                                         if ($firstline) {
4066                                                 $this->cMargin = $tmpcmargin;
4067                                                 return ($this->UniArrSubString($uchars, $i));
4068                                         }
4069                                         ++$nl;
4070                                         $j = $i + 1;
4071                                         $l = 0;
4072                                         $sep = -1;
4073                                         $shy = false;
4074                                         // account for margin changes
4075                                         if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4076                                                 // AcceptPageBreak() may be overriden on extended classed to include margin changes
4077                                                 $this->AcceptPageBreak();
4078                                         }
4079                                         $w = $this->getRemainingWidth();
4080                                         $wmax = $w - (2 * $this->cMargin);
4081                                 } else {
4082                                         // 160 is the non-breaking space.
4083                                         // 173 is SHY (Soft Hypen).
4084                                         // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
4085                                         // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
4086                                         // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
4087                                         if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
4088                                                 // update last blank space position
4089                                                 $sep = $i;
4090                                                 // check if is a SHY
4091                                                 if ($c == 173) {
4092                                                         $shy = true;
4093                                                 } else {
4094                                                         $shy = false;
4095                                                 }
4096                                         }
4097                                         // update string length
4098                                         if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
4099                                                 // with bidirectional algorithm some chars may be changed affecting the line length
4100                                                 // *** very slow ***
4101                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), '', $this->tmprtl));
4102                                         } else {
4103                                                 $l += $this->GetCharWidth($c);
4104                                         }
4105                                         if (!$stretch && (($l > $wmax) OR ($shy AND (($l + $shy_replacement_width) > $wmax))) ) {
4106                                                 // we have reached the end of column
4107                                                 if ($sep == -1) {
4108                                                         // check if the line was already started
4109                                                         if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
4110                                                                 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
4111                                                                 // print a void cell and go to next line
4112                                                                 $this->Cell($w, $h, '', 0, 1);
4113                                                                 $linebreak = true;
4114                                                                 if ($firstline) {
4115                                                                         return ($this->UniArrSubString($uchars, $j));
4116                                                                 }
4117                                                         } else {
4118                                                                 // truncate the word because do not fit on column
4119                                                                 $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4120                                                                 if ($firstline) {
4121                                                                         $startx = $this->x;
4122                                                                         $tmparr = array_slice($chars, $j, $i);
4123                                                                         if ($rtlmode) {
4124                                                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4125                                                                         }
4126                                                                         $linew = $this->GetArrStringWidth($tmparr);
4127                                                                         unset($tmparr);
4128                                                                         if ($this->rtl) {
4129                                                                                 $this->endlinex = $startx - $linew;
4130                                                                         } else {
4131                                                                                 $this->endlinex = $startx + $linew;
4132                                                                         }
4133                                                                         $w = $linew;
4134                                                                         $tmpcmargin = $this->cMargin;
4135                                                                         if ($maxh == 0) {
4136                                                                                 $this->cMargin = 0;
4137                                                                         }
4138                                                                 }
4139                                                                 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
4140                                                                 unset($tmpstr);
4141                                                                 if ($firstline) {
4142                                                                         $this->cMargin = $tmpcmargin;
4143                                                                         return ($this->UniArrSubString($uchars, $i));
4144                                                                 }
4145                                                                 $j = $i;
4146                                                                 --$i;
4147                                                         }       
4148                                                 } else {
4149                                                         // word wrapping
4150                                                         if ($this->rtl AND (!$firstblock)) {
4151                                                                 $endspace = 1;
4152                                                         } else {
4153                                                                 $endspace = 0;
4154                                                         }
4155                                                         if ($shy) {
4156                                                                 // add hypen (minus symbol) at the end of the line
4157                                                                 $shy_width = $shy_replacement_width;
4158                                                                 if ($this->rtl) {
4159                                                                         $shy_char_left = $shy_replacement_char;
4160                                                                         $shy_char_right = '';
4161                                                                 } else {
4162                                                                         $shy_char_left = '';
4163                                                                         $shy_char_right = $shy_replacement_char;
4164                                                                 }
4165                                                         } else {
4166                                                                 $shy_width = 0;
4167                                                                 $shy_char_left = '';
4168                                                                 $shy_char_right = '';
4169                                                         }
4170                                                         $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
4171                                                         if ($firstline) {
4172                                                                 $startx = $this->x;
4173                                                                 $tmparr = array_slice($chars, $j, ($sep + $endspace));
4174                                                                 if ($rtlmode) {
4175                                                                         $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4176                                                                 }
4177                                                                 $linew = $this->GetArrStringWidth($tmparr);
4178                                                                 unset($tmparr);
4179                                                                 if ($this->rtl) {
4180                                                                         $this->endlinex = $startx - $linew - $shy_width;
4181                                                                 } else {
4182                                                                         $this->endlinex = $startx + $linew + $shy_width;
4183                                                                 }
4184                                                                 $w = $linew;
4185                                                                 $tmpcmargin = $this->cMargin;
4186                                                                 if ($maxh == 0) {
4187                                                                         $this->cMargin = 0;
4188                                                                 }
4189                                                         }
4190                                                         // print the line
4191                                                         $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
4192                                                         unset($tmpstr);
4193                                                         if ($firstline) {
4194                                                                 // return the remaining text
4195                                                                 $this->cMargin = $tmpcmargin;
4196                                                                 return ($this->UniArrSubString($uchars, ($sep + $endspace)));
4197                                                         }
4198                                                         $i = $sep;
4199                                                         $sep = -1;
4200                                                         $shy = false;
4201                                                         $j = ($i+1);
4202                                                 }
4203                                                 // account for margin changes
4204                                                 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4205                                                         // AcceptPageBreak() may be overriden on extended classed to include margin changes
4206                                                         $this->AcceptPageBreak();
4207                                                 }
4208                                                 $w = $this->getRemainingWidth();
4209                                                 $wmax = $w - (2 * $this->cMargin);
4210                                                 if ($linebreak) {
4211                                                         $linebreak = false;
4212                                                 } else {
4213                                                         ++$nl;
4214                                                         $l = 0;
4215                                                 }
4216                                         }
4217                                 }
4218                                 ++$i;
4219                         } // end while i < nb
4220                         // print last substring (if any)
4221                         if ($l > 0) {
4222                             if (!$stretch)
4223                             {
4224                                 switch ($align) {
4225                                         case 'J':
4226                                         case 'C': {
4227                                                 $w = $w;
4228                                                 break;
4229                                         }
4230                                         case 'L': {
4231                                                 if ($this->rtl) {
4232                                                         $w = $w;
4233                                                 } else {
4234                                                         $w = $l;
4235                                                 }
4236                                                 break;
4237                                         }
4238                                         case 'R': {
4239                                                 if ($this->rtl) {
4240                                                         $w = $l;
4241                                                 } else {
4242                                                         $w = $w;
4243                                                 }
4244                                                 break;
4245                                         }
4246                                         default: {
4247                                                 $w = $l;
4248                                                 break;
4249                                         }
4250                                 }
4251                             }
4252                                 $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
4253                                 if ($firstline) {
4254                                         $startx = $this->x;
4255                                         $tmparr = array_slice($chars, $j, $nb);
4256                                         if ($rtlmode) {
4257                                                 $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4258                                         }
4259                                         $linew = $this->GetArrStringWidth($tmparr);
4260                                         unset($tmparr);
4261                                         if ($this->rtl) {
4262                                                 $this->endlinex = $startx - $linew;
4263                                         } else {
4264                                                 $this->endlinex = $startx + $linew;
4265                                         }
4266                                         if (!$stretch)
4267                                             $w = $linew;
4268                                         $tmpcmargin = $this->cMargin;
4269                                         if ($maxh == 0) {
4270                                                 $this->cMargin = 0;
4271                                         }
4272                                 }
4273                                 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
4274                                 unset($tmpstr);
4275                                 if ($firstline) {
4276                                         $this->cMargin = $tmpcmargin;
4277                                         return ($this->UniArrSubString($uchars, $nb));
4278                                 }
4279                                 ++$nl;
4280                         }
4281                         if ($firstline) {
4282                                 return '';
4283                         }
4284                         return $nl;
4285                 }
4286                                 
4287                 /**
4288                 * Returns the remaining width between the current position and margins.
4289                 * @return int Return the remaining width
4290                 * @access protected
4291                 */
4292                 protected function getRemainingWidth() {
4293                         if ($this->rtl) {
4294                                 return ($this->x - $this->lMargin);
4295                         } else {
4296                                 return ($this->w - $this->rMargin - $this->x);
4297                         }
4298                 }
4299
4300                 /**
4301                 * Extract a slice of the $strarr array and return it as string.
4302                 * @param string $strarr The input array of characters.
4303                 * @param int $start the starting element of $strarr.
4304                 * @param int $end first element that will not be returned.
4305                 * @return Return part of a string
4306                 * @access public
4307                 */
4308                 public function UTF8ArrSubString($strarr, $start='', $end='') {
4309                         if (strlen($start) == 0) {
4310                                 $start = 0;
4311                         }
4312                         if (strlen($end) == 0) {
4313                                 $end = count($strarr);
4314                         }
4315                         $string = '';
4316                         for ($i=$start; $i < $end; ++$i) {
4317                                 $string .= $this->unichr($strarr[$i]);
4318                         }
4319                         return $string;
4320                 }
4321
4322                 /**
4323                 * Extract a slice of the $uniarr array and return it as string.
4324                 * @param string $uniarr The input array of characters.
4325                 * @param int $start the starting element of $strarr.
4326                 * @param int $end first element that will not be returned.
4327                 * @return Return part of a string
4328                 * @access public
4329                 * @since 4.5.037 (2009-04-07)
4330                 */
4331                 public function UniArrSubString($uniarr, $start='', $end='') {
4332                         if (strlen($start) == 0) {
4333                                 $start = 0;
4334                         }
4335                         if (strlen($end) == 0) {
4336                                 $end = count($uniarr);
4337                         }
4338                         $string = '';
4339                         for ($i=$start; $i < $end; ++$i) {
4340                                 $string .= $uniarr[$i];
4341                         }
4342                         return $string;
4343                 }
4344
4345                 /**
4346                 * Convert an array of UTF8 values to array of unicode characters
4347                 * @param string $ta The input array of UTF8 values.
4348                 * @return Return array of unicode characters
4349                 * @access public
4350                 * @since 4.5.037 (2009-04-07)
4351                 */
4352                 public function UTF8ArrayToUniArray($ta) {
4353                         return array_map(array($this, 'unichr'), $ta);
4354                 }
4355                 
4356                 /**
4357                 * Returns the unicode caracter specified by UTF-8 code
4358                 * @param int $c UTF-8 code
4359                 * @return Returns the specified character.
4360                 * @author Miguel Perez, Nicola Asuni
4361                 * @access public
4362                 * @since 2.3.000 (2008-03-05)
4363                 */
4364                 public function unichr($c) {
4365                         if (!$this->isunicode) {
4366                                 return chr($c);
4367                         } elseif ($c <= 0x7F) {
4368                                 // one byte
4369                                 return chr($c);
4370                         } elseif ($c <= 0x7FF) {
4371                                 // two bytes
4372                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
4373                         } elseif ($c <= 0xFFFF) {
4374                                 // three bytes
4375                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4376                         } elseif ($c <= 0x10FFFF) {
4377                                 // four bytes
4378                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
4379                         } else {
4380                                 return '';
4381                         }
4382                 }
4383                 
4384                 /**
4385                 * Puts an image in the page. 
4386                 * The upper-left corner must be given. 
4387                 * The dimensions can be specified in different ways:<ul>
4388                 * <li>explicit width and height (expressed in user unit)</li>
4389                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
4390                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
4391                 * 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;
4392                 * The format can be specified explicitly or inferred from the file extension.<br />
4393                 * It is possible to put a link on the image.<br />
4394                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
4395                 * @param string $file Name of the file containing the image.
4396                 * @param float $x Abscissa of the upper-left corner.
4397                 * @param float $y Ordinate of the upper-left corner.
4398                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
4399                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
4400                 * @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.
4401                 * @param mixed $link URL or identifier returned by AddLink().
4402                 * @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>
4403                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
4404                 * @param int $dpi dot-per-inch resolution used on resize
4405                 * @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>
4406                 * @param boolean $ismask true if this image is a mask, false otherwise
4407                 * @param mixed $imgmask image object returned by this function or false
4408                 * @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>
4409                 * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box.
4410                 * @return image information
4411                 * @access public
4412                 * @since 1.1
4413         *
4414         * peter d: Upgrading tcpdf to the latest version causes the whole thing to blow up since we have modifications to the original source as well as sugarpdf which
4415         * also extend from this. The only option is to add a workaround for some of the bugs in this function.
4416                 */
4417                 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) {
4418                         if ($x === '') {
4419                                 $x = $this->x;
4420                         }
4421                         if ($y === '') {
4422                                 $y = $this->y;
4423                         }
4424                         // get image dimensions
4425                         $imsize = @getimagesize($file);
4426                         if ($imsize === FALSE) {
4427                                 // encode spaces on filename
4428                                 $file = str_replace(' ', '%20', $file);
4429                                 $imsize = @getimagesize($file);
4430                                 if ($imsize === FALSE) {
4431                                         $this->Error('[Image] No such file or directory in '.$file);
4432                                 }
4433                         }
4434                         // get original image width and height in pixels
4435                         list($pixw, $pixh) = $imsize;
4436                         // calculate image width and height on document
4437                         if (($w <= 0) AND ($h <= 0)) {
4438                                 // convert image size to document unit
4439                                 $w = $this->pixelsToUnits($pixw);
4440                                 $h = $this->pixelsToUnits($pixh);
4441                         } elseif ($w <= 0) {
4442                                 $w = $h * $pixw / $pixh;
4443                         } elseif ($h <= 0) {
4444                                 $h = $w * $pixh / $pixw;
4445                         } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
4446                                 // scale image dimensions proportionally to fit within the ($w, $h) box
4447                 // NOTE: This section doesn't actually work very well, use the resize = true case I added.
4448                                 if ((($w * $pixh) / ($h * $pixw)) < 1) {
4449                                         $h = $w * $pixh / $pixw;
4450                                 } else {
4451                                         $w = $h * $pixw / $pixh;
4452                                 }
4453                         } else if ($resize) { // Added resize case
4454                 // Note: The issue here is that $w and $h represent abstract sizes, we pass it in as pixels,
4455                 // but tcpdf treats it as document units.
4456                 $wratio = $pixw / $w;
4457                 $hratio = $pixh / $h;
4458
4459                 // Check if our image exceeds the boundaries of $w and $h
4460                 if ($wratio > 1 AND $hratio > 1) {
4461                     $favoredRatio = ($wratio > $hratio) ? $wratio : $hratio;
4462                 } else if ($wratio > 1) {
4463                     $favoredRatio = $wratio;
4464                 } else if ($hratio > 1) {
4465                     $favoredRatio = $hratio;
4466                 } else {
4467                     $favoredRatio = 1;
4468                     $resize = false;
4469                 }
4470
4471                 // Calculate the new boundaries that also happen to fit the box..
4472                 // Dividing by the unit conversion $this->k seems to make the size not blow up
4473                 // later down in the code.
4474                 $w = $pixw / $favoredRatio / $this->k;
4475                 $h = $pixh / $favoredRatio / $this->k;
4476             }
4477                         // calculate new minimum dimensions in pixels
4478                         $neww = round($w * $this->k * $dpi / $this->dpi);
4479                         $newh = round($h * $this->k * $dpi / $this->dpi);
4480
4481                         // check if resize is necessary (resize is used only to reduce the image)
4482
4483             // - commmented out by pete d.
4484             // this is not a good way of checking for resize. and it might overwrite resize if the flag is enabled.
4485                         // if (($neww * $newh) >= ($pixw * $pixh)) {
4486                         //      $resize = false;
4487                         // }
4488
4489                         // check if image has been already added on document
4490                         if (!in_array($file, $this->imagekeys)) {
4491                                 //First use of image, get info
4492                                 if ($type == '') {
4493                                         $fileinfo = pathinfo($file);
4494                                         if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
4495                                                 $type = $fileinfo['extension'];
4496                                         } else {
4497                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
4498                                         }
4499                                 }
4500                                 $type = strtolower($type);
4501                                 if ($type == 'jpg') {
4502                                         $type = 'jpeg';
4503                                 }
4504                                 $mqr = get_magic_quotes_runtime();
4505                                 //set_magic_quotes_runtime(0);
4506                                 // Specific image handlers
4507                                 $mtd = '_parse'.$type;
4508                                 // GD image handler function
4509                                 $gdfunction = 'imagecreatefrom'.$type;
4510                                 $info = false;
4511                                 if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
4512                                         // TCPDF image functions
4513                                         $info = $this->$mtd($file);
4514                                         if ($info == 'pngalpha') {
4515                                                 return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
4516                                         }
4517                                 } 
4518                                 if (!$info) {
4519                                         if (function_exists($gdfunction)) {
4520                                                 // GD library
4521                                                 $img = $gdfunction($file);
4522                                                 if ($resize) {
4523                                                         $imgr = imagecreatetruecolor($neww, $newh);
4524                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh); 
4525                                                         $info = $this->_toJPEG($imgr);
4526                                                 } else {
4527                                                         $info = $this->_toJPEG($img);
4528                                                 }
4529                                         } elseif (extension_loaded('imagick')) {
4530                                                 // ImageMagick library
4531                                                 $img = new Imagick();
4532                                                 $img->readImage($file);
4533                                                 if ($resize) {
4534                                                         $img->resizeImage($neww, $newh, 10, 1, false);
4535                                                 }
4536                                                 $img->setCompressionQuality($this->jpeg_quality);
4537                                                 $img->setImageFormat('jpeg');
4538                                                 $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4539                                                 $img->writeImage($tempname);
4540                                                 $info = $this->_parsejpeg($tempname);
4541                                                 unlink($tempname);
4542                                                 $img->destroy();
4543                                         } 
4544                                         else if ($type == 'jpeg') {
4545                                                 $info = $this->_parsejpeg($file);
4546                                         }                                       
4547                                         else {
4548                                                 return;
4549                                         }
4550                                 }
4551                                 if ($info === false) {
4552                                         //If false, we cannot process image
4553                                         return;
4554                                 }
4555                                 //set_magic_quotes_runtime($mqr);
4556                                 if ($ismask) {
4557                                         // force grayscale
4558                                         $info['cs'] = 'DeviceGray';
4559                                 }
4560                                 $info['i'] = $this->numimages + 1;
4561                                 if ($imgmask !== false) {
4562                                         $info['masked'] = $imgmask;
4563                                 }
4564                                 // add image to document
4565                                 $this->setImageBuffer($file, $info);
4566                         } else {
4567                                 $info = $this->getImageBuffer($file);
4568                         }
4569                         // Check whether we need a new page first as this does not fit
4570                         if ($this->checkPageBreak($h, $y)) {
4571                                 $y = $this->GetY() + $this->cMargin;
4572                         }
4573                         // set bottomcoordinates
4574                         $this->img_rb_y = $y + $h;
4575                         // set alignment
4576                         if ($this->rtl) {
4577                                 if ($palign == 'L') {
4578                                         $ximg = $this->lMargin;
4579                                         // set right side coordinate
4580                                         $this->img_rb_x = $ximg + $w;
4581                                 } elseif ($palign == 'C') {
4582                                         $ximg = ($this->w - $x - $w) / 2;
4583                                         // set right side coordinate
4584                                         $this->img_rb_x = $ximg + $w;
4585                                 } else {
4586                                         $ximg = $this->w - $x - $w;
4587                                         // set left side coordinate
4588                                         $this->img_rb_x = $ximg;
4589                                 }
4590                         } else {
4591                                 if ($palign == 'R') {
4592                                         $ximg = $this->w - $this->rMargin - $w;
4593                                         // set left side coordinate
4594                                         $this->img_rb_x = $ximg;
4595                                 } elseif ($palign == 'C') {
4596                                         $ximg = ($this->w - $x - $w) / 2;
4597                                         // set right side coordinate
4598                                         $this->img_rb_x = $ximg + $w;
4599                                 } else {
4600                                         $ximg = $x;
4601                                         // set right side coordinate
4602                                         $this->img_rb_x = $ximg + $w;
4603                                 }
4604                         }
4605                         if ($ismask) {
4606                                 // embed hidden, ouside the canvas
4607                                 $xkimg = ($this->pagedim[$this->page]['w'] + 10);
4608                         } else {
4609                                 $xkimg = $ximg * $this->k;
4610                         }
4611                         $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']));
4612                         if (!empty($border)) {
4613                                 $bx = $x;
4614                                 $by = $y;
4615                                 $this->x = $ximg;
4616                                 $this->y = $y;
4617                                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
4618                                 $this->x = $bx;
4619                                 $this->y = $by;
4620                         }
4621                         if ($link) {
4622                                 $this->Link($ximg, $y, $w, $h, $link, 0);
4623                         }
4624                         // set pointer to align the successive text/objects
4625                         switch($align) {
4626                                 case 'T': {
4627                                         $this->y = $y;
4628                                         $this->x = $this->img_rb_x;
4629                                         break;
4630                                 }
4631                                 case 'M': {
4632                                         $this->y = $y + round($h/2);
4633                                         $this->x = $this->img_rb_x;
4634                                         break;
4635                                 }
4636                                 case 'B': {
4637                                         $this->y = $this->img_rb_y;
4638                                         $this->x = $this->img_rb_x;
4639                                         break;
4640                                 }
4641                                 case 'N': {
4642                                         $this->SetY($this->img_rb_y);
4643                                         break;
4644                                 }
4645                                 default:{
4646                                         break;
4647                                 }
4648                         }
4649                         $this->endlinex = $this->img_rb_x;
4650                         return $info['i'];
4651                 }
4652                                 
4653                 /**
4654                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
4655                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
4656                 * @param string $file Image file name.
4657                 * @param image $image Image object.
4658                 * return image JPEG image object.
4659                 * @access protected
4660                 */
4661                 protected function _toJPEG($image) {
4662                         $tempname = tempnam(K_PATH_CACHE, 'jpg_');
4663                         imagejpeg($image, $tempname, $this->jpeg_quality);
4664                         imagedestroy($image);
4665                         $retvars = $this->_parsejpeg($tempname);
4666                         // tidy up by removing temporary image
4667                         unlink($tempname);
4668                         return $retvars;
4669                 }
4670                 
4671                 /**
4672                 * Extract info from a JPEG file without using the GD library.
4673                 * @param string $file image file to parse
4674                 * @return array structure containing the image data
4675                 * @access protected
4676                 */
4677                 protected function _parsejpeg($file) {
4678                         $a = getimagesize($file);
4679                         if (empty($a)) {
4680                                 $this->Error('Missing or incorrect image file: '.$file);
4681                         }
4682                         if ($a[2] != 2) {
4683                                 $this->Error('Not a JPEG file: '.$file);
4684                         }
4685                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
4686                                 $colspace = 'DeviceRGB';
4687                         } elseif ($a['channels'] == 4) {
4688                                 $colspace = 'DeviceCMYK';
4689                         } else {
4690                                 $colspace = 'DeviceGray';
4691                         }
4692                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
4693                         $data = file_get_contents($file);
4694                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
4695                 }
4696
4697                 /**
4698                 * Extract info from a PNG file without using the GD library.
4699                 * @param string $file image file to parse
4700                 * @return array structure containing the image data
4701                 * @access protected
4702                 */
4703                 protected function _parsepng($file) {
4704                         $f = fopen($file, 'rb');
4705                         if ($f === false) {
4706                                 $this->Error('Can\'t open image file: '.$file);
4707                         }
4708                         //Check signature
4709                         if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
4710                                 $this->Error('Not a PNG file: '.$file);
4711                         }
4712                         //Read header chunk
4713                         fread($f, 4);
4714                         if (fread($f, 4) != 'IHDR') {
4715                                 $this->Error('Incorrect PNG file: '.$file);
4716                         }
4717                         $w = $this->_freadint($f);
4718                         $h = $this->_freadint($f);
4719                         $bpc = ord(fread($f, 1));
4720                         if ($bpc > 8) {
4721                                 //$this->Error('16-bit depth not supported: '.$file);
4722                                 fclose($f);
4723                                 return false;
4724                         }
4725                         $ct = ord(fread($f, 1));
4726                         if ($ct == 0) {
4727                                 $colspace = 'DeviceGray';
4728                         } elseif ($ct == 2) {
4729                                 $colspace = 'DeviceRGB';
4730                         } elseif ($ct == 3) {
4731                                 $colspace = 'Indexed';
4732                         } else {
4733                                 // alpha channel
4734                                 fclose($f);
4735                                 return 'pngalpha';
4736                         }
4737                         if (ord(fread($f, 1)) != 0) {
4738                                 //$this->Error('Unknown compression method: '.$file);
4739                                 fclose($f);
4740                                 return false;
4741                         }
4742                         if (ord(fread($f, 1)) != 0) {
4743                                 //$this->Error('Unknown filter method: '.$file);
4744                                 fclose($f);
4745                                 return false;
4746                         }
4747                         if (ord(fread($f, 1)) != 0) {
4748                                 //$this->Error('Interlacing not supported: '.$file);
4749                                 fclose($f);
4750                                 return false;
4751                         }
4752                         fread($f, 4);
4753                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
4754                         //Scan chunks looking for palette, transparency and image data
4755                         $pal = '';
4756                         $trns = '';
4757                         $data = '';
4758                         do {
4759                                 $n = $this->_freadint($f);
4760                                 $type = fread($f, 4);
4761                                 if ($type == 'PLTE') {
4762                                         //Read palette
4763                                         $pal = $this->rfread($f, $n);
4764                                         fread($f, 4);
4765                                 } elseif ($type == 'tRNS') {
4766                                         //Read transparency info
4767                                         $t = $this->rfread($f, $n);
4768                                         if ($ct == 0) {
4769                                                 $trns = array(ord(substr($t, 1, 1)));
4770                                         } elseif ($ct == 2) {
4771                                                 $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
4772                                         } else {
4773                                                 $pos = strpos($t, chr(0));
4774                                                 if ($pos !== false) {
4775                                                         $trns = array($pos);
4776                                                 }
4777                                         }
4778                                         fread($f, 4);
4779                                 } elseif ($type == 'IDAT') {
4780                                         //Read image data block
4781                                         $data .= $this->rfread($f, $n);
4782                                         fread($f, 4);
4783                                 } elseif ($type == 'IEND') {
4784                                         break;
4785                                 } else {
4786                                         $this->rfread($f, $n + 4);
4787                                 }
4788                         } while ($n);
4789                         if (($colspace == 'Indexed') AND (empty($pal))) {
4790                                 //$this->Error('Missing palette in '.$file);
4791                                 fclose($f);
4792                                 return false;
4793                         }
4794                         fclose($f);
4795                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
4796                 }
4797
4798                 /**
4799                 * Binary-safe and URL-safe file read.
4800                 * 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.
4801                 * @param resource $handle
4802                 * @param int $length
4803                 * @return Returns the read string or FALSE in case of error.
4804                 * @author Nicola Asuni
4805                 * @access protected
4806                 * @since 4.5.027 (2009-03-16)
4807                 */
4808                 protected function rfread($handle, $length) {
4809                         $data = fread($handle, $length);
4810                         if ($data === false) {
4811                                 return false;
4812                         }
4813                         $rest = $length - strlen($data);
4814                         if ($rest > 0) {
4815                                 $data .= $this->rfread($handle, $rest);
4816                         }
4817                         return $data;
4818                 }
4819
4820                 /**
4821                 * Extract info from a PNG image with alpha channel using the GD library.
4822                 * @param string $file Name of the file containing the image.
4823                 * @param float $x Abscissa of the upper-left corner.
4824                 * @param float $y Ordinate of the upper-left corner.
4825                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
4826                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
4827                 * @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.
4828                 * @param mixed $link URL or identifier returned by AddLink().
4829                 * @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>
4830                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
4831                 * @param int $dpi dot-per-inch resolution used on resize
4832                 * @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>
4833                 * @author Valentin Schmidt, Nicola Asuni
4834                 * @access protected
4835                 * @since 4.3.007 (2008-12-04)
4836                 * @see Image()
4837                 */
4838                 protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
4839                         // get image size
4840                         list($wpx, $hpx) = getimagesize($file);
4841                         // generate images
4842                         $img = imagecreatefrompng($file);
4843                         $imgalpha = imagecreate($wpx, $hpx);
4844                         // generate gray scale pallete
4845                         for ($c = 0; $c < 256; ++$c) {
4846                                 ImageColorAllocate($imgalpha, $c, $c, $c);
4847                         }
4848                         // extract alpha channel
4849                         for ($xpx = 0; $xpx < $wpx; ++$xpx) {
4850                                 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
4851                                         $colorindex = imagecolorat($img, $xpx, $ypx);
4852                                         $col = imagecolorsforindex($img, $colorindex);
4853                                         imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
4854                                 }
4855                         }
4856                         // create temp alpha file
4857                         $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
4858                         imagepng($imgalpha, $tempfile_alpha);
4859                         imagedestroy($imgalpha);
4860                         // extract image without alpha channel
4861                         $imgplain = imagecreatetruecolor($wpx, $hpx);
4862                         imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
4863                         // create temp image file
4864                         $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
4865                         imagepng($imgplain, $tempfile_plain);
4866                         imagedestroy($imgplain);
4867                         // embed mask image
4868                         $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
4869                         // embed image, masked with previously embedded mask
4870                         $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
4871                         // remove temp files
4872                         unlink($tempfile_alpha);
4873                         unlink($tempfile_plain);
4874                 }
4875
4876                 /**
4877                 * Correct the gamma value to be used with GD library
4878                 * @param float $v the gamma value to be corrected
4879                 * @access protected
4880                 * @since 4.3.007 (2008-12-04)
4881                 */
4882                 protected function getGDgamma($v) {
4883                         return (pow(($v / 255), 2.2) * 255);
4884                 } 
4885                 
4886                 /**
4887                 * Performs a line break. 
4888                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
4889                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
4890                 * @param boolean $cell if true add a cMargin to the x coordinate
4891                 * @access public
4892                 * @since 1.0
4893                 * @see Cell()
4894                 */
4895                 public function Ln($h='', $cell=false) {
4896                         //Line feed; default value is last cell height
4897                         if ($cell) {
4898                                 $cellmargin = $this->cMargin;
4899                         } else {
4900                                 $cellmargin = 0;
4901                         }
4902                         if ($this->rtl) {
4903                                 $this->x = $this->w - $this->rMargin - $cellmargin;
4904                         } else {
4905                                 $this->x = $this->lMargin + $cellmargin;
4906                         }
4907                         if (is_string($h)) {
4908                                 $this->y += $this->lasth;
4909                         } else {
4910                                 $this->y += $h;
4911                         }
4912                         $this->newline = true;
4913                 }
4914
4915                 /**
4916                 * Returns the relative X value of current position.
4917                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
4918                 * @return float
4919                 * @access public
4920                 * @since 1.2
4921                 * @see SetX(), GetY(), SetY()
4922                 */
4923                 public function GetX() {
4924                         //Get x position
4925                         if ($this->rtl) {
4926                                 return ($this->w - $this->x);
4927                         } else {
4928                                 return $this->x;
4929                         }
4930                 }
4931                 
4932                 /**
4933                 * Returns the absolute X value of current position.
4934                 * @return float
4935                 * @access public
4936                 * @since 1.2
4937                 * @see SetX(), GetY(), SetY()
4938                 */
4939                 public function GetAbsX() {
4940                         return $this->x;
4941                 }
4942                 
4943                 /**
4944                 * Returns the ordinate of the current position.
4945                 * @return float
4946                 * @access public
4947                 * @since 1.0
4948                 * @see SetY(), GetX(), SetX()
4949                 */
4950                 public function GetY() {
4951                         //Get y position
4952                         return $this->y;
4953                 }
4954                 
4955                 /**
4956                 * Defines the abscissa of the current position. 
4957                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
4958                 * @param float $x The value of the abscissa.
4959                 * @access public
4960                 * @since 1.2
4961                 * @see GetX(), GetY(), SetY(), SetXY()
4962                 */
4963                 public function SetX($x) {
4964                         //Set x position
4965                         if ($this->rtl) {
4966                                 if ($x >= 0) {
4967                                         $this->x = $this->w - $x;
4968                                 } else {
4969                                         $this->x = abs($x);
4970                                 }
4971                         } else {
4972                                 if ($x >= 0) {
4973                                         $this->x = $x;
4974                                 } else {
4975                                         $this->x = $this->w + $x;
4976                                 }
4977                         }
4978                         if ($this->x < 0) {
4979                                 $this->x = 0;
4980                         }
4981                         if ($this->x > $this->w) {
4982                                 $this->x = $this->w;
4983                         }
4984                 }
4985                 
4986                 /**
4987                 * Moves the current abscissa back to the left margin and sets the ordinate.
4988                 * If the passed value is negative, it is relative to the bottom of the page.
4989                 * @param float $y The value of the ordinate.
4990                 * @param bool $resetx if true (default) reset the X position.
4991                 * @access public
4992                 * @since 1.0
4993                 * @see GetX(), GetY(), SetY(), SetXY()
4994                 */
4995                 public function SetY($y, $resetx=true) {
4996                         if ($resetx) {
4997                                 //reset x
4998                                 if ($this->rtl) {
4999                                         $this->x = $this->w - $this->rMargin;
5000                                 } else {
5001                                         $this->x = $this->lMargin;
5002                                 }
5003                         }
5004                         if ($y >= 0) {
5005                                 $this->y = $y;
5006                         } else {
5007                                 $this->y = $this->h + $y;
5008                         }
5009                         if ($this->y < 0) {
5010                                 $this->y = 0;
5011                         }
5012                         if ($this->y > $this->h) {
5013                                 $this->y = $this->h;
5014                         }
5015                 }
5016                 
5017                 /**
5018                 * Defines the abscissa and ordinate of the current position. 
5019                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
5020                 * @param float $x The value of the abscissa
5021                 * @param float $y The value of the ordinate
5022                 * @access public
5023                 * @since 1.2
5024                 * @see SetX(), SetY()
5025                 */
5026                 public function SetXY($x, $y) {
5027                         //Set x and y positions
5028                         $this->SetY($y);
5029                         $this->SetX($x);
5030                 }
5031
5032                 /**
5033                 * Send the document to a given destination: string, local file or browser. 
5034                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
5035                 * The method first calls Close() if necessary to terminate the document.
5036                 * @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.
5037                 * @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>
5038                 * @access public
5039                 * @since 1.0
5040                 * @see Close()
5041                 */
5042                 public function Output($name='doc.pdf', $dest='I') {
5043                         //Output PDF to some destination
5044                         //Finish document if necessary
5045                         if ($this->state < 3) {
5046                                 $this->Close();
5047                         }
5048                         //Normalize parameters
5049                         if (is_bool($dest)) {
5050                                 $dest = $dest ? 'D' : 'F';
5051                         }
5052                         $dest = strtoupper($dest);
5053                         if ($dest != 'F') {
5054                                 $name = preg_replace('/[\s]+/', '_', $name);
5055                                 $name = preg_replace('/[^a-zA-Z0-9\._-\x{4e00}-\x{9fa5}]/u', '', $name);
5056                         }
5057                         if ($this->sign) {
5058                                 // *** apply digital signature to the document ***
5059                                 // get the document content
5060                                 $pdfdoc = $this->getBuffer();
5061                                 // remove last newline
5062                                 $pdfdoc = substr($pdfdoc, 0, -1);
5063                                 // Remove the original buffer
5064                                 if (isset($this->diskcache) AND $this->diskcache) {
5065                                         // remove buffer file from cache
5066                                         unlink($this->buffer);
5067                                 }
5068                                 unset($this->buffer);
5069                                 // remove filler space
5070                                 $tmppos = strpos($pdfdoc, '/ByteRange[0 ********** ********** **********]') + 58;
5071                                 $pdfdoc = substr($pdfdoc, 0, $tmppos).substr($pdfdoc, $tmppos + $this->signature_max_lenght);
5072                                 // define the ByteRange
5073                                 $byte_range = array();
5074                                 $byte_range[0] = 0;
5075                                 $byte_range[1] = $tmppos - 1;
5076                                 $byte_range[2] = $byte_range[1] + $this->signature_max_lenght;
5077                                 $byte_range[3] = strlen($pdfdoc) - $byte_range[1];
5078                                 // replace the ByteRange
5079                                 $byterange = sprintf('/ByteRange[0 %010u %010u %010u]', $byte_range[1], $byte_range[2], $byte_range[3]);
5080                                 $pdfdoc = str_replace('/ByteRange[0 ********** ********** **********]', $byterange, $pdfdoc);
5081                                 // write the document to a temporary folder
5082                                 $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
5083                                 $f = fopen($tempdoc, 'wb');
5084                                 if (!$f) {
5085                                         $this->Error('Unable to create temporary file: '.$tempdoc);
5086                                 }
5087                                 $pdfdoc_lenght = strlen($pdfdoc);
5088                                 fwrite($f, $pdfdoc, $pdfdoc_lenght);
5089                                 fclose($f);
5090                                 // get digital signature.
5091                                 // IS THE FOLLOWING PROCEDURE CORRECT? THE SIGNED DOCUMENTS ARE NOT VALID!
5092                                 $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
5093                                 if (empty($this->signature_data['extracerts'])) {
5094                                         openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
5095                                 } else {
5096                                         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']);
5097                                 }       
5098                                 unlink($tempdoc);
5099                                 // read signature
5100                                 $signature = file_get_contents($tempsign, false, null, $pdfdoc_lenght);
5101                                 unlink($tempsign);
5102                                 // extract signature
5103                                 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
5104                                 $tmparr = explode("\n\n", $signature);
5105                                 $signature = $tmparr[1];
5106                                 unset($tmparr);
5107                                 // decode signature
5108                                 $signature = base64_decode(trim($signature));
5109                                 // convert signature to hex
5110                                 $signature = current(unpack('H*', $signature));
5111                                 $signature = str_pad($signature, $this->signature_max_lenght, '0');
5112                                 // Add signature to the document
5113                                 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).$signature.substr($pdfdoc, (0 - $byte_range[3]));
5114                                 $this->diskcache = false;
5115                                 $this->buffer = &$pdfdoc;
5116                                 $this->bufferlen = strlen($pdfdoc);
5117                         }
5118                         switch($dest) {
5119                                 case 'I': {
5120                                         // Send PDF to the standard output
5121                                         if (ob_get_contents()) {
5122                                                 $this->Error('Some data has already been output, can\'t send PDF file');
5123                                         }
5124                                         if (php_sapi_name() != 'cli') {
5125                                                 //We send to a browser
5126                                                 header('Content-Type: application/pdf');
5127                                                 if (headers_sent()) {
5128                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
5129                                                 }
5130                                                 header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5131                                                 header('Pragma: public');
5132                                                 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5133                                                 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');      
5134                                                 header('Content-Length: '.$this->bufferlen);
5135                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
5136                                         }
5137                                         echo $this->getBuffer();
5138                                         break;
5139                                 }
5140                                 case 'D': {
5141                                         // Download PDF as file
5142                                         if (ob_get_contents()) {
5143                                                 $this->Error('Some data has already been output, can\'t send PDF file');
5144                                         }
5145                                         header('Content-Description: File Transfer');
5146                                         if (headers_sent()) {
5147                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
5148                                         }
5149                                         header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
5150                                         header('Pragma: public');
5151                                         header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
5152                                         header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
5153                                         // force download dialog
5154                                         header('Content-Type: application/force-download');
5155                                         header('Content-Type: application/octet-stream', false);
5156                                         header('Content-Type: application/download', false);
5157                                         header('Content-Type: application/pdf', false);
5158                                         // use the Content-Disposition header to supply a recommended filename
5159                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
5160                                         header('Content-Transfer-Encoding: binary');
5161                                         header('Content-Length: '.$this->bufferlen);
5162                                         echo $this->getBuffer();
5163                                         break;
5164                                 }
5165                                 case 'F': {
5166                                         // Save PDF to a local file
5167                                         if ($this->diskcache) {
5168                                                 copy($this->buffer, $name);
5169                                         } else {
5170                                                 $f = fopen($name, 'wb');
5171                                                 if (!$f) {
5172                                                         $this->Error('Unable to create output file: '.$name);
5173                                                 }
5174                                                 fwrite($f, $this->getBuffer(), $this->bufferlen);
5175                                                 fclose($f);
5176                                         }
5177                                         break;
5178                                 }
5179                                 case 'S': {
5180                                         // Returns PDF as a string
5181                                         return $this->getBuffer();
5182                                 }
5183                                 default: {
5184                                         $this->Error('Incorrect output destination: '.$dest);
5185                                 }
5186                         }
5187                         return '';
5188                 }
5189
5190                 /**
5191                  * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
5192                  * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.
5193                  * @param boolean $preserve_objcopy if true preserves the objcopy variable
5194                  * @access public
5195                  * @since 4.5.016 (2009-02-24)
5196                  */
5197                 public function _destroy($destroyall=false, $preserve_objcopy=false) {
5198                         if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
5199                                 // remove buffer file from cache
5200                                 unlink($this->buffer);
5201                         }
5202                         foreach (array_keys(get_object_vars($this)) as $val) {
5203                                 if ($destroyall OR (
5204                                         ($val != 'internal_encoding') 
5205                                         AND ($val != 'state') 
5206                                         AND ($val != 'bufferlen') 
5207                                         AND ($val != 'buffer') 
5208                                         AND ($val != 'diskcache')
5209                                         AND ($val != 'sign')
5210                                         AND ($val != 'signature_data')
5211                                         AND ($val != 'signature_max_lenght')
5212                                         )) {
5213                                         if (!$preserve_objcopy OR ($val != 'objcopy')) {
5214                                                 unset($this->$val);
5215                                         }
5216                                 }
5217                         }
5218                 }
5219                 
5220                 /**
5221                 * Check for locale-related bug
5222                 * @access protected
5223                 */
5224                 protected function _dochecks() {
5225                         //Check for locale-related bug
5226                         if (1.1 == 1) {
5227                                 $this->Error('Don\'t alter the locale before including class file');
5228                         }
5229                         //Check for decimal separator
5230                         if (sprintf('%.1F', 1.0) != '1.0') {
5231                                 setlocale(LC_NUMERIC, 'C');
5232                         }
5233                 }
5234
5235                 /**
5236                 * Return fonts path
5237                 * @return string
5238                 * @access protected
5239                 */
5240                 protected function _getfontpath() {
5241                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
5242                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
5243                         }
5244                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
5245                 }
5246                 
5247                 /**
5248                 * Output pages.
5249                 * @access protected
5250                 */
5251                 protected function _putpages() {
5252                         $nb = $this->numpages;
5253                         if (!empty($this->AliasNbPages)) {
5254                                 $nbs = $this->formatPageNumber($nb);
5255                                 $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
5256                                 $alias_a = $this->_escape($this->AliasNbPages);
5257                                 $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
5258                                 if ($this->isunicode) {
5259                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
5260                                         $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
5261                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
5262                                         $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
5263                                 }
5264                         }
5265                         if (!empty($this->AliasNumPage)) {
5266                                 $alias_pa = $this->_escape($this->AliasNumPage);
5267                                 $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
5268                                 if ($this->isunicode) {
5269                                         $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
5270                                         $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
5271                                         $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
5272                                         $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
5273                                 }
5274                         }
5275                         $pagegroupnum = 0;
5276                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
5277                         for ($n=1; $n <= $nb; ++$n) {
5278                                 $temppage = $this->getPageBuffer($n);
5279                                 if (!empty($this->pagegroups)) {
5280                                         if(isset($this->newpagegroup[$n])) {
5281                                                 $pagegroupnum = 0;
5282                                         }
5283                                         ++$pagegroupnum;
5284                                         foreach ($this->pagegroups as $k => $v) {
5285                                                 // replace total pages group numbers
5286                                                 $vs = $this->formatPageNumber($v);
5287                                                 $vu = $this->UTF8ToUTF16BE($vs, false);
5288                                                 $alias_ga = $this->_escape($k);
5289                                                 $alias_gau = $this->_escape('{'.$k.'}');
5290                                                 if ($this->isunicode) {
5291                                                         $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
5292                                                         $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
5293                                                         $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
5294                                                         $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
5295                                                 }
5296                                                 $temppage = str_replace($alias_gau, $vu, $temppage);
5297                                                 if ($this->isunicode) {
5298                                                         $temppage = str_replace($alias_gbu, $vu, $temppage);
5299                                                         $temppage = str_replace($alias_gcu, $vu, $temppage);
5300                                                         $temppage = str_replace($alias_gb, $vs, $temppage);
5301                                                         $temppage = str_replace($alias_gc, $vs, $temppage);
5302                                                 }
5303                                                 $temppage = str_replace($alias_ga, $vs, $temppage);
5304                                                 // replace page group numbers
5305                                                 $pvs = $this->formatPageNumber($pagegroupnum);
5306                                                 $pvu = $this->UTF8ToUTF16BE($pvs, false);
5307                                                 $pk = str_replace('{nb', '{pnb', $k);
5308                                                 $alias_pga = $this->_escape($pk);
5309                                                 $alias_pgau = $this->_escape('{'.$pk.'}');
5310                                                 if ($this->isunicode) {
5311                                                         $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
5312                                                         $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
5313                                                         $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
5314                                                         $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
5315                                                 }
5316                                                 $temppage = str_replace($alias_pgau, $pvu, $temppage);
5317                                                 if ($this->isunicode) {
5318                                                         $temppage = str_replace($alias_pgbu, $pvu, $temppage);
5319                                                         $temppage = str_replace($alias_pgcu, $pvu, $temppage);
5320                                                         $temppage = str_replace($alias_pgb, $pvs, $temppage);
5321                                                         $temppage = str_replace($alias_pgc, $pvs, $temppage);
5322                                                 }
5323                                                 $temppage = str_replace($alias_pga, $pvs, $temppage);
5324                                         }
5325                                 }
5326                                 if (!empty($this->AliasNbPages)) {
5327                                         // replace total pages number
5328                                         $temppage = str_replace($alias_au, $nbu, $temppage);
5329                                         if ($this->isunicode) {
5330                                                 $temppage = str_replace($alias_bu, $nbu, $temppage);
5331                                                 $temppage = str_replace($alias_cu, $nbu, $temppage);
5332                                                 $temppage = str_replace($alias_b, $nbs, $temppage);
5333                                                 $temppage = str_replace($alias_c, $nbs, $temppage);
5334                                         }
5335                                         $temppage = str_replace($alias_a, $nbs, $temppage);
5336                                 }
5337                                 if (!empty($this->AliasNumPage)) {
5338                                         // replace page number
5339                                         $pnbs = $this->formatPageNumber($n);
5340                                         $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
5341                                         $temppage = str_replace($alias_pau, $pnbu, $temppage);
5342                                         if ($this->isunicode) {
5343                                                 $temppage = str_replace($alias_pbu, $pnbu, $temppage);
5344                                                 $temppage = str_replace($alias_pcu, $pnbu, $temppage);
5345                                                 $temppage = str_replace($alias_pb, $pnbs, $temppage);
5346                                                 $temppage = str_replace($alias_pc, $pnbs, $temppage);
5347                                         }
5348                                         $temppage = str_replace($alias_pa, $pnbs, $temppage);
5349                                 }
5350                                 $temppage = str_replace($this->epsmarker, '', $temppage);
5351                                 //$this->setPageBuffer($n, $temppage);
5352                                 //Page
5353                                 $this->_newobj();
5354                                 $this->_out('<</Type /Page');
5355                                 $this->_out('/Parent 1 0 R');
5356                                 $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
5357                                 $this->_out('/Resources 2 0 R');
5358                                 $this->_putannots($n);
5359                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
5360                                 $this->_out('endobj');
5361                                 //Page content
5362                                 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
5363                                 $this->_newobj();
5364                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
5365                                 $this->_putstream($p);
5366                                 $this->_out('endobj');
5367                                 if ($this->diskcache) {
5368                                         // remove temporary files
5369                                         unlink($this->pages[$n]);
5370                                 }
5371                         }
5372                         //Pages root
5373                         $this->offsets[1] = $this->bufferlen;
5374                         $this->_out('1 0 obj');
5375                         $this->_out('<</Type /Pages');
5376                         $kids='/Kids [';
5377                         for ($i=0; $i < $nb; ++$i) {
5378                                 $kids .= (3 + (2 * $i)).' 0 R ';
5379                         }
5380                         $this->_out($kids.']');
5381                         $this->_out('/Count '.$nb);
5382                         //$this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
5383                         $this->_out('>>');
5384                         $this->_out('endobj');
5385                 }
5386
5387                 /**
5388                 * Output Page Annotations.
5389                 * !!! THIS FUNCTION IS NOT YET COMPLETED !!!
5390                 * See section 8.4 of PDF reference.
5391                 * @param int $n page number
5392                 * @access protected
5393                 * @author Nicola Asuni
5394                 * @since 4.0.018 (2008-08-06)
5395                 */
5396                 protected function _putannots($n) {
5397                         if (isset($this->PageAnnots[$n])) {
5398                                 $annots = '/Annots [';
5399                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
5400                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
5401                                         $a = $pl['x'] * $this->k;
5402                                         $b = $this->pagedim[$n]['h'] - ($pl['y']  * $this->k);
5403                                         $c = $pl['w'] * $this->k;
5404                                         $d = $pl['h'] * $this->k;
5405                                         $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b-$d);
5406                                         $annots .= "\n";
5407                                         $annots .= '<</Type /Annot';
5408                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
5409                                         $annots .= ' /Rect ['.$rect.']';
5410                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
5411                                         //$annots .= ' /P ';
5412                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
5413                                         $annots .= ' /M '.$this->_datastring('D:'.date('YmdHis'));
5414                                         if (isset($pl['opt']['f'])) {
5415                                                 $val = 0;
5416                                                 if (is_array($pl['opt']['f'])) {
5417                                                         foreach ($pl['opt']['f'] as $f) {
5418                                                                 switch (strtolower($f)) {
5419                                                                         case 'invisible': {
5420                                                                                 $val += 1 << 0;
5421                                                                                 break;
5422                                                                         }
5423                                                                         case 'hidden': {
5424                                                                                 $val += 1 << 1;
5425                                                                                 break;
5426                                                                         }
5427                                                                         case 'print': {
5428                                                                                 $val += 1 << 2;
5429                                                                                 break;
5430                                                                         }
5431                                                                         case 'nozoom': {
5432                                                                                 $val += 1 << 3;
5433                                                                                 break;
5434                                                                         }
5435                                                                         case 'norotate': {
5436                                                                                 $val += 1 << 4;
5437                                                                                 break;
5438                                                                         }
5439                                                                         case 'noview': {
5440                                                                                 $val += 1 << 5;
5441                                                                                 break;
5442                                                                         }
5443                                                                         case 'readonly': {
5444                                                                                 $val += 1 << 6;
5445                                                                                 break;
5446                                                                         }
5447                                                                         case 'locked': {
5448                                                                                 $val += 1 << 8;
5449                                                                                 break;
5450                                                                         }
5451                                                                         case 'togglenoview': {
5452                                                                                 $val += 1 << 9;
5453                                                                                 break;
5454                                                                         }
5455                                                                         case 'lockedcontents': {
5456                                                                                 $val += 1 << 10;
5457                                                                                 break;
5458                                                                         }
5459                                                                         default: {
5460                                                                                 break;
5461                                                                         }
5462                                                                 }
5463                                                         }
5464                                                 }
5465                                                 $annots .= ' /F '.intval($val);
5466                                         }
5467                                         //$annots .= ' /AP ';
5468                                         //$annots .= ' /AS ';
5469                                         $annots .= ' /Border [';
5470                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
5471                                                 $annots .= intval($pl['opt']['border'][0]).' ';
5472                                                 $annots .= intval($pl['opt']['border'][1]).' ';
5473                                                 $annots .= intval($pl['opt']['border'][2]);
5474                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
5475                                                         $annots .= ' [';
5476                                                         foreach ($pl['opt']['border'][3] as $dash) {
5477                                                                 $annots .= intval($dash).' ';
5478                                                         }
5479                                                         $annots .= ']';
5480                                                 }
5481                                         } else {
5482                                                 $annots .= '0 0 0';
5483                                         }
5484                                         $annots .= ']';
5485                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
5486                                                 $annots .= ' /BS <<Type /Border';
5487                                                 if (isset($pl['opt']['bs']['w'])) {
5488                                                         $annots .= ' /W '.sprintf("%.4F", floatval($pl['opt']['bs']['w']));
5489                                                 }
5490                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
5491                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
5492                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
5493                                                 }
5494                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
5495                                                         $annots .= ' /D [';
5496                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
5497                                                                 $cord = floatval($cord);
5498                                                                 $annots .= sprintf(" %.4F", $cord);
5499                                                         }
5500                                                         $annots .= ']';
5501                                                 }
5502                                                 $annots .= '>> ';
5503                                         }
5504                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
5505                                                 $annots .= ' /BE <<';
5506                                                 $bstyles = array('S', 'C');
5507                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
5508                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
5509                                                 } else {
5510                                                         $annots .= ' /S /S';
5511                                                 }
5512                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
5513                                                         $annots .= ' /I '.sprintf(" %.4F", $pl['opt']['be']['i']);
5514                                                 }
5515                                                 $annots .= '>>';
5516                                         }
5517                                         $annots .= ' /C [';
5518                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
5519                                                 foreach ($pl['opt']['c'] as $col) {
5520                                                         $col = intval($col);
5521                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
5522                                                         $annots .= sprintf(" %.4F", $color);
5523                                                 }
5524                                         }
5525                                         $annots .= ']';
5526                                         //$annots .= ' /StructParent ';
5527                                         //$annots .= ' /OC ';
5528                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
5529                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
5530                                                 // this is a markup type
5531                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
5532                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
5533                                                 }
5534                                                 //$annots .= ' /Popup ';
5535                                                 if (isset($pl['opt']['ca'])) {
5536                                                         $annots .= ' /CA '.sprintf("%.4F", floatval($pl['opt']['ca']));
5537                                                 }
5538                                                 if (isset($pl['opt']['rc'])) {
5539                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5540                                                 }
5541                                                 $annots .= ' /CreationDate '.$this->_datastring('D:'.date('YmdHis'));
5542                                                 //$annots .= ' /IRT ';
5543                                                 if (isset($pl['opt']['subj'])) {
5544                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
5545                                                 }
5546                                                 //$annots .= ' /RT ';
5547                                                 //$annots .= ' /IT ';
5548                                                 //$annots .= ' /ExData ';
5549                                         }
5550                                         switch (strtolower($pl['opt']['subtype'])) {
5551                                                 case 'text': {
5552                                                         if (isset($pl['opt']['open'])) {
5553                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
5554                                                         }
5555                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
5556                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5557                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
5558                                                         } else {
5559                                                                 $annots .= ' /Name /Note';
5560                                                         }
5561                                                         $statemodels = array('Marked', 'Review');
5562                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
5563                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5564                                                         } else {
5565                                                                 $pl['opt']['statemodel'] = 'Marked';
5566                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
5567                                                         }
5568                                                         if ($pl['opt']['statemodel'] == 'Marked') {
5569                                                                 $states = array('Accepted', 'Unmarked');
5570                                                         } else {
5571                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
5572                                                         }
5573                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
5574                                                                 $annots .= ' /State /'.$pl['opt']['state'];
5575                                                         } else {
5576                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
5577                                                                         $annots .= ' /State /Unmarked';
5578                                                                 } else {
5579                                                                         $annots .= ' /State /None';
5580                                                                 }
5581                                                         }
5582                                                         break;
5583                                                 }
5584                                                 case 'link': {
5585                                                         if(is_string($pl['txt'])) {
5586                                                                 // external URI link
5587                                                                 $annots .= ' /A <</S /URI /URI '.$this->_datastring($pl['txt']).'>>';
5588                                                         } else {
5589                                                                 // internal link
5590                                                                 $l = $this->links[$pl['txt']];
5591                                                                 $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
5592                                                         }
5593                                                         $hmodes = array('N', 'I', 'O', 'P');
5594                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
5595                                                                 $annots .= ' /H /'.$pl['opt']['h'];
5596                                                         } else {
5597                                                                 $annots .= ' /H /I';
5598                                                         }
5599                                                         //$annots .= ' /PA ';
5600                                                         //$annots .= ' /Quadpoints ';
5601                                                         break;
5602                                                 }
5603                                                 case 'freetext': {
5604                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
5605                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
5606                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
5607                                                         }
5608                                                         if (isset($pl['opt']['rc'])) {
5609                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
5610                                                         }
5611                                                         if (isset($pl['opt']['ds'])) {
5612                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
5613                                                         }
5614                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
5615                                                                 $annots .= ' /CL [';
5616                                                                 foreach ($pl['opt']['cl'] as $cl) {
5617                                                                         $annots .= sprintf("%.4F ", $cl * $this->k);
5618                                                                 }
5619                                                                 $annots .= ']';
5620                                                         }
5621                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
5622                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
5623                                                                 $annots .= ' /IT '.$pl['opt']['it'];
5624                                                         }
5625                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
5626                                                                 $l = $pl['opt']['rd'][0] * $this->k;
5627                                                                 $r = $pl['opt']['rd'][1] * $this->k;
5628                                                                 $t = $pl['opt']['rd'][2] * $this->k;
5629                                                                 $b = $pl['opt']['rd'][3] * $this->k;
5630                                                                 $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
5631                                                         }
5632                                                         //$annots .= ' /LE ';
5633                                                         break;
5634                                                 }
5635                                                 // ... to be completed ...
5636                                                 case 'line': {
5637                                                         break;
5638                                                 }
5639                                                 case 'square': {
5640                                                         break;
5641                                                 }
5642                                                 case 'circle': {
5643                                                         break;
5644                                                 }
5645                                                 case 'polygon': {
5646                                                         break;
5647                                                 }
5648                                                 case 'polyline': {
5649                                                         break;
5650                                                 }
5651                                                 case 'highlight': {
5652                                                         break;
5653                                                 }
5654                                                 case 'underline': {
5655                                                         break;
5656                                                 }
5657                                                 case 'squiggly': {
5658                                                         break;
5659                                                 }
5660                                                 case 'strikeout': {
5661                                                         break;
5662                                                 }
5663                                                 case 'stamp': {
5664                                                         break;
5665                                                 }
5666                                                 case 'caret': {
5667                                                         break;
5668                                                 }
5669                                                 case 'ink': {
5670                                                         break;
5671                                                 }
5672                                                 case 'popup': {
5673                                                         break;
5674                                                 }
5675                                                 case 'fileattachment': {
5676                                                         if (!isset($pl['opt']['fs'])) {
5677                                                                 break;
5678                                                         }
5679                                                         $filename = basename($pl['opt']['fs']);
5680                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
5681                                                                 $annots .= ' /FS <</Type /Filespec /F '.$this->_datastring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
5682                                                                 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
5683                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5684                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
5685                                                                 } else {
5686                                                                         $annots .= ' /Name /PushPin';
5687                                                                 }
5688                                                         }
5689                                                         break;
5690                                                 }
5691                                                 case 'sound': {
5692                                                         if (!isset($pl['opt']['sound'])) {
5693                                                                 break;
5694                                                         }
5695                                                         $filename = basename($pl['opt']['sound']);
5696                                                         if (isset($this->embeddedfiles[$filename]['n'])) {
5697                                                                 // ... TO BE COMPLETED ...
5698                                                                 $iconsapp = array('Speaker', 'Mic');
5699                                                                 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
5700                                                                         $annots .= ' /Name /'.$pl['opt']['name'];
5701                                                                 } else {
5702                                                                         $annots .= ' /Name /Speaker';
5703                                                                 }
5704                                                         }
5705                                                         break;
5706                                                 }
5707                                                 case 'movie': {
5708                                                         break;
5709                                                 }
5710                                                 case 'widget': {
5711                                                         if (isset($pl['opt']['h'])) {
5712                                                                 $annots .= ' /H '.intval($pl['opt']['h']);
5713                                                         }
5714                                                         if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk']))) {
5715                                                                 $annots .= ' /MK <<';
5716                                                                 // ... TO BE COMPLETED ...
5717                                                                 $annots .= '>>';
5718                                                         }
5719                                                         break;
5720                                                 }
5721                                                 case 'screen': {
5722                                                         break;
5723                                                 }
5724                                                 case 'printermark': {
5725                                                         break;
5726                                                 }
5727                                                 case 'trapnet': {
5728                                                         break;
5729                                                 }
5730                                                 case 'watermark': {
5731                                                         break;
5732                                                 }
5733                                                 case '3d': {
5734                                                         break;
5735                                                 }
5736                                                 default: {
5737                                                         break;
5738                                                 }
5739                                         }
5740                                         
5741                                 $annots .= '>>';
5742                                 }
5743                                 $annots .= "\n]";
5744                                 $this->_out($annots);
5745                         }
5746                 }
5747
5748                 /**
5749                 * Output fonts.
5750                 * @access protected
5751                 */
5752                 protected function _putfonts() {
5753                         $nf = $this->n;
5754                         foreach ($this->diffs as $diff) {
5755                                 //Encodings
5756                                 $this->_newobj();
5757                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
5758                                 $this->_out('endobj');
5759                         }
5760                         $mqr = get_magic_quotes_runtime();
5761                         //set_magic_quotes_runtime(0);
5762                         foreach ($this->FontFiles as $file => $info) {
5763                                 // search and get font file to embedd
5764                                 $fontdir = $info['fontdir'];
5765                                 $file = strtolower($file);
5766                                 $fontfile = '';
5767                                 // search files on various directories
5768                                 if (file_exists($fontdir.$file)) {
5769                                         $fontfile = $fontdir.$file;
5770                                 } elseif (file_exists($this->_getfontpath().$file)) {
5771                                         $fontfile = $this->_getfontpath().$file;
5772                                 } elseif (file_exists($file)) {
5773                                         $fontfile = $file;
5774                                 }
5775                                 if (!$this->empty_string($fontfile)) {
5776                                         $font = file_get_contents($fontfile);
5777                                         $compressed = (substr($file, -2) == '.z');
5778                                         if ((!$compressed) AND (isset($info['length2']))) {
5779                                                 $header = (ord($font{0}) == 128);
5780                                                 if ($header) {
5781                                                         //Strip first binary header
5782                                                         $font = substr($font, 6);
5783                                                 }
5784                                                 if ($header AND (ord($font{$info['length1']}) == 128)) {
5785                                                         //Strip second binary header
5786                                                         $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
5787                                                 }
5788                                         }
5789                                         $this->_newobj();
5790                                         $this->FontFiles[$file]['n'] = $this->n;
5791                                         $this->_out('<</Length '.strlen($font));
5792                                         if ($compressed) {
5793                                                 $this->_out('/Filter /FlateDecode');
5794                                         }
5795                                         $this->_out('/Length1 '.$info['length1']);
5796                                         if (isset($info['length2'])) {
5797                                                 $this->_out('/Length2 '.$info['length2'].' /Length3 0');
5798                                         }
5799                                         $this->_out('>>');
5800                                         $this->_putstream($font);
5801                                         $this->_out('endobj');
5802                                 }
5803                         }
5804                         //set_magic_quotes_runtime($mqr);
5805                         foreach ($this->fontkeys as $k) {
5806                                 //Font objects
5807                                 $this->setFontSubBuffer($k, 'n', $this->n + 1);
5808                                 $font = $this->getFontBuffer($k);
5809                                 $type = $font['type'];
5810                                 $name = $font['name'];
5811                                 if ($type == 'core') {
5812                                         //Standard font
5813                                         $this->_newobj();
5814                                         $this->_out('<</Type /Font');
5815                                         $this->_out('/BaseFont /'.$name);
5816                                         $this->_out('/Subtype /Type1');
5817                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
5818                                                 $this->_out('/Encoding /WinAnsiEncoding');
5819                                         }
5820                                         $this->_out('>>');
5821                                         $this->_out('endobj');
5822                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
5823                                         //Additional Type1 or TrueType font
5824                                         $this->_newobj();
5825                                         $this->_out('<</Type /Font');
5826                                         $this->_out('/BaseFont /'.$name);
5827                                         $this->_out('/Subtype /'.$type);
5828                                         $this->_out('/FirstChar 32 /LastChar 255');
5829                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
5830                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
5831                                         if ($font['enc']) {
5832                                                 if (isset($font['diff'])) {
5833                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
5834                                                 } else {
5835                                                         $this->_out('/Encoding /WinAnsiEncoding');
5836                                                 }
5837                                         }
5838                                         $this->_out('>>');
5839                                         $this->_out('endobj');
5840                                         // Widths
5841                                         $this->_newobj();
5842                                         $cw = &$font['cw'];
5843                                         $s = '[';
5844                                         for ($i = 32; $i < 256; ++$i) {
5845                                                 $s .= $cw[$i].' ';
5846                                         }
5847                                         $this->_out($s.']');
5848                                         $this->_out('endobj');
5849                                         //Descriptor
5850                                         $this->_newobj();
5851                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
5852                                         foreach ($font['desc'] as $k => $v) {
5853                                                 $s .= ' /'.$k.' '.$v.'';
5854                                         }
5855                                         if (!$this->empty_string($font['file'])) {
5856                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
5857                                         }
5858                                         $this->_out($s.'>>');
5859                                         $this->_out('endobj');
5860                                 } else {
5861                                         //Allow for additional types
5862                                         $mtd = '_put'.strtolower($type);
5863                                         if (!method_exists($this, $mtd)) {
5864                                                 $this->Error('Unsupported font type: '.$type);
5865                                         }
5866                                         $this->$mtd($font);
5867                                 }
5868                         }
5869                 }
5870                 
5871                 /**
5872                 * Outputs font widths
5873                 * @parameter array $font font data
5874                 * @parameter int $cidoffset offset for CID values
5875                 * @author Nicola Asuni
5876                 * @access protected
5877                 * @since 4.4.000 (2008-12-07)
5878                 */
5879                 protected function _putfontwidths($font, $cidoffset=0) {
5880                         ksort($font['cw']);
5881                         $rangeid = 0;
5882                         $range = array();
5883                         $prevcid = -2;
5884                         $prevwidth = -1;
5885                         $interval = false;
5886                         // for each character
5887                         foreach ($font['cw'] as $cid => $width) {
5888                                 $cid -= $cidoffset;
5889                                 if ($width != $font['dw']) {
5890                                         if ($cid == ($prevcid + 1)) {
5891                                                 // consecutive CID
5892                                                 if ($width == $prevwidth) {
5893                                                         if ($width == $range[$rangeid][0]) {
5894                                                                 $range[$rangeid][] = $width;
5895                                                         } else {
5896                                                                 array_pop($range[$rangeid]);
5897                                                                 // new range
5898                                                                 $rangeid = $prevcid;
5899                                                                 $range[$rangeid] = array();
5900                                                                 $range[$rangeid][] = $prevwidth;
5901                                                                 $range[$rangeid][] = $width;
5902                                                         }
5903                                                         $interval = true;
5904                                                         $range[$rangeid]['interval'] = true;
5905                                                 } else {
5906                                                         if ($interval) {
5907                                                                 // new range
5908                                                                 $rangeid = $cid;
5909                                                                 $range[$rangeid] = array();
5910                                                                 $range[$rangeid][] = $width;
5911                                                         } else {
5912                                                                 $range[$rangeid][] = $width;
5913                                                         }
5914                                                         $interval = false;
5915                                                 }
5916                                         } else {
5917                                                 // new range
5918                                                 $rangeid = $cid;
5919                                                 $range[$rangeid] = array();
5920                                                 $range[$rangeid][] = $width;
5921                                                 $interval = false;
5922                                         }
5923                                         $prevcid = $cid;
5924                                         $prevwidth = $width;
5925                                 }
5926                         }
5927                         // optimize ranges
5928                         $prevk = -1;
5929                         $nextk = -1;
5930                         $prevint = false;
5931                         foreach ($range as $k => $ws) {
5932                                 $cws = count($ws);
5933                                 if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
5934                                         if (isset($range[$k]['interval'])) {
5935                                                 unset($range[$k]['interval']);
5936                                         }
5937                                         $range[$prevk] = array_merge($range[$prevk], $range[$k]);
5938                                         unset($range[$k]);
5939                                 } else {
5940                                         $prevk = $k;
5941                                 }
5942                                 $nextk = $k + $cws;
5943                                 if (isset($ws['interval'])) {
5944                                         if ($cws > 3) {
5945                                                 $prevint = true;
5946                                         } else {
5947                                                 $prevint = false;
5948                                         }
5949                                         unset($range[$k]['interval']);
5950                                         --$nextk;
5951                                 } else {
5952                                         $prevint = false;
5953                                 }
5954                         }
5955                         // output data
5956                         $w = '';
5957                         foreach ($range as $k => $ws) {
5958                                 if (count(array_count_values($ws)) == 1) {
5959                                         // interval mode is more compact
5960                                         $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
5961                                 } else {
5962                                         // range mode
5963                                         $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
5964                                 }
5965                         }
5966                         $this->_out('/W ['.$w.' ]');
5967                 }
5968                 
5969                 /**
5970                 * Adds unicode fonts.<br>
5971                 * Based on PDF Reference 1.3 (section 5)
5972                 * @parameter array $font font data
5973                 * @access protected
5974                 * @author Nicola Asuni
5975                 * @since 1.52.0.TC005 (2005-01-05)
5976                 */
5977                 protected function _puttruetypeunicode($font) {
5978                         // Type0 Font
5979                         // A composite font composed of other fonts, organized hierarchically
5980                         $this->_newobj();
5981                         $this->_out('<</Type /Font');
5982                         $this->_out('/Subtype /Type0');
5983                         $this->_out('/BaseFont /'.$font['name'].'');
5984                         $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.
5985                         $this->_out('/ToUnicode /Identity-H');
5986                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5987                         $this->_out('>>');
5988                         $this->_out('endobj');
5989                         // CIDFontType2
5990                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5991                         $this->_newobj();
5992                         $this->_out('<</Type /Font');
5993                         $this->_out('/Subtype /CIDFontType2');
5994                         $this->_out('/BaseFont /'.$font['name'].'');
5995                         // A dictionary containing entries that define the character collection of the CIDFont.
5996                         $cidinfo = '/Registry '.$this->_datastring('Adobe');
5997                         $cidinfo .= ' /Ordering '.$this->_datastring('Identity');
5998                         $cidinfo .= ' /Supplement 0';
5999                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
6000                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
6001                         $this->_out('/DW '.$font['dw'].''); // default width
6002                         $this->_putfontwidths($font, 0);
6003                         $this->_out('/CIDToGIDMap '.($this->n + 2).' 0 R');
6004                         $this->_out('>>');
6005                         $this->_out('endobj');                  
6006                         // Font descriptor
6007                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
6008                         $this->_newobj();
6009                         $this->_out('<</Type /FontDescriptor');
6010                         $this->_out('/FontName /'.$font['name']);
6011                         foreach ($font['desc'] as $key => $value) {
6012                                 $this->_out('/'.$key.' '.$value);
6013                         }
6014                         $fontdir = '';
6015                         if (!$this->empty_string($font['file'])) {
6016                                 // A stream containing a TrueType font
6017                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
6018                                 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
6019                         }
6020                         $this->_out('>>');
6021                         $this->_out('endobj');
6022                         $this->_newobj();
6023                         if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
6024                                 // Embed CIDToGIDMap
6025                                 // A specification of the mapping from CIDs to glyph indices
6026                                 // search and get CTG font file to embedd
6027                                 $ctgfile = strtolower($font['ctg']);
6028                                 // search and get ctg font file to embedd
6029                                 $fontfile = '';
6030                                 // search files on various directories
6031                                 if (file_exists($fontdir.$ctgfile)) {
6032                                         $fontfile = $fontdir.$ctgfile;
6033                                 } elseif (file_exists($this->_getfontpath().$ctgfile)) {
6034                                         $fontfile = $this->_getfontpath().$ctgfile;
6035                                 } elseif (file_exists($ctgfile)) {
6036                                         $fontfile = $ctgfile;
6037                                 }
6038                                 if ($this->empty_string($fontfile)) {
6039                                         $this->Error('Font file not found: '.$ctgfile);
6040                                 }
6041                                 $size = filesize($fontfile);
6042                                 $this->_out('<</Length '.$size.'');
6043                                 if (substr($fontfile, -2) == '.z') { // check file extension
6044                                         // Decompresses data encoded using the public-domain 
6045                                         // zlib/deflate compression method, reproducing the 
6046                                         // original text or binary data
6047                                         $this->_out('/Filter /FlateDecode');
6048                                 }
6049                                 $this->_out('>>');
6050                                 $this->_putstream(file_get_contents($fontfile));
6051                         }
6052                         $this->_out('endobj');
6053                 }
6054                 
6055                 /**
6056                  * Output CID-0 fonts.
6057                  * @param array $font font data
6058                  * @access protected
6059                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
6060                  * @since 3.2.000 (2008-06-23)
6061                  */
6062                 protected function _putcidfont0($font) {
6063                         $cidoffset = 31;
6064                         if (isset($font['cidinfo']['uni2cid'])) {
6065                                 // convert unicode to cid.
6066                                 $uni2cid = $font['cidinfo']['uni2cid'];
6067                                 $cw = array();
6068                                 foreach ($font['cw'] as $uni => $width) {
6069                                         if (isset($uni2cid[$uni])) {
6070                                                 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
6071                                         } elseif ($uni < 256) {
6072                                                 $cw[$uni] = $width;
6073                                         } // else unknown character
6074                                 }
6075                                 $font = array_merge($font, array('cw' => $cw));
6076                         }
6077                         $name = $font['name'];
6078                         $enc = $font['enc'];
6079                         if ($enc) {
6080                                 $longname = $name.'-'.$enc;
6081                         } else {
6082                                 $longname = $name;
6083                         }
6084                         $this->_newobj();
6085                         $this->_out('<</Type /Font');
6086                         $this->_out('/BaseFont /'.$longname);
6087                         $this->_out('/Subtype /Type0');
6088                         if ($enc) {
6089                                 $this->_out('/Encoding /'.$enc);
6090                         }
6091                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
6092                         $this->_out('>>');
6093                         $this->_out('endobj');
6094                         $this->_newobj();
6095                         $this->_out('<</Type /Font');
6096                         $this->_out('/BaseFont /'.$name);
6097                         $this->_out('/Subtype /CIDFontType0');
6098                         $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
6099                         $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
6100                         $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
6101                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
6102                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
6103                         $this->_out('/DW '.$font['dw']);
6104                         $this->_putfontwidths($font, $cidoffset);
6105                         $this->_out('>>');
6106                         $this->_out('endobj');
6107                         $this->_newobj();
6108                         $s = '<</Type /FontDescriptor /FontName /'.$name;
6109                         foreach ($font['desc'] as $k => $v) {
6110                                 if ($k != 'Style') {
6111                                         $s .= ' /'.$k.' '.$v.'';
6112                                 }
6113                         }
6114                         $this->_out($s.'>>');
6115                         $this->_out('endobj');
6116                 }
6117
6118                 /**
6119                  * Output images.
6120                  * @access protected
6121                  */
6122                 protected function _putimages() {
6123                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
6124                         foreach ($this->imagekeys as $file) {
6125                                 $info = $this->getImageBuffer($file);
6126                                 $this->_newobj();
6127                                 $this->setImageSubBuffer($file, 'n', $this->n);
6128                                 $this->_out('<</Type /XObject');
6129                                 $this->_out('/Subtype /Image');
6130                                 $this->_out('/Width '.$info['w']);
6131                                 $this->_out('/Height '.$info['h']);
6132                                 if (isset($info['masked'])) {
6133                                         $this->_out('/SMask '.($this->n - 1).' 0 R');
6134                                 }
6135                                 if ($info['cs'] == 'Indexed') {
6136                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]');
6137                                 } else {
6138                                         $this->_out('/ColorSpace /'.$info['cs']);
6139                                         if ($info['cs'] == 'DeviceCMYK') {
6140                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
6141                                         }
6142                                 }
6143                                 $this->_out('/BitsPerComponent '.$info['bpc']);
6144                                 if (isset($info['f'])) {
6145                                         $this->_out('/Filter /'.$info['f']);
6146                                 }
6147                                 if (isset($info['parms'])) {
6148                                         $this->_out($info['parms']);
6149                                 }
6150                                 if (isset($info['trns']) AND is_array($info['trns'])) {
6151                                         $trns='';
6152                                         $count_info = count($info['trns']);
6153                                         for ($i=0; $i < $count_info; ++$i) {
6154                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
6155                                         }
6156                                         $this->_out('/Mask ['.$trns.']');
6157                                 }
6158                                 $this->_out('/Length '.strlen($info['data']).'>>');
6159                                 $this->_putstream($info['data']);
6160                                 $this->_out('endobj');
6161                                 //Palette
6162                                 if ($info['cs'] == 'Indexed') {
6163                                         $this->_newobj();
6164                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
6165                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
6166                                         $this->_putstream($pal);
6167                                         $this->_out('endobj');
6168                                 }
6169                         }
6170                 }
6171
6172                 /**
6173                 * Output Spot Colors Resources.
6174                 * @access protected
6175                 * @since 4.0.024 (2008-09-12)
6176                 */
6177                 protected function _putspotcolors() {
6178                         foreach ($this->spot_colors as $name => $color) {
6179                                 $this->_newobj();
6180                                 $this->spot_colors[$name]['n'] = $this->n;
6181                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
6182                                 $this->_out('/DeviceCMYK <<');
6183                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
6184                                 $this->_out(sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
6185                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
6186                                 $this->_out('endobj');
6187                         }
6188                 }
6189
6190                 /**
6191                 * Output object dictionary for images.
6192                 * @access protected
6193                 */
6194                 protected function _putxobjectdict() {
6195                         foreach ($this->imagekeys as $file) {
6196                                 $info = $this->getImageBuffer($file);
6197                                 $this->_out('/I'.$info['i'].' '.$info['n'].' 0 R');
6198                         }
6199                 }
6200
6201                 /**
6202                 * Output Resources Dictionary.
6203                 * @access protected
6204                 */
6205                 protected function _putresourcedict() {
6206                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
6207                         $this->_out('/Font <<');
6208                         foreach ($this->fontkeys as $fontkey) {
6209                                 $font = $this->getFontBuffer($fontkey);
6210                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
6211                         }
6212                         $this->_out('>>');
6213                         $this->_out('/XObject <<');
6214                         $this->_putxobjectdict();
6215                         $this->_out('>>');
6216                         // visibility
6217                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
6218                         // transparency
6219                         $this->_out('/ExtGState <<');
6220                         foreach ($this->extgstates as $k => $extgstate) {
6221                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
6222                         }
6223                         $this->_out('>>');
6224                         // gradients
6225                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
6226                                 $this->_out('/Shading <<');
6227                                 foreach ($this->gradients as $id => $grad) {
6228                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
6229                                 }
6230                                 $this->_out('>>');
6231                         }
6232                         // spot colors
6233                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
6234                                 $this->_out('/ColorSpace <<');
6235                                 foreach ($this->spot_colors as $color) {
6236                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
6237                                 }
6238                                 $this->_out('>>');
6239                         }
6240                 }
6241                 
6242                 /**
6243                 * Output Resources.
6244                 * @access protected
6245                 */
6246                 protected function _putresources() {
6247                         $this->_putextgstates();
6248                         $this->_putocg();
6249                         $this->_putfonts();
6250                         $this->_putimages();
6251                         $this->_putspotcolors();
6252                         $this->_putshaders();
6253                         //Resource dictionary
6254                         $this->offsets[2] = $this->bufferlen;
6255                         $this->_out('2 0 obj');
6256                         $this->_out('<<');
6257                         $this->_putresourcedict();
6258                         $this->_out('>>');
6259                         $this->_out('endobj');
6260                         $this->_putjavascript();
6261                         $this->_putbookmarks();
6262                         $this->_putEmbeddedFiles();
6263                         // encryption
6264                         if ($this->encrypted) {
6265                                 $this->_newobj();
6266                                 $this->enc_obj_id = $this->n;
6267                                 $this->_out('<<');
6268                                 $this->_putencryption();
6269                                 $this->_out('>>');
6270                                 $this->_out('endobj');
6271                         }
6272                 }
6273                 
6274                 /**
6275                 * Adds some Metadata information
6276                 * (see Chapter 10.2 of PDF Reference)
6277                 * @access protected
6278                 */
6279                 protected function _putinfo() {
6280                         if (!$this->empty_string($this->title)) {
6281                                 $this->_out('/Title '.$this->_textstring($this->title));
6282                         }
6283                         if (!$this->empty_string($this->author)) {
6284                                 $this->_out('/Author '.$this->_textstring($this->author));
6285                         }
6286                         if (!$this->empty_string($this->subject)) {
6287                                 $this->_out('/Subject '.$this->_textstring($this->subject));
6288                         }
6289                         if (!$this->empty_string($this->keywords)) {
6290                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
6291                         }
6292                         if (!$this->empty_string($this->creator)) {
6293                                 $this->_out('/Creator '.$this->_textstring($this->creator));
6294                         }
6295                         if (defined('PDF_PRODUCER')) {
6296                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
6297                         }
6298                         $this->_out('/CreationDate '.$this->_datastring('D:'.date('YmdHis')));
6299                         $this->_out('/ModDate '.$this->_datastring('D:'.date('YmdHis')));       
6300                 }
6301                 
6302                 /**
6303                 * Output Catalog.
6304                 * @access protected
6305                 */
6306                 protected function _putcatalog() {
6307                         $this->_out('/Type /Catalog');
6308                         $this->_out('/Pages 1 0 R');
6309                         if ($this->ZoomMode == 'fullpage') {
6310                                 $this->_out('/OpenAction [3 0 R /Fit]');
6311                         } elseif ($this->ZoomMode == 'fullwidth') {
6312                                 $this->_out('/OpenAction [3 0 R /FitH null]');
6313                         } elseif ($this->ZoomMode == 'real') {
6314                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
6315                         } elseif (!is_string($this->ZoomMode)) {
6316                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
6317                         }
6318                         if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
6319                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
6320                         }
6321                         if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
6322                                 $this->_out('/PageMode /'.$this->PageMode);
6323                         }
6324                         if (isset($this->l['a_meta_language'])) {
6325                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
6326                         }
6327                         $this->_out('/Names <<');
6328                         if (!$this->empty_string($this->javascript)) {
6329                                 $this->_out('/JavaScript '.($this->n_js).' 0 R');
6330                         }
6331                         $this->_out('>>');
6332                         if (count($this->outlines) > 0) {
6333                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
6334                                 $this->_out('/PageMode /UseOutlines');
6335                         }
6336                         $this->_putviewerpreferences();
6337                         $p = $this->n_ocg_print.' 0 R';
6338                         $v = $this->n_ocg_view.' 0 R';
6339                         $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
6340                         $this->_out('/OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>');
6341                 }
6342                 
6343                 /**
6344                 * Output viewer preferences.
6345                 * @author Nicola asuni
6346                 * @since 3.1.000 (2008-06-09)
6347                 * @access protected
6348                 */
6349                 protected function _putviewerpreferences() {
6350                         $this->_out('/ViewerPreferences<<');
6351                         if ($this->rtl) {
6352                                 $this->_out('/Direction /R2L');
6353                         } else {
6354                                 $this->_out('/Direction /L2R');
6355                         }
6356                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
6357                                 $this->_out('/HideToolbar true');
6358                         }
6359                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
6360                                 $this->_out('/HideMenubar true');
6361                         }
6362                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
6363                                 $this->_out('/HideWindowUI true');
6364                         }
6365                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
6366                                 $this->_out('/FitWindow true');
6367                         }
6368                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
6369                                 $this->_out('/CenterWindow true');
6370                         }
6371                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
6372                                 $this->_out('/DisplayDocTitle true');
6373                         }
6374                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
6375                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
6376                         }
6377                         if (isset($this->viewer_preferences['ViewArea'])) {
6378                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
6379                         }
6380                         if (isset($this->viewer_preferences['ViewClip'])) {
6381                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
6382                         }
6383                         if (isset($this->viewer_preferences['PrintArea'])) {
6384                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
6385                         }
6386                         if (isset($this->viewer_preferences['PrintClip'])) {
6387                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
6388                         }
6389                         if (isset($this->viewer_preferences['PrintScaling'])) {
6390                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
6391                         }
6392                         if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
6393                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
6394                         }
6395                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
6396                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
6397                                         $this->_out('/PickTrayByPDFSize true');
6398                                 } else {
6399                                         $this->_out('/PickTrayByPDFSize false');
6400                                 }
6401                         }
6402                         if (isset($this->viewer_preferences['PrintPageRange'])) {
6403                                 $PrintPageRangeNum = '';
6404                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
6405                                         $PrintPageRangeNum .= ' '.($v - 1).'';
6406                                 }
6407                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
6408                         }
6409                         if (isset($this->viewer_preferences['NumCopies'])) {
6410                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
6411                         }
6412                         $this->_out('>>');
6413                 }
6414
6415                 /**
6416                 * Output trailer.
6417                 * @access protected
6418                 */
6419                 protected function _puttrailer() {
6420                         $this->_out('/Size '.($this->n + 1));
6421                         $this->_out('/Root '.$this->n.' 0 R');
6422                         $this->_out('/Info '.($this->n - 1).' 0 R');
6423                         if ($this->encrypted) {
6424                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
6425                                 $this->_out('/ID [()()]');
6426                         }
6427                 }
6428
6429                 /**
6430                 * Output PDF header.
6431                 * @access protected
6432                 */
6433                 protected function _putheader() {
6434                         $this->_out('%PDF-'.$this->PDFVersion);
6435                 }
6436
6437                 /**
6438                 * Output end of document (EOF).
6439                 * @access protected
6440                 */
6441                 protected function _enddoc() {
6442                         $this->state = 1;
6443                         $this->_putheader();
6444                         $this->_putpages();
6445                         $this->_putresources();
6446                         //Info
6447                         $this->_newobj();
6448                         $this->_out('<<');
6449                         $this->_putinfo();
6450                         $this->_out('>>');
6451                         $this->_out('endobj');
6452                         //Catalog
6453                         $this->_newobj();
6454                         $this->_out('<<');
6455                         $this->_putcatalog();
6456                         $this->_putcertification();
6457                         $this->_putuserrights();
6458                         $this->_out('>>');
6459                         $this->_out('endobj');
6460                         //Cross-ref
6461                         $o = $this->bufferlen;
6462                         $this->_out('xref');
6463                         $this->_out('0 '.($this->n + 1));
6464                         $this->_out('0000000000 65535 f ');
6465                         for ($i=1; $i <= $this->n; ++$i) {
6466                                 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
6467                         }
6468                         if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
6469                                 $this->_out('100000 '.count($this->embeddedfiles));
6470                                 foreach ($this->embeddedfiles as $filename => $filedata) {
6471                                         $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
6472                                 }
6473                         }
6474                         //Trailer
6475                         $this->_out('trailer');
6476                         $this->_out('<<');
6477                         $this->_puttrailer();
6478                         $this->_out('>>');
6479                         $this->_out('startxref');
6480                         $this->_out($o);
6481                         $this->_out('%%EOF');
6482                         $this->state = 3; // end-of-doc
6483                         if ($this->diskcache) {
6484                                 // remove temporary files used for images
6485                                 foreach ($this->imagekeys as $key) {
6486                                         // remove temporary files
6487                                         unlink($this->images[$key]);
6488                                 }
6489                                 foreach ($this->fontkeys as $key) {
6490                                         // remove temporary files
6491                                         unlink($this->fonts[$key]);
6492                                 }
6493                         }
6494                 }
6495
6496                 /**
6497                 * Initialize a new page.
6498                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
6499                 * @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>
6500                 * @access protected
6501                 */
6502                 protected function _beginpage($orientation='', $format='') {
6503                         ++$this->page;
6504                         $this->setPageBuffer($this->page, '');
6505                         // initialize array for graphics tranformation positions inside a page buffer
6506                         $this->transfmrk[$this->page] = array();
6507                         $this->state = 2;
6508                         if ($this->empty_string($orientation)) {
6509                                 if (isset($this->CurOrientation)) {
6510                                         $orientation = $this->CurOrientation;
6511                                 } else {
6512                                         $orientation = 'P';
6513                                 }
6514                         }
6515                         if ($this->empty_string($format)) {
6516                                 $this->setPageOrientation($orientation);
6517                         } else {
6518                                 $this->setPageFormat($format, $orientation);
6519                         }
6520                         if ($this->rtl) {
6521                                 $this->x = $this->w - $this->rMargin;
6522                         } else {
6523                                 $this->x = $this->lMargin;
6524                         }
6525                         $this->y = $this->tMargin;
6526                         if (isset($this->newpagegroup[$this->page])) {
6527                                 // start a new group
6528                                 $n = sizeof($this->pagegroups) + 1;
6529                                 $alias = '{nb'.$n.'}';
6530                                 $this->pagegroups[$alias] = 1;
6531                                 $this->currpagegroup = $alias;
6532                         } elseif ($this->currpagegroup) {
6533                                 ++$this->pagegroups[$this->currpagegroup];
6534                         }
6535                 }
6536
6537                 /**
6538                 * Mark end of page.
6539                 * @access protected
6540                 */
6541                 protected function _endpage() {
6542                         $this->setVisibility('all');
6543                         $this->state = 1;
6544                 }
6545
6546                 /**
6547                 * Begin a new object.
6548                 * @access protected
6549                 */
6550                 protected function _newobj() {
6551                         ++$this->n;
6552                         $this->offsets[$this->n] = $this->bufferlen;
6553                         $this->_out($this->n.' 0 obj');
6554                 }
6555
6556                 /**
6557                 * Underline text.
6558                 * @param int $x X coordinate
6559                 * @param int $y Y coordinate
6560                 * @param string $txt text to underline
6561                 * @access protected
6562                 */
6563                 protected function _dounderline($x, $y, $txt) {
6564                         $up = $this->CurrentFont['up'];
6565                         $ut = $this->CurrentFont['ut'];
6566                         $w = $this->GetStringWidth($txt);
6567                         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);
6568                 }
6569                 
6570                 /**
6571                 * Line through text.
6572                 * @param int $x X coordinate
6573                 * @param int $y Y coordinate
6574                 * @param string $txt text to linethrough
6575                 * @access protected
6576                 */
6577                 protected function _dolinethrough($x, $y, $txt) {
6578                         $up = $this->CurrentFont['up'];
6579                         $ut = $this->CurrentFont['ut'];
6580                         $w = $this->GetStringWidth($txt);
6581                         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);
6582                 }
6583                 
6584                 /**
6585                 * Read a 4-byte integer from file.
6586                 * @param string $f file name.
6587                 * @return 4-byte integer
6588                 * @access protected
6589                 */
6590                 protected function _freadint($f) {
6591                         $a = unpack('Ni', fread($f, 4));
6592                         return $a['i'];
6593                 }
6594                 
6595                 /**
6596                 * Add "\" before "\", "(" and ")"
6597                 * @param string $s string to escape.
6598                 * @return string escaped string.
6599                 * @access protected
6600                 */
6601                 protected function _escape($s) {
6602                         // the chr(13) substitution fixes the Bugs item #1421290.
6603                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
6604                 }
6605                 
6606                 /**
6607                 * Format a date string for meta information
6608                 * @param string $s date string to escape.
6609                 * @return string escaped string.
6610                 * @access protected
6611                 */
6612                 protected function _datastring($s) {
6613                         if ($this->encrypted) {
6614                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
6615                         }
6616                         return '('. $this->_escape($s).')';
6617                 }
6618                 
6619                 /**
6620                 * Format a text string for meta information
6621                 * @param string $s string to escape.
6622                 * @return string escaped string.
6623                 * @access protected
6624                 */
6625                 protected function _textstring($s) {
6626                         if ($this->isunicode) {
6627                                 //Convert string to UTF-16BE
6628                                 $s = $this->UTF8ToUTF16BE($s, true);
6629                         }
6630                         return $this->_datastring($s);
6631                 }
6632                                 
6633                 /**
6634                 * Format a text string
6635                 * @param string $s string to escape.
6636                 * @return string escaped string.
6637                 * @access protected
6638                 */
6639                 protected function _escapetext($s) {
6640                         if ($this->isunicode) {
6641                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
6642                                         $s = $this->UTF8ToLatin1($s);
6643                                 } else {
6644                                         //Convert string to UTF-16BE and reverse RTL language
6645                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
6646                                 }
6647                         }
6648                         return $this->_escape($s);
6649                 }
6650                 
6651                 /**
6652                 * Output a stream.
6653                 * @param string $s string to output.
6654                 * @access protected
6655                 */
6656                 protected function _putstream($s) {
6657                         if ($this->encrypted) {
6658                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
6659                         }
6660                         $this->_out('stream');
6661                         $this->_out($s);
6662                         $this->_out('endstream');
6663                 }
6664                 
6665                 /**
6666                 * Output a string to the document.
6667                 * @param string $s string to output.
6668                 * @access protected
6669                 */
6670                 protected function _out($s) {
6671                         if ($this->state == 2) {
6672                                 if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
6673                                         // puts data before page footer
6674                                         $pagebuff = $this->getPageBuffer($this->page);
6675                                         $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
6676                                         $footer = substr($pagebuff, -$this->footerlen[$this->page]);
6677                                         $this->setPageBuffer($this->page, $page.$s."\n".$footer);
6678                                         // update footer position
6679                                         $this->footerpos[$this->page] += strlen($s."\n");       
6680                                 } else {
6681                                         $this->setPageBuffer($this->page, $s."\n", true);
6682                                 }
6683                         } else {
6684                                 $this->setBuffer($s."\n");
6685                         }
6686                 }
6687                 
6688                  /**
6689                  * Converts UTF-8 strings to codepoints array.<br>
6690                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
6691                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
6692                  * <pre>
6693                  *        Char. number range  |        UTF-8 octet sequence
6694                  *       (hexadecimal)    |              (binary)
6695                  *    --------------------+-----------------------------------------------
6696                  *    0000 0000-0000 007F | 0xxxxxxx
6697                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
6698                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
6699                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
6700                  *    ---------------------------------------------------------------------
6701                  *
6702                  *   ABFN notation:
6703                  *   ---------------------------------------------------------------------
6704                  *   UTF8-octets = *( UTF8-char )
6705                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
6706                  *   UTF8-1      = %x00-7F
6707                  *   UTF8-2      = %xC2-DF UTF8-tail
6708                  *
6709                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
6710                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
6711                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
6712                  *                 %xF4 %x80-8F 2( UTF8-tail )
6713                  *   UTF8-tail   = %x80-BF
6714                  *   ---------------------------------------------------------------------
6715                  * </pre>
6716                  * @param string $str string to process.
6717                  * @return array containing codepoints (UTF-8 characters values)
6718                  * @access protected
6719                  * @author Nicola Asuni
6720                  * @since 1.53.0.TC005 (2005-01-05)
6721                  */
6722                 protected function UTF8StringToArray($str) {
6723                         if (isset($this->cache_UTF8StringToArray['_'.$str])) {
6724                                 // return cached value
6725                                 return($this->cache_UTF8StringToArray['_'.$str]);
6726                         }
6727                         // check cache size
6728                         if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
6729                                 // remove first element
6730                                 array_shift($this->cache_UTF8StringToArray);
6731                         }
6732                         ++$this->cache_size_UTF8StringToArray;
6733                         if (!$this->isunicode) {
6734                                 // split string into array of equivalent codes
6735                                 $strarr = array();
6736                                 $strlen = strlen($str);
6737                                 for ($i=0; $i < $strlen; ++$i) {
6738                                         $strarr[] = ord($str{$i});
6739                                 }
6740                                 // insert new value on cache
6741                                 $this->cache_UTF8StringToArray['_'.$str] = $strarr;
6742                                 return $strarr;
6743                         }
6744                         $unicode = array(); // array containing unicode values
6745                         $bytes  = array(); // array containing single character byte sequences
6746                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
6747                         $str .= ''; // force $str to be a string
6748                         $length = strlen($str);
6749                         for ($i = 0; $i < $length; ++$i) {
6750                                 $char = ord($str{$i}); // get one string character at time
6751                                 if (count($bytes) == 0) { // get starting octect
6752                                         if ($char <= 0x7F) {
6753                                                 $unicode[] = $char; // use the character "as is" because is ASCII
6754                                                 $numbytes = 1;
6755                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
6756                                                 $bytes[] = ($char - 0xC0) << 0x06; 
6757                                                 $numbytes = 2;
6758                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
6759                                                 $bytes[] = ($char - 0xE0) << 0x0C; 
6760                                                 $numbytes = 3;
6761                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
6762                                                 $bytes[] = ($char - 0xF0) << 0x12; 
6763                                                 $numbytes = 4;
6764                                         } else {
6765                                                 // use replacement character for other invalid sequences
6766                                                 $unicode[] = 0xFFFD;
6767                                                 $bytes = array();
6768                                                 $numbytes = 1;
6769                                         }
6770                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
6771                                         $bytes[] = $char - 0x80;
6772                                         if (count($bytes) == $numbytes) {
6773                                                 // compose UTF-8 bytes to a single unicode value
6774                                                 $char = $bytes[0];
6775                                                 for ($j = 1; $j < $numbytes; ++$j) {
6776                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
6777                                                 }
6778                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
6779                                                         /* The definition of UTF-8 prohibits encoding character numbers between
6780                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
6781                                                         encoding form (as surrogate pairs) and do not directly represent
6782                                                         characters. */
6783                                                         $unicode[] = 0xFFFD; // use replacement character
6784                                                 } else {
6785                                                         $unicode[] = $char; // add char to array
6786                                                 }
6787                                                 // reset data for next char
6788                                                 $bytes = array(); 
6789                                                 $numbytes = 1;
6790                                         }
6791                                 } else {
6792                                         // use replacement character for other invalid sequences
6793                                         $unicode[] = 0xFFFD;
6794                                         $bytes = array();
6795                                         $numbytes = 1;
6796                                 }
6797                         }
6798                         // insert new value on cache
6799                         $this->cache_UTF8StringToArray['_'.$str] = $unicode;
6800                         return $unicode;
6801                 }
6802                 
6803                 /**
6804                  * Converts UTF-8 strings to UTF16-BE.<br>
6805                  * @param string $str string to process.
6806                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
6807                  * @return string
6808                  * @access protected
6809                  * @author Nicola Asuni
6810                  * @since 1.53.0.TC005 (2005-01-05)
6811                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
6812                  */
6813                 protected function UTF8ToUTF16BE($str, $setbom=true) {
6814                         if (!$this->isunicode) {
6815                                 return $str; // string is not in unicode
6816                         }
6817                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6818                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
6819                 }
6820                 
6821                 /**
6822                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
6823                  * @param string $str string to process.
6824                  * @return string
6825                  * @author Andrew Whitehead, Nicola Asuni
6826                  * @access protected
6827                  * @since 3.2.000 (2008-06-23)
6828                  */
6829                 protected function UTF8ToLatin1($str) {
6830                         global $utf8tolatin;
6831                         if (!$this->isunicode) {
6832                                 return $str; // string is not in unicode
6833                         }
6834                         $outstr = ''; // string to be returned
6835                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
6836                         foreach ($unicode as $char) {
6837                                 if ($char < 256) {
6838                                         $outstr .= chr($char);
6839                                 } elseif (array_key_exists($char, $utf8tolatin)) {
6840                                         // map from UTF-8
6841                                         $outstr .= chr($utf8tolatin[$char]);
6842                                 } elseif ($char == 0xFFFD) {
6843                                         // skip
6844                                 } else {
6845                                         $outstr .= '?';
6846                                 }
6847                         }
6848                         return $outstr;
6849                 }
6850
6851                 /**
6852                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
6853                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
6854                  * <pre>
6855                  *   Encoding UTF-16:
6856                  * 
6857                  *   Encoding of a single character from an ISO 10646 character value to
6858                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
6859                  *    than 0x10FFFF.
6860                  * 
6861                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
6862                  *       terminate.
6863                  * 
6864                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
6865                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
6866                  *       represented in 20 bits.
6867                  * 
6868                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
6869                  *       0xDC00, respectively. These integers each have 10 bits free to
6870                  *       encode the character value, for a total of 20 bits.
6871                  * 
6872                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
6873                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
6874                  *       bits of W2. Terminate.
6875                  * 
6876                  *    Graphically, steps 2 through 4 look like:
6877                  *    U' = yyyyyyyyyyxxxxxxxxxx
6878                  *    W1 = 110110yyyyyyyyyy
6879                  *    W2 = 110111xxxxxxxxxx
6880                  * </pre>
6881                  * @param array $unicode array containing UTF-8 unicode values
6882                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
6883                  * @return string
6884                  * @access protected
6885                  * @author Nicola Asuni
6886                  * @since 2.1.000 (2008-01-08)
6887                  * @see UTF8ToUTF16BE()
6888                  */
6889                 protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
6890                         $outstr = ''; // string to be returned
6891                         if ($setbom) {
6892                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
6893                         }
6894                         foreach ($unicode as $char) {
6895                                 if ($char == 0xFFFD) {
6896                                         $outstr .= "\xFF\xFD"; // replacement character
6897                                 } elseif ($char < 0x10000) {
6898                                         $outstr .= chr($char >> 0x08);
6899                                         $outstr .= chr($char & 0xFF);
6900                                 } else {
6901                                         $char -= 0x10000;
6902                                         $w1 = 0xD800 | ($char >> 0x10);
6903                                         $w2 = 0xDC00 | ($char & 0x3FF); 
6904                                         $outstr .= chr($w1 >> 0x08);
6905                                         $outstr .= chr($w1 & 0xFF);
6906                                         $outstr .= chr($w2 >> 0x08);
6907                                         $outstr .= chr($w2 & 0xFF);
6908                                 }
6909                         }
6910                         return $outstr;
6911                 }
6912                 // ====================================================
6913                 
6914                 /**
6915                  * Set header font.
6916                  * @param array $font font
6917                  * @access public
6918                  * @since 1.1
6919                  */
6920                 public function setHeaderFont($font) {
6921                         $this->header_font = $font;
6922                 }
6923                 
6924                 /**
6925                  * Get header font.
6926                  * @return array()
6927                  * @access public
6928                  * @since 4.0.012 (2008-07-24)
6929                  */
6930                 public function getHeaderFont() {
6931                         return $this->header_font;
6932                 }
6933                 
6934                 /**
6935                  * Set footer font.
6936                  * @param array $font font
6937                  * @access public
6938                  * @since 1.1
6939                  */
6940                 public function setFooterFont($font) {
6941                         $this->footer_font = $font;
6942                 }
6943                 
6944                 /**
6945                  * Get Footer font.
6946                  * @return array()
6947                  * @access public
6948                  * @since 4.0.012 (2008-07-24)
6949                  */
6950                 public function getFooterFont() {
6951                         return $this->footer_font;
6952                 }
6953                 
6954                 /**
6955                  * Set language array.
6956                  * @param array $language
6957                  * @access public
6958                  * @since 1.1
6959                  */
6960                 public function setLanguageArray($language) {
6961                         $this->l = $language;
6962                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
6963                 }
6964                 
6965                 /**
6966                  * Returns the PDF data.
6967                  * @access public
6968                  */
6969                 public function getPDFData() {
6970                         if ($this->state < 3) {
6971                                 $this->Close();
6972                         }
6973                         return $this->buffer;
6974                 }
6975                                 
6976                 /**
6977                  * Output anchor link.
6978                  * @param string $url link URL or internal link (i.e.: &lt;a href="#23"&gt;link to page 23&lt;/a&gt;)
6979                  * @param string $name link name
6980                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
6981                  * @param boolean $firstline if true prints only the first line and return the remaining string.
6982                  * @param array $color array of RGB text color
6983                  * @param string $style font style (U, D, B, I)
6984                  * @return the number of cells used or the remaining text if $firstline = true;
6985                  * @access public
6986                  */
6987                 public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1) {
6988                         if (!$this->empty_string($url) AND ($url{0} == '#')) {
6989                                 // convert url to internal link
6990                                 $page = intval(substr($url, 1));
6991                                 $url = $this->AddLink();
6992                                 $this->SetLink($url, 0, $page);
6993                         }
6994                         // store current settings
6995                         $prevcolor = $this->fgcolor;
6996                         $prevstyle = $this->FontStyle;
6997                         if (empty($color)) {
6998                                 $this->SetTextColorArray($this->htmlLinkColorArray);
6999                         } else {
7000                                 $this->SetTextColorArray($color);
7001                         }
7002                         if ($style == -1) {
7003                                 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
7004                         } else {
7005                                 $this->SetFont('', $this->FontStyle.$style);
7006                         }
7007                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
7008                         // restore settings
7009                         $this->SetFont('', $prevstyle);
7010                         $this->SetTextColorArray($prevcolor);
7011                         return $ret;
7012                 }
7013                 
7014                 /**
7015                  * 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).
7016                  * @param string $color html color 
7017                  * @return array RGB color or false in case of error.
7018                  * @access public
7019                  */             
7020                 public function convertHTMLColorToDec($color='#FFFFFF') {
7021                         global $webcolor;
7022                         $returncolor = false;
7023                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
7024                         $color = strtolower($color);
7025                         if (strlen($color) == 0) {
7026                                 return false;
7027                         }
7028                         if (substr($color, 0, 3) == 'rgb') {
7029                                 $codes = substr($color, 4);
7030                                 $codes = str_replace(')', '', $codes);
7031                                 $returncolor = explode(',', $codes, 3);
7032                                 return $returncolor;
7033                         }
7034                         if (substr($color, 0, 1) != '#') {
7035                                 // decode color name
7036                                 if (isset($webcolor[$color])) {
7037                                         $color_code = $webcolor[$color];
7038                                 } else {
7039                                         return false;
7040                                 }
7041                         } else {
7042                                 $color_code = substr($color, 1);
7043                         }
7044                         switch (strlen($color_code)) {
7045                                 case 3: {
7046                                         // three-digit hexadecimal representation
7047                                         $r = substr($color_code, 0, 1);
7048                                         $g = substr($color_code, 1, 1);
7049                                         $b = substr($color_code, 2, 1);
7050                                         $returncolor['R'] = hexdec($r.$r);
7051                                         $returncolor['G'] = hexdec($g.$g);
7052                                         $returncolor['B'] = hexdec($b.$b);
7053                                         break;
7054                                 }
7055                                 case 6: {
7056                                         // six-digit hexadecimal representation
7057                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
7058                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
7059                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
7060                                         break;
7061                                 }
7062                         }
7063                         return $returncolor;
7064                 }
7065                 
7066                 /**
7067                  * Converts pixels to User's Units.
7068                  * @param int $px pixels
7069                  * @return float value in user's unit
7070                  * @access public
7071                  * @see setImageScale(), getImageScale()
7072                  */
7073                 public function pixelsToUnits($px) {
7074                         return ($px / ($this->imgscale * $this->k));
7075                 }
7076                         
7077                 /**
7078                  * Reverse function for htmlentities. 
7079                  * Convert entities in UTF-8.
7080                  * @param $text_to_convert Text to convert.
7081                  * @return string converted
7082                  * @access public
7083                 */
7084                 public function unhtmlentities($text_to_convert) {
7085                         return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
7086                 }
7087                 
7088                 // ENCRYPTION METHODS ----------------------------------
7089                 // SINCE 2.0.000 (2008-01-02)
7090                 
7091                 /**
7092                 * Compute encryption key depending on object number where the encrypted data is stored
7093                 * @param int $n object number
7094                 * @access protected
7095                 * @since 2.0.000 (2008-01-02)
7096                 */
7097                 protected function _objectkey($n) {
7098                         return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
7099                 }
7100                 
7101                 /**
7102                  * Put encryption on PDF document.
7103                  * @access protected
7104                  * @since 2.0.000 (2008-01-02)
7105                  */
7106                 protected function _putencryption() {
7107                         $this->_out('/Filter /Standard');
7108                         $this->_out('/V 1');
7109                         $this->_out('/R 2');
7110                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
7111                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
7112                         $this->_out('/P '.$this->Pvalue);
7113                 }
7114                 
7115                 /**
7116                 * Returns the input text exrypted using RC4 algorithm and the specified key.
7117                 * RC4 is the standard encryption algorithm used in PDF format
7118                 * @param string $key encryption key
7119                 * @param String $text input text to be encrypted
7120                 * @return String encrypted text
7121                 * @access protected
7122                 * @since 2.0.000 (2008-01-02)
7123                 * @author Klemen Vodopivec
7124                 */
7125                 protected function _RC4($key, $text) {
7126                         if ($this->last_rc4_key != $key) {
7127                                 $k = str_repeat($key, ((256 / strlen($key)) + 1));
7128                                 $rc4 = range(0, 255);
7129                                 $j = 0;
7130                                 for ($i = 0; $i < 256; ++$i) {
7131                                         $t = $rc4[$i];
7132                                         $j = ($j + $t + ord($k{$i})) % 256;
7133                                         $rc4[$i] = $rc4[$j];
7134                                         $rc4[$j] = $t;
7135                                 }
7136                                 $this->last_rc4_key = $key;
7137                                 $this->last_rc4_key_c = $rc4;
7138                         } else {
7139                                 $rc4 = $this->last_rc4_key_c;
7140                         }
7141                         $len = strlen($text);
7142                         $a = 0;
7143                         $b = 0;
7144                         $out = '';
7145                         for ($i = 0; $i < $len; ++$i) {
7146                                 $a = ($a + 1) % 256;
7147                                 $t = $rc4[$a];
7148                                 $b = ($b + $t) % 256;
7149                                 $rc4[$a] = $rc4[$b];
7150                                 $rc4[$b] = $t;
7151                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
7152                                 $out .= chr(ord($text{$i}) ^ $k);
7153                         }
7154                         return $out;
7155                 }
7156                 
7157                 /**
7158                 * Encrypts a string using MD5 and returns it's value as a binary string.
7159                 * @param string $str input string
7160                 * @return String MD5 encrypted binary string
7161                 * @access protected
7162                 * @since 2.0.000 (2008-01-02)
7163                 * @author Klemen Vodopivec
7164                 */
7165                 protected function _md5_16($str) {
7166                         return pack('H*', md5($str));
7167                 }
7168                 
7169                 /**
7170                 * Compute O value (used for RC4 encryption)
7171                 * @param String $user_pass user password
7172                 * @param String $owner_pass user password
7173                 * @return String O value
7174                 * @access protected
7175                 * @since 2.0.000 (2008-01-02)
7176                 * @author Klemen Vodopivec
7177                 */
7178                 protected function _Ovalue($user_pass, $owner_pass) {
7179                         $tmp = $this->_md5_16($owner_pass);
7180                         $owner_RC4_key = substr($tmp, 0, 5);
7181                         return $this->_RC4($owner_RC4_key, $user_pass);
7182                 }
7183                 
7184                 /**
7185                 * Compute U value (used for RC4 encryption)
7186                 * @return String U value
7187                 * @access protected
7188                 * @since 2.0.000 (2008-01-02)
7189                 * @author Klemen Vodopivec
7190                 */
7191                 protected function _Uvalue() {
7192                         return $this->_RC4($this->encryption_key, $this->padding);
7193                 }
7194                 
7195                 /**
7196                 * Compute encryption key
7197                 * @param String $user_pass user password
7198                 * @param String $owner_pass user password
7199                 * @param String $protection protection type
7200                 * @access protected
7201                 * @since 2.0.000 (2008-01-02)
7202                 * @author Klemen Vodopivec
7203                 */
7204                 protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
7205                         // Pad passwords
7206                         $user_pass = substr($user_pass.$this->padding, 0, 32);
7207                         $owner_pass = substr($owner_pass.$this->padding, 0, 32);
7208                         // Compute O value
7209                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
7210                         // Compute encyption key
7211                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
7212                         $this->encryption_key = substr($tmp, 0, 5);
7213                         // Compute U value
7214                         $this->Uvalue = $this->_Uvalue();
7215                         // Compute P value
7216                         $this->Pvalue = -(($protection^255) + 1);
7217                 }
7218                 
7219                 /**
7220                 * Set document protection
7221                 * The permission array is composed of values taken from the following ones:
7222                 * - copy: copy text and images to the clipboard
7223                 * - print: print the document
7224                 * - modify: modify it (except for annotations and forms)
7225                 * - annot-forms: add annotations and forms 
7226                 * Remark: the protection against modification is for people who have the full Acrobat product.
7227                 * 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.
7228                 * 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.
7229                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
7230                 * @param String $user_pass user password. Empty by default.
7231                 * @param String $owner_pass owner password. If not specified, a random value is used.
7232                 * @access public
7233                 * @since 2.0.000 (2008-01-02)
7234                 * @author Klemen Vodopivec
7235                 */
7236                 public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
7237                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
7238                         $protection = 192;
7239                         foreach ($permissions as $permission) {
7240                                 if (!isset($options[$permission])) {
7241                                         $this->Error('Incorrect permission: '.$permission);
7242                                 }
7243                                 $protection += $options[$permission];
7244                         }
7245                         if ($owner_pass === null) {
7246                                 $owner_pass = uniqid(rand());
7247                         }
7248                         $this->encrypted = true;
7249                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
7250                 }
7251                 
7252                 // END OF ENCRYPTION FUNCTIONS -------------------------
7253                 
7254                 // START TRANSFORMATIONS SECTION -----------------------
7255                 
7256                 /**
7257                 * Starts a 2D tranformation saving current graphic state.
7258                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
7259                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
7260                 * @access public
7261                 * @since 2.1.000 (2008-01-07)
7262                 * @see StartTransform(), StopTransform()
7263                 */
7264                 public function StartTransform() {
7265                         $this->_out('q');
7266                         $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
7267                 }
7268                 
7269                 /**
7270                 * Stops a 2D tranformation restoring previous graphic state.
7271                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
7272                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
7273                 * @access public
7274                 * @since 2.1.000 (2008-01-07)
7275                 * @see StartTransform(), StopTransform()
7276                 */
7277                 public function StopTransform() {
7278                         $this->_out('Q');
7279                         if (isset($this->transfmatrix)) {
7280                                 array_pop($this->transfmatrix);
7281                         }
7282                         array_pop($this->transfmrk[$this->page]);
7283                 }
7284                 /**
7285                 * Horizontal Scaling.
7286                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
7287                 * @param int $x abscissa of the scaling center. Default is current x position
7288                 * @param int $y ordinate of the scaling center. Default is current y position
7289                 * @access public
7290                 * @since 2.1.000 (2008-01-07)
7291                 * @see StartTransform(), StopTransform()
7292                 */
7293                 public function ScaleX($s_x, $x='', $y='') {
7294                         $this->Scale($s_x, 100, $x, $y);
7295                 }
7296                 
7297                 /**
7298                 * Vertical Scaling.
7299                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
7300                 * @param int $x abscissa of the scaling center. Default is current x position
7301                 * @param int $y ordinate of the scaling center. Default is current y position
7302                 * @access public
7303                 * @since 2.1.000 (2008-01-07)
7304                 * @see StartTransform(), StopTransform()
7305                 */
7306                 public function ScaleY($s_y, $x='', $y='') {
7307                         $this->Scale(100, $s_y, $x, $y);
7308                 }
7309                 
7310                 /**
7311                 * Vertical and horizontal proportional Scaling.
7312                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
7313                 * @param int $x abscissa of the scaling center. Default is current x position
7314                 * @param int $y ordinate of the scaling center. Default is current y position
7315                 * @access public
7316                 * @since 2.1.000 (2008-01-07)
7317                 * @see StartTransform(), StopTransform()
7318                 */
7319                 public function ScaleXY($s, $x='', $y='') {
7320                         $this->Scale($s, $s, $x, $y);
7321                 }
7322                 
7323                 /**
7324                 * Vertical and horizontal non-proportional Scaling.
7325                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
7326                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
7327                 * @param int $x abscissa of the scaling center. Default is current x position
7328                 * @param int $y ordinate of the scaling center. Default is current y position
7329                 * @access public
7330                 * @since 2.1.000 (2008-01-07)
7331                 * @see StartTransform(), StopTransform()
7332                 */
7333                 public function Scale($s_x, $s_y, $x='', $y='') {
7334                         if ($x === '') {
7335                                 $x = $this->x;
7336                         }
7337                         if ($y === '') {
7338                                 $y = $this->y;
7339                         }
7340                         if ($this->rtl) {
7341                                 $x = $this->w - $x;
7342                         }
7343                         if (($s_x == 0) OR ($s_y == 0)) {
7344                                 $this->Error('Please do not use values equal to zero for scaling');
7345                         }
7346                         $y = ($this->h - $y) * $this->k;
7347                         $x *= $this->k;
7348                         //calculate elements of transformation matrix
7349                         $s_x /= 100;
7350                         $s_y /= 100;
7351                         $tm[0] = $s_x;
7352                         $tm[1] = 0;
7353                         $tm[2] = 0;
7354                         $tm[3] = $s_y;
7355                         $tm[4] = $x * (1 - $s_x);
7356                         $tm[5] = $y * (1 - $s_y);
7357                         //scale the coordinate system
7358                         $this->Transform($tm);
7359                 }
7360                 
7361                 /**
7362                 * Horizontal Mirroring.
7363                 * @param int $x abscissa of the point. Default is current x position
7364                 * @access public
7365                 * @since 2.1.000 (2008-01-07)
7366                 * @see StartTransform(), StopTransform()
7367                 */
7368                 public function MirrorH($x='') {
7369                         $this->Scale(-100, 100, $x);
7370                 }
7371                 
7372                 /**
7373                 * Verical Mirroring.
7374                 * @param int $y ordinate of the point. Default is current y position
7375                 * @access public
7376                 * @since 2.1.000 (2008-01-07)
7377                 * @see StartTransform(), StopTransform()
7378                 */
7379                 public function MirrorV($y='') {
7380                         $this->Scale(100, -100, '', $y);
7381                 }
7382                 
7383                 /**
7384                 * Point reflection mirroring.
7385                 * @param int $x abscissa of the point. Default is current x position
7386                 * @param int $y ordinate of the point. Default is current y position
7387                 * @access public
7388                 * @since 2.1.000 (2008-01-07)
7389                 * @see StartTransform(), StopTransform()
7390                 */
7391                 public function MirrorP($x='',$y='') {
7392                         $this->Scale(-100, -100, $x, $y);
7393                 }
7394                 
7395                 /**
7396                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
7397                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
7398                 * @param int $x abscissa of the point. Default is current x position
7399                 * @param int $y ordinate of the point. Default is current y position
7400                 * @access public
7401                 * @since 2.1.000 (2008-01-07)
7402                 * @see StartTransform(), StopTransform()
7403                 */
7404                 public function MirrorL($angle=0, $x='',$y='') {
7405                         $this->Scale(-100, 100, $x, $y);
7406                         $this->Rotate(-2*($angle-90), $x, $y);
7407                 }
7408                 
7409                 /**
7410                 * Translate graphic object horizontally.
7411                 * @param int $t_x movement to the right (or left for RTL)
7412                 * @access public
7413                 * @since 2.1.000 (2008-01-07)
7414                 * @see StartTransform(), StopTransform()
7415                 */
7416                 public function TranslateX($t_x) {
7417                         $this->Translate($t_x, 0);
7418                 }
7419                 
7420                 /**
7421                 * Translate graphic object vertically.
7422                 * @param int $t_y movement to the bottom
7423                 * @access public
7424                 * @since 2.1.000 (2008-01-07)
7425                 * @see StartTransform(), StopTransform()
7426                 */
7427                 public function TranslateY($t_y) {
7428                         $this->Translate(0, $t_y);
7429                 }
7430                 
7431                 /**
7432                 * Translate graphic object horizontally and vertically.
7433                 * @param int $t_x movement to the right
7434                 * @param int $t_y movement to the bottom
7435                 * @access public
7436                 * @since 2.1.000 (2008-01-07)
7437                 * @see StartTransform(), StopTransform()
7438                 */
7439                 public function Translate($t_x, $t_y) {
7440                         if ($this->rtl) {
7441                                 $t_x = -$t_x;
7442                         }
7443                         //calculate elements of transformation matrix
7444                         $tm[0] = 1;
7445                         $tm[1] = 0;
7446                         $tm[2] = 0;
7447                         $tm[3] = 1;
7448                         $tm[4] = $t_x * $this->k;
7449                         $tm[5] = -$t_y * $this->k;
7450                         //translate the coordinate system
7451                         $this->Transform($tm);
7452                 }
7453                 
7454                 /**
7455                 * Rotate object.
7456                 * @param float $angle angle in degrees for counter-clockwise rotation
7457                 * @param int $x abscissa of the rotation center. Default is current x position
7458                 * @param int $y ordinate of the rotation center. Default is current y position
7459                 * @access public
7460                 * @since 2.1.000 (2008-01-07)
7461                 * @see StartTransform(), StopTransform()
7462                 */
7463                 public function Rotate($angle, $x='', $y='') {
7464                         if ($x === '') {
7465                                 $x = $this->x;
7466                         }
7467                         if ($y === '') {
7468                                 $y = $this->y;
7469                         }
7470                         if ($this->rtl) {
7471                                 $x = $this->w - $x;
7472                                 $angle = -$angle;
7473                         }
7474                         $y = ($this->h - $y) * $this->k;
7475                         $x *= $this->k;
7476                         //calculate elements of transformation matrix
7477                         $tm[0] = cos(deg2rad($angle));
7478                         $tm[1] = sin(deg2rad($angle));
7479                         $tm[2] = -$tm[1];
7480                         $tm[3] = $tm[0];
7481                         $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
7482                         $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
7483                         //rotate the coordinate system around ($x,$y)
7484                         $this->Transform($tm);
7485                 }
7486                 
7487                 /**
7488                 * Skew horizontally.
7489                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
7490                 * @param int $x abscissa of the skewing center. default is current x position
7491                 * @param int $y ordinate of the skewing center. default is current y position
7492                 * @access public
7493                 * @since 2.1.000 (2008-01-07)
7494                 * @see StartTransform(), StopTransform()
7495                 */
7496                 public function SkewX($angle_x, $x='', $y='') {
7497                         $this->Skew($angle_x, 0, $x, $y);
7498                 }
7499                 
7500                 /**
7501                 * Skew vertically.
7502                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
7503                 * @param int $x abscissa of the skewing center. default is current x position
7504                 * @param int $y ordinate of the skewing center. default is current y position
7505                 * @access public
7506                 * @since 2.1.000 (2008-01-07)
7507                 * @see StartTransform(), StopTransform()
7508                 */
7509                 public function SkewY($angle_y, $x='', $y='') {
7510                         $this->Skew(0, $angle_y, $x, $y);
7511                 }
7512                 
7513                 /**
7514                 * Skew.
7515                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
7516                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
7517                 * @param int $x abscissa of the skewing center. default is current x position
7518                 * @param int $y ordinate of the skewing center. default is current y position
7519                 * @access public
7520                 * @since 2.1.000 (2008-01-07)
7521                 * @see StartTransform(), StopTransform()
7522                 */
7523                 public function Skew($angle_x, $angle_y, $x='', $y='') {
7524                         if ($x === '') {
7525                                 $x = $this->x;
7526                         }
7527                         if ($y === '') {
7528                                 $y = $this->y;
7529                         }
7530                         if ($this->rtl) {
7531                                 $x = $this->w - $x;
7532                                 $angle_x = -$angle_x;
7533                         }
7534                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
7535                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
7536                         }
7537                         $x *= $this->k;
7538                         $y = ($this->h - $y) * $this->k;
7539                         //calculate elements of transformation matrix
7540                         $tm[0] = 1;
7541                         $tm[1] = tan(deg2rad($angle_y));
7542                         $tm[2] = tan(deg2rad($angle_x));
7543                         $tm[3] = 1;
7544                         $tm[4] = -$tm[2] * $y;
7545                         $tm[5] = -$tm[1] * $x;
7546                         //skew the coordinate system
7547                         $this->Transform($tm);
7548                 }
7549                 
7550                 /**
7551                 * Apply graphic transformations.
7552                 * @access protected
7553                 * @since 2.1.000 (2008-01-07)
7554                 * @see StartTransform(), StopTransform()
7555                 */
7556                 protected function Transform($tm) {
7557                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
7558                         // store transformation matrix
7559                         $this->transfmatrix[] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
7560                         // update tranformation mark
7561                         if (end($this->transfmrk[$this->page]) !== false) {
7562                                 $key = key($this->transfmrk[$this->page]);
7563                                 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
7564                         }
7565                 }
7566                 
7567                 // END TRANSFORMATIONS SECTION -------------------------
7568                 
7569                 
7570                 // START GRAPHIC FUNCTIONS SECTION ---------------------
7571                 // The following section is based on the code provided by David Hernandez Sanz
7572                 
7573                 /**
7574                 * 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.
7575                 * @param float $width The width.
7576                 * @access public
7577                 * @since 1.0
7578                 * @see Line(), Rect(), Cell(), MultiCell()
7579                 */
7580                 public function SetLineWidth($width) {
7581                         //Set line width
7582                         $this->LineWidth = $width;
7583                         $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
7584                         $this->_out($this->linestyleWidth);
7585                 }
7586                 
7587                 /**
7588                 * Returns the current the line width.
7589                 * @return int Line width 
7590                 * @access public
7591                 * @since 2.1.000 (2008-01-07)
7592                 * @see Line(), SetLineWidth()
7593                 */
7594                 public function GetLineWidth() {
7595                         return $this->LineWidth;
7596                 }
7597                 
7598                 /**
7599                 * Set line style.
7600                 * @param array $style Line style. Array with keys among the following:
7601                 * <ul>
7602                 *        <li>width (float): Width of the line in user units.</li>
7603                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
7604                 * butt, round, square. The difference between "square" and "butt" is that
7605                 * "square" projects a flat end past the end of the line.</li>
7606                 *        <li>join (string): Type of join. Possible values are: miter, round,
7607                 * bevel.</li>
7608                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
7609                 * series of length values, which are the lengths of the on and off dashes.
7610                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
7611                 * 1 off, 2 on, 1 off, ...</li>
7612                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
7613                 * the point at which the pattern starts.</li>
7614                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
7615                 * </ul>
7616                 * @access public
7617                 * @since 2.1.000 (2008-01-08)
7618                 */
7619                 public function SetLineStyle($style) {
7620                         extract($style);
7621                         if (isset($width)) {
7622                                 $width_prev = $this->LineWidth;
7623                                 $this->SetLineWidth($width);
7624                                 $this->LineWidth = $width_prev;
7625                         }
7626                         if (isset($cap)) {
7627                                 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
7628                                 if (isset($ca[$cap])) {
7629                                         $this->linestyleCap = $ca[$cap].' J';
7630                                         $this->_out($this->linestyleCap);
7631                                 }
7632                         }
7633                         if (isset($join)) {
7634                                 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
7635                                 if (isset($ja[$join])) {
7636                                         $this->linestyleJoin = $ja[$join].' j';
7637                                         $this->_out($this->linestyleJoin);
7638                                 }
7639                         }
7640                         if (isset($dash)) {
7641                                 $dash_string = '';
7642                                 if ($dash) {
7643                                         if (preg_match('/^.+,/', $dash)) {
7644                                                 $tab = explode(',', $dash);
7645                                         } else {
7646                                                 $tab = array($dash);
7647                                         }
7648                                         $dash_string = '';
7649                                         foreach ($tab as $i => $v) {
7650                                                 if ($i) {
7651                                                         $dash_string .= ' ';
7652                                                 }
7653                                                 $dash_string .= sprintf("%.2F", $v);
7654                                         }
7655                                 }
7656                                 if (!isset($phase) OR !$dash) {
7657                                         $phase = 0;
7658                                 }
7659                                 $this->linestyleDash = sprintf("[%s] %.2F d", $dash_string, $phase);
7660                                 $this->_out($this->linestyleDash);
7661                         }
7662                         if (isset($color)) {
7663                                 $this->SetDrawColorArray($color);
7664                         }
7665                 }
7666                 
7667                 /*
7668                 * Set a draw point.
7669                 * @param float $x Abscissa of point.
7670                 * @param float $y Ordinate of point.
7671                 * @access protected
7672                 * @since 2.1.000 (2008-01-08)
7673                 */
7674                 protected function _outPoint($x, $y) {
7675                         if ($this->rtl) {
7676                                 $x = $this->w - $x;
7677                         }
7678                         $this->_out(sprintf("%.2F %.2F m", $x * $this->k, ($this->h - $y) * $this->k));
7679                 }
7680                 
7681                 /*
7682                 * Draws a line from last draw point.
7683                 * @param float $x Abscissa of end point.
7684                 * @param float $y Ordinate of end point.
7685                 * @access protected
7686                 * @since 2.1.000 (2008-01-08)
7687                 */
7688                 protected function _outLine($x, $y) {
7689                         if ($this->rtl) {
7690                                 $x = $this->w - $x;
7691                         }
7692                         $this->_out(sprintf("%.2F %.2F l", $x * $this->k, ($this->h - $y) * $this->k));
7693                 }
7694                 
7695                 /**
7696                 * Draws a rectangle.
7697                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
7698                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
7699                 * @param float $w Width.
7700                 * @param float $h Height.
7701                 * @param string $op options
7702                 * @access protected
7703                 * @since 2.1.000 (2008-01-08)
7704                 */
7705                 protected function _outRect($x, $y, $w, $h, $op) {
7706                         if ($this->rtl) {
7707                                 $x = $this->w - $x - $w;
7708                         }
7709                         $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
7710                 }
7711                 
7712                 /*
7713                 * Draws a Bezier curve from last draw point.
7714                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
7715                 * @param float $x1 Abscissa of control point 1.
7716                 * @param float $y1 Ordinate of control point 1.
7717                 * @param float $x2 Abscissa of control point 2.
7718                 * @param float $y2 Ordinate of control point 2.
7719                 * @param float $x3 Abscissa of end point.
7720                 * @param float $y3 Ordinate of end point.
7721                 * @access protected
7722                 * @since 2.1.000 (2008-01-08)
7723                 */
7724                 protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
7725                         if ($this->rtl) {
7726                                 $x1 = $this->w - $x1;
7727                                 $x2 = $this->w - $x2;
7728                                 $x3 = $this->w - $x3;
7729                         }
7730                         $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));
7731                 }
7732                 
7733                 /**
7734                 * Draws a line between two points.
7735                 * @param float $x1 Abscissa of first point.
7736                 * @param float $y1 Ordinate of first point.
7737                 * @param float $x2 Abscissa of second point.
7738                 * @param float $y2 Ordinate of second point.
7739                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7740                 * @access public
7741                 * @since 1.0
7742                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
7743                 */
7744                 public function Line($x1, $y1, $x2, $y2, $style=array()) {
7745                         if ($style) {
7746                                 $this->SetLineStyle($style);
7747                         }
7748                         $this->_outPoint($x1, $y1);
7749                         $this->_outLine($x2, $y2);
7750                         $this->_out(' S');
7751                 }
7752                 
7753                 /**
7754                 * Draws a rectangle.
7755                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
7756                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
7757                 * @param float $w Width.
7758                 * @param float $h Height.
7759                 * @param string $style Style of rendering. Possible values are:
7760                 * <ul>
7761                 *        <li>D or empty string: Draw (default).</li>
7762                 *        <li>F: Fill.</li>
7763                 *        <li>DF or FD: Draw and fill.</li>
7764                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7765                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7766                 * </ul>
7767                 * @param array $border_style Border style of rectangle. Array with keys among the following:
7768                 * <ul>
7769                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
7770                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
7771                 * </ul>
7772                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
7773                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7774                 * @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).
7775                 * @access public
7776                 * @since 1.0
7777                 * @see SetLineStyle()
7778                 */
7779                 public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
7780                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7781                                 $this->SetFillColorArray($fill_color);
7782                         }
7783                         switch ($style) {
7784                                 case 'F': {
7785                                         $op = 'f';
7786                                         $border_style = array();
7787                                         $this->_outRect($x, $y, $w, $h, $op);
7788                                         break;
7789                                 }
7790                                 case 'DF':
7791                                 case 'FD': {
7792                                         if ((!$border_style) OR (isset($border_style['all']))) {
7793                                                 $op = 'B';
7794                                                 if (isset($border_style['all'])) {
7795                                                         $this->SetLineStyle($border_style['all']);
7796                                                         $border_style = array();
7797                                                 }
7798                                         } else {
7799                                                 $op = 'f';
7800                                         }
7801                                         $this->_outRect($x, $y, $w, $h, $op);
7802                                         break;
7803                                 }
7804                                 case 'CNZ': {
7805                                         $op = 'W n';
7806                                         $this->_outRect($x, $y, $w, $h, $op);
7807                                         break;
7808                                 }
7809                                 case 'CEO': {
7810                                         $op = 'W* n';
7811                                         $this->_outRect($x, $y, $w, $h, $op);
7812                                         break;
7813                                 }
7814                                 default: {
7815                                         $op = 'S';
7816                                         if ((!$border_style) OR (isset($border_style['all']))) {
7817                                                 if (isset($border_style['all']) AND $border_style['all']) {
7818                                                         $this->SetLineStyle($border_style['all']);
7819                                                         $border_style = array();
7820                                                 }
7821                                                 $this->_outRect($x, $y, $w, $h, $op);
7822                                         }
7823                                         break;
7824                                 }
7825                         }
7826                         if ($border_style) {
7827                                 $border_style2 = array();
7828                                 foreach ($border_style as $line => $value) {
7829                                         $lenght = strlen($line);
7830                                         for ($i = 0; $i < $lenght; ++$i) {
7831                                                 $border_style2[$line[$i]] = $value;
7832                                         }
7833                                 }
7834                                 $border_style = $border_style2;
7835                                 if (isset($border_style['L']) AND $border_style['L']) {
7836                                         $this->Line($x, $y, $x, $y + $h, $border_style['L']);
7837                                 }
7838                                 if (isset($border_style['T']) AND $border_style['T']) {
7839                                         $this->Line($x, $y, $x + $w, $y, $border_style['T']);
7840                                 }
7841                                 if (isset($border_style['R']) AND $border_style['R']) {
7842                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
7843                                 }
7844                                 if (isset($border_style['B']) AND $border_style['B']) {
7845                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
7846                                 }
7847                         }
7848                 }
7849                 
7850                 
7851                 /**
7852                 * Draws a Bezier curve.
7853                 * The Bezier curve is a tangent to the line between the control points at
7854                 * either end of the curve.
7855                 * @param float $x0 Abscissa of start point.
7856                 * @param float $y0 Ordinate of start point.
7857                 * @param float $x1 Abscissa of control point 1.
7858                 * @param float $y1 Ordinate of control point 1.
7859                 * @param float $x2 Abscissa of control point 2.
7860                 * @param float $y2 Ordinate of control point 2.
7861                 * @param float $x3 Abscissa of end point.
7862                 * @param float $y3 Ordinate of end point.
7863                 * @param string $style Style of rendering. Possible values are:
7864                 * <ul>
7865                 *        <li>D or empty string: Draw (default).</li>
7866                 *        <li>F: Fill.</li>
7867                 *        <li>DF or FD: Draw and fill.</li>
7868                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7869                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7870                 * </ul>
7871                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7872                 * @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).
7873                 * @access public
7874                 * @see SetLineStyle()
7875                 * @since 2.1.000 (2008-01-08)
7876                 */
7877                 public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
7878                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7879                                 $this->SetFillColorArray($fill_color);
7880                         }
7881                         switch ($style) {
7882                                 case 'F': {
7883                                         $op = 'f';
7884                                         $line_style = array();
7885                                         break;
7886                                 }
7887                                 case 'FD': 
7888                                 case 'DF': {
7889                                         $op = 'B';
7890                                         break;
7891                                 }
7892                                 case 'CNZ': {
7893                                         $op = 'W n';
7894                                         break;
7895                                 }
7896                                 case 'CEO': {
7897                                         $op = 'W* n';
7898                                         break;
7899                                 }
7900                                 default: {
7901                                         $op = 'S';
7902                                         break;
7903                                 }
7904                         }
7905                         if ($line_style) {
7906                                 $this->SetLineStyle($line_style);
7907                         }
7908                         $this->_outPoint($x0, $y0);
7909                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7910                         $this->_out($op);
7911                 }
7912                 
7913                 /**
7914                 * Draws a poly-Bezier curve.
7915                 * Each Bezier curve segment is a tangent to the line between the control points at
7916                 * either end of the curve.
7917                 * @param float $x0 Abscissa of start point.
7918                 * @param float $y0 Ordinate of start point.
7919                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
7920                 * @param string $style Style of rendering. Possible values are:
7921                 * <ul>
7922                 *        <li>D or empty string: Draw (default).</li>
7923                 *        <li>F: Fill.</li>
7924                 *        <li>DF or FD: Draw and fill.</li>
7925                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7926                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7927                 * </ul>
7928                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7929                 * @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).
7930                 * @access public
7931                 * @see SetLineStyle()
7932                 * @since 3.0008 (2008-05-12)
7933                 */
7934                 public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
7935                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
7936                                 $this->SetFillColorArray($fill_color);
7937                         }
7938                         switch ($style) {
7939                                 case 'F': {
7940                                         $op = 'f';
7941                                         $line_style = array();
7942                                         break;
7943                                 }
7944                                 case 'FD':
7945                                 case 'DF': {
7946                                         $op = 'B';
7947                                         break;
7948                                 }
7949                                 case 'CNZ': {
7950                                         $op = 'W n';
7951                                         break;
7952                                 }
7953                                 case 'CEO': {
7954                                         $op = 'W* n';
7955                                         break;
7956                                 }
7957                                 default: {
7958                                         $op = 'S';
7959                                         break;
7960                                 }
7961                         }
7962                         if ($line_style) {
7963                                 $this->SetLineStyle($line_style);
7964                         }
7965                         $this->_outPoint($x0, $y0);
7966                         foreach ($segments as $segment) {
7967                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
7968                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
7969                         }       
7970                         $this->_out($op);
7971                 }
7972                 
7973                 /**
7974                 * Draws an ellipse.
7975                 * An ellipse is formed from n Bezier curves.
7976                 * @param float $x0 Abscissa of center point.
7977                 * @param float $y0 Ordinate of center point.
7978                 * @param float $rx Horizontal radius.
7979                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
7980                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
7981                 * @param float $astart: Angle start of draw line. Default value: 0.
7982                 * @param float $afinish: Angle finish of draw line. Default value: 360.
7983                 * @param string $style Style of rendering. Possible values are:
7984                 * <ul>
7985                 *        <li>D or empty string: Draw (default).</li>
7986                 *        <li>F: Fill.</li>
7987                 *        <li>DF or FD: Draw and fill.</li>
7988                 *        <li>C: Draw close.</li>
7989                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
7990                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
7991                 * </ul>
7992                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
7993                 * @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).
7994                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
7995                 * @access public
7996                 * @since 2.1.000 (2008-01-08)
7997                 */
7998                 public function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
7999                         if ($angle) {
8000                                 $this->StartTransform();
8001                                 $this->Rotate($angle, $x0, $y0);
8002                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8003                                 $this->StopTransform();
8004                                 return;
8005                         }
8006                         if ($rx) {
8007                                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8008                                         $this->SetFillColorArray($fill_color);
8009                                 }
8010                                 switch ($style) {
8011                                         case 'F': {
8012                                                 $op = 'f';
8013                                                 $line_style = array();
8014                                                 break;
8015                                         }
8016                                         case 'FD': 
8017                                         case 'DF': {
8018                                                 $op = 'B';
8019                                                 break;
8020                                         }
8021                                         case 'C': {
8022                                                 $op = 's'; // Small 's' signifies closing the path as well
8023                                                 break;
8024                                         }
8025                                         case 'CNZ': {
8026                                                 $op = 'W n';
8027                                                 break;
8028                                         }
8029                                         case 'CEO': {
8030                                                 $op = 'W* n';
8031                                                 break;
8032                                         }
8033                                         default: {
8034                                                 $op = 'S';
8035                                                 break;
8036                                         }
8037                                 }
8038                                 if ($line_style) {
8039                                         $this->SetLineStyle($line_style);
8040                                 }
8041                                 if (!$ry) {
8042                                         $ry = $rx;
8043                                 }
8044                                 $rx *= $this->k;
8045                                 $ry *= $this->k;
8046                                 if ($nc < 2) {
8047                                         $nc = 2;
8048                                 }
8049                                 $astart = deg2rad((float) $astart);
8050                                 $afinish = deg2rad((float) $afinish);
8051                                 $total_angle = $afinish - $astart;
8052                                 $dt = $total_angle / $nc;
8053                                 $dtm = $dt / 3;
8054                                 $x0 *= $this->k;
8055                                 $y0 = ($this->h - $y0) * $this->k;
8056                                 $t1 = $astart;
8057                                 $a0 = $x0 + ($rx * cos($t1));
8058                                 $b0 = $y0 + ($ry * sin($t1));
8059                                 $c0 = -$rx * sin($t1);
8060                                 $d0 = $ry * cos($t1);
8061                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
8062                                 for ($i = 1; $i <= $nc; ++$i) {
8063                                         // Draw this bit of the total curve
8064                                         $t1 = ($i * $dt) + $astart;
8065                                         $a1 = $x0 + ($rx * cos($t1));
8066                                         $b1 = $y0 + ($ry * sin($t1));
8067                                         $c1 = -$rx * sin($t1);
8068                                         $d1 = $ry * cos($t1);
8069                                         $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));
8070                                         $a0 = $a1;
8071                                         $b0 = $b1;
8072                                         $c0 = $c1;
8073                                         $d0 = $d1;
8074                                 }
8075                                 $this->_out($op);
8076                         }
8077                 }
8078                 
8079                 /**
8080                 * Draws a circle.
8081                 * A circle is formed from n Bezier curves.
8082                 * @param float $x0 Abscissa of center point.
8083                 * @param float $y0 Ordinate of center point.
8084                 * @param float $r Radius.
8085                 * @param float $astart: Angle start of draw line. Default value: 0.
8086                 * @param float $afinish: Angle finish of draw line. Default value: 360.
8087                 * @param string $style Style of rendering. Possible values are:
8088                 * <ul>
8089                 *        <li>D or empty string: Draw (default).</li>
8090                 *        <li>F: Fill.</li>
8091                 *        <li>DF or FD: Draw and fill.</li>
8092                 *        <li>C: Draw close.</li>
8093                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8094                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8095                 * </ul>
8096                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8097                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8098                 * @param integer $nc Number of curves used in circle. Default value: 8.
8099                 * @access public
8100                 * @since 2.1.000 (2008-01-08)
8101                 */
8102                 public function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=8) {
8103                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
8104                 }
8105                 
8106                 /**
8107                 * Draws a polygon.
8108                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
8109                 * @param string $style Style of rendering. Possible values are:
8110                 * <ul>
8111                 *        <li>D or empty string: Draw (default).</li>
8112                 *        <li>F: Fill.</li>
8113                 *        <li>DF or FD: Draw and fill.</li>
8114                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8115                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8116                 * </ul>
8117                 * @param array $line_style Line style of polygon. Array with keys among the following:
8118                 * <ul>
8119                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
8120                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
8121                 * </ul>
8122                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
8123                 * @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).
8124                 * @access public
8125                 * @since 2.1.000 (2008-01-08)
8126                 */
8127                 public function Polygon($p, $style='', $line_style=array(), $fill_color=array()) {
8128                         $np = count($p) / 2;
8129                         if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8130                                 $this->SetFillColorArray($fill_color);
8131                         }
8132                         switch ($style) {
8133                                 case 'F': {
8134                                         $line_style = array();
8135                                         $op = 'f';
8136                                         break;
8137                                 }
8138                                 case 'FD': 
8139                                 case 'DF': {
8140                                         $op = 'B';
8141                                         break;
8142                                 }
8143                                 case 'CNZ': {
8144                                         $op = 'W n';
8145                                         break;
8146                                 }
8147                                 case 'CEO': {
8148                                         $op = 'W* n';
8149                                         break;
8150                                 }                               
8151                                 default: {
8152                                         $op = 'S';
8153                                         break;
8154                                 }
8155                         }
8156                         $draw = true;
8157                         if ($line_style) {
8158                                 if (isset($line_style['all'])) {
8159                                         $this->SetLineStyle($line_style['all']);
8160                                 } else { // 0 .. (np - 1), op = {B, S}
8161                                         $draw = false;
8162                                         if ('B' == $op) {
8163                                                 $op = 'f';
8164                                                 $this->_outPoint($p[0], $p[1]);
8165                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8166                                                         $this->_outLine($p[$i], $p[$i + 1]);
8167                                                 }
8168                                                 $this->_outLine($p[0], $p[1]);
8169                                                 $this->_out($op);
8170                                         }
8171                                         $p[($np * 2)] = $p[0];
8172                                         $p[(($np * 2) + 1)] = $p[1];
8173                                         for ($i = 0; $i < $np; ++$i) {
8174                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
8175                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
8176                                                 }
8177                                         }
8178                                 }
8179                         }
8180                         if ($draw) {
8181                                 $this->_outPoint($p[0], $p[1]);
8182                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
8183                                         $this->_outLine($p[$i], $p[$i + 1]);
8184                                 }
8185                                 $this->_outLine($p[0], $p[1]);
8186                                 $this->_out($op);
8187                         }
8188                 }
8189                 
8190                 /**
8191                 * Draws a regular polygon.
8192                 * @param float $x0 Abscissa of center point.
8193                 * @param float $y0 Ordinate of center point.
8194                 * @param float $r: Radius of inscribed circle.
8195                 * @param integer $ns Number of sides.
8196                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
8197                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
8198                 * @param string $style Style of rendering. Possible values are:
8199                 * <ul>
8200                 *        <li>D or empty string: Draw (default).</li>
8201                 *        <li>F: Fill.</li>
8202                 *        <li>DF or FD: Draw and fill.</li>
8203                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8204                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8205                 * </ul>
8206                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
8207                 * <ul>
8208                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
8209                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
8210                 * </ul>
8211                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
8212                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8213                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
8214                 * <ul>
8215                 *        <li>D or empty string: Draw (default).</li>
8216                 *        <li>F: Fill.</li>
8217                 *        <li>DF or FD: Draw and fill.</li>
8218                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8219                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8220                 * </ul>
8221                 * @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).
8222                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
8223                 * @access public
8224                 * @since 2.1.000 (2008-01-08)
8225                 */
8226                 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()) {
8227                         if (3 > $ns) {
8228                                 $ns = 3;
8229                         }
8230                         if ($draw_circle) {
8231                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8232                         }
8233                         $p = array();
8234                         for ($i = 0; $i < $ns; ++$i) {
8235                                 $a = $angle + ($i * 360 / $ns);
8236                                 $a_rad = deg2rad((float) $a);
8237                                 $p[] = $x0 + ($r * sin($a_rad));
8238                                 $p[] = $y0 + ($r * cos($a_rad));
8239                         }
8240                         $this->Polygon($p, $style, $line_style, $fill_color);
8241                 }
8242                 
8243                 /**
8244                 * Draws a star polygon
8245                 * @param float $x0 Abscissa of center point.
8246                 * @param float $y0 Ordinate of center point.
8247                 * @param float $r Radius of inscribed circle.
8248                 * @param integer $nv Number of vertices.
8249                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
8250                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
8251                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
8252                 * @param string $style Style of rendering. Possible values are:
8253                 * <ul>
8254                 *        <li>D or empty string: Draw (default).</li>
8255                 *        <li>F: Fill.</li>
8256                 *        <li>DF or FD: Draw and fill.</li>
8257                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8258                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8259                 * </ul>
8260                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
8261                 * <ul>
8262                 *        <li>all: Line style of all sides. Array like for
8263                 * {@link SetLineStyle SetLineStyle}.</li>
8264                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
8265                 * </ul>
8266                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
8267                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
8268                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
8269                 * <ul>
8270                 *        <li>D or empty string: Draw (default).</li>
8271                 *        <li>F: Fill.</li>
8272                 *        <li>DF or FD: Draw and fill.</li>
8273                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8274                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8275                 * </ul>
8276                 * @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).
8277                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
8278                 * @access public
8279                 * @since 2.1.000 (2008-01-08)
8280                 */
8281                 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()) {
8282                         if (2 > $nv) {
8283                                 $nv = 2;
8284                         }
8285                         if ($draw_circle) {
8286                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
8287                         }
8288                         $p2 = array();
8289                         $visited = array();
8290                         for ($i = 0; $i < $nv; ++$i) {
8291                                 $a = $angle + ($i * 360 / $nv);
8292                                 $a_rad = deg2rad((float) $a);
8293                                 $p2[] = $x0 + ($r * sin($a_rad));
8294                                 $p2[] = $y0 + ($r * cos($a_rad));
8295                                 $visited[] = false;
8296                         }
8297                         $p = array();
8298                         $i = 0;
8299                         do {
8300                                 $p[] = $p2[$i * 2];
8301                                 $p[] = $p2[($i * 2) + 1];
8302                                 $visited[$i] = true;
8303                                 $i += $ng;
8304                                 $i %= $nv;
8305                         } while (!$visited[$i]);
8306                         $this->Polygon($p, $style, $line_style, $fill_color);
8307                 }
8308                 
8309                 /**
8310                 * Draws a rounded rectangle.
8311                 * @param float $x Abscissa of upper-left corner.
8312                 * @param float $y Ordinate of upper-left corner.
8313                 * @param float $w Width.
8314                 * @param float $h Height.
8315                 * @param float $r Radius of the rounded corners.
8316                 * @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").
8317                 * @param string $style Style of rendering. Possible values are:
8318                 * <ul>
8319                 *        <li>D or empty string: Draw (default).</li>
8320                 *        <li>F: Fill.</li>
8321                 *        <li>DF or FD: Draw and fill.</li>
8322                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
8323                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
8324                 * </ul>
8325                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
8326                 * @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).
8327                 * @access public
8328                 * @since 2.1.000 (2008-01-08)
8329                 */
8330                 public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
8331                         if ('0000' == $round_corner) { // Not rounded
8332                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
8333                         } else { // Rounded
8334                                 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
8335                                         $this->SetFillColorArray($fill_color);
8336                                 }
8337                                 switch ($style) {
8338                                         case 'F': {
8339                                                 $border_style = array();
8340                                                 $op = 'f';
8341                                                 break;
8342                                         }
8343                                         case 'FD': 
8344                                         case 'DF': {
8345                                                 $op = 'B';
8346                                                 break;
8347                                         }
8348                                         case 'CNZ': {
8349                                                 $op = 'W n';
8350                                                 break;
8351                                         }
8352                                         case 'CEO': {
8353                                                 $op = 'W* n';
8354                                                 break;
8355                                         }
8356                                         default: {
8357                                                 $op = 'S';
8358                                                 break;
8359                                         }
8360                                 }
8361                                 if ($border_style) {
8362                                         $this->SetLineStyle($border_style);
8363                                 }
8364                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
8365                                 $this->_outPoint($x + $r, $y);
8366                                 $xc = $x + $w - $r;
8367                                 $yc = $y + $r;
8368                                 $this->_outLine($xc, $y);
8369                                 if ($round_corner[0]) {
8370                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
8371                                 } else {
8372                                         $this->_outLine($x + $w, $y);
8373                                 }
8374                                 $xc = $x + $w - $r;
8375                                 $yc = $y + $h - $r;
8376                                 $this->_outLine($x + $w, $yc);
8377                                 if ($round_corner[1]) {
8378                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
8379                                 } else {
8380                                         $this->_outLine($x + $w, $y + $h);
8381                                 }
8382                                 $xc = $x + $r;
8383                                 $yc = $y + $h - $r;
8384                                 $this->_outLine($xc, $y + $h);
8385                                 if ($round_corner[2]) {
8386                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
8387                                 } else {
8388                                         $this->_outLine($x, $y + $h);
8389                                 }
8390                                 $xc = $x + $r;
8391                                 $yc = $y + $r;
8392                                 $this->_outLine($x, $yc);
8393                                 if ($round_corner[3]) {
8394                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
8395                                 } else {
8396                                         $this->_outLine($x, $y);
8397                                         $this->_outLine($x + $r, $y);
8398                                 }
8399                                 $this->_out($op);
8400                         }
8401                 }
8402                 
8403                 // END GRAPHIC FUNCTIONS SECTION -----------------------
8404                 
8405                 // BIDIRECTIONAL TEXT SECTION --------------------------
8406                 /**
8407                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
8408                  * @param string $str string to manipulate.
8409                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
8410                  * @return string
8411                  * @access protected
8412                  * @author Nicola Asuni
8413                  * @since 2.1.000 (2008-01-08)
8414                 */
8415                 protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
8416                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $str, $forcertl), $setbom);
8417                 }
8418                 
8419                 /**
8420                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
8421                  * @param array $ta array of characters composing the string.
8422                  * @param string $str string to process
8423                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
8424                  * @return string
8425                  * @author Nicola Asuni
8426                  * @access protected
8427                  * @since 2.4.000 (2008-03-06)
8428                 */
8429                 protected function utf8Bidi($ta, $str='', $forcertl=false) {
8430                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
8431                         // paragraph embedding level
8432                         $pel = 0;
8433                         // max level
8434                         $maxlevel = 0;
8435                         if ($this->empty_string($str)) {
8436                                 // create string from array
8437                                 $str = $this->UTF8ArrSubString($ta);
8438                         }
8439                         // check if string contains arabic text
8440                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
8441                                 $arabic = true;
8442                         } else {
8443                                 $arabic = false;
8444                         }
8445                         // check if string contains RTL text
8446                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
8447                                 return $ta;
8448                         }
8449                         
8450                         // get number of chars
8451                         $numchars = count($ta);
8452                         
8453                         if ($forcertl == 'R') {
8454                                         $pel = 1;
8455                         } elseif ($forcertl == 'L') {
8456                                         $pel = 0;
8457                         } else {
8458                                 // P2. In each paragraph, find the first character of type L, AL, or R.
8459                                 // 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.
8460                                 for ($i=0; $i < $numchars; ++$i) {
8461                                         $type = $unicode[$ta[$i]];
8462                                         if ($type == 'L') {
8463                                                 $pel = 0;
8464                                                 break;
8465                                         } elseif (($type == 'AL') OR ($type == 'R')) {
8466                                                 $pel = 1;
8467                                                 break;
8468                                         }
8469                                 }
8470                         }
8471                         
8472                         // Current Embedding Level
8473                         $cel = $pel;
8474                         // directional override status
8475                         $dos = 'N';
8476                         $remember = array();
8477                         // start-of-level-run
8478                         $sor = $pel % 2 ? 'R' : 'L';
8479                         $eor = $sor;
8480                         
8481                         // Array of characters data
8482                         $chardata = Array();
8483                         
8484                         // 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.
8485                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
8486                         for ($i=0; $i < $numchars; ++$i) {
8487                                 if ($ta[$i] == K_RLE) {
8488                                         // X2. With each RLE, compute the least greater odd embedding level.
8489                                         //      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.
8490                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8491                                         $next_level = $cel + ($cel % 2) + 1;
8492                                         if ($next_level < 62) {
8493                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
8494                                                 $cel = $next_level;
8495                                                 $dos = 'N';
8496                                                 $sor = $eor;
8497                                                 $eor = $cel % 2 ? 'R' : 'L';
8498                                         }
8499                                 } elseif ($ta[$i] == K_LRE) {
8500                                         // X3. With each LRE, compute the least greater even embedding level.
8501                                         //      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.
8502                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8503                                         $next_level = $cel + 2 - ($cel % 2);
8504                                         if ( $next_level < 62 ) {
8505                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
8506                                                 $cel = $next_level;
8507                                                 $dos = 'N';
8508                                                 $sor = $eor;
8509                                                 $eor = $cel % 2 ? 'R' : 'L';
8510                                         }
8511                                 } elseif ($ta[$i] == K_RLO) {
8512                                         // X4. With each RLO, compute the least greater odd embedding level.
8513                                         //      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.
8514                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8515                                         $next_level = $cel + ($cel % 2) + 1;
8516                                         if ($next_level < 62) {
8517                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
8518                                                 $cel = $next_level;
8519                                                 $dos = 'R';
8520                                                 $sor = $eor;
8521                                                 $eor = $cel % 2 ? 'R' : 'L';
8522                                         }
8523                                 } elseif ($ta[$i] == K_LRO) {
8524                                         // X5. With each LRO, compute the least greater even embedding level.
8525                                         //      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.
8526                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
8527                                         $next_level = $cel + 2 - ($cel % 2);
8528                                         if ( $next_level < 62 ) {
8529                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
8530                                                 $cel = $next_level;
8531                                                 $dos = 'L';
8532                                                 $sor = $eor;
8533                                                 $eor = $cel % 2 ? 'R' : 'L';
8534                                         }
8535                                 } elseif ($ta[$i] == K_PDF) {
8536                                         // 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.
8537                                         if (count($remember)) {
8538                                                 $last = count($remember ) - 1;
8539                                                 if (($remember[$last]['num'] == K_RLE) OR 
8540                                                           ($remember[$last]['num'] == K_LRE) OR 
8541                                                           ($remember[$last]['num'] == K_RLO) OR 
8542                                                           ($remember[$last]['num'] == K_LRO)) {
8543                                                         $match = array_pop($remember);
8544                                                         $cel = $match['cel'];
8545                                                         $dos = $match['dos'];
8546                                                         $sor = $eor;
8547                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
8548                                                 }
8549                                         }
8550                                 } elseif (($ta[$i] != K_RLE) AND
8551                                                                  ($ta[$i] != K_LRE) AND
8552                                                                  ($ta[$i] != K_RLO) AND
8553                                                                  ($ta[$i] != K_LRO) AND
8554                                                                  ($ta[$i] != K_PDF)) {
8555                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
8556                                         //      a. Set the level of the current character to the current embedding level.
8557                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
8558                                         if ($dos != 'N') {
8559                                                 $chardir = $dos;
8560                                         } else {
8561                                                 $chardir = $unicode[$ta[$i]];
8562                                         }
8563                                         // stores string characters and other information
8564                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
8565                                 }
8566                         } // end for each char
8567                         
8568                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
8569                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
8570                         // 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.
8571                         
8572                         // 3.3.3 Resolving Weak Types
8573                         // 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.
8574                         // Nonspacing marks are now resolved based on the previous characters.
8575                         $numchars = count($chardata);
8576                         
8577                         // 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.
8578                         $prevlevel = -1; // track level changes
8579                         $levcount = 0; // counts consecutive chars at the same level
8580                         for ($i=0; $i < $numchars; ++$i) {
8581                                 if ($chardata[$i]['type'] == 'NSM') {
8582                                         if ($levcount) {
8583                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8584                                         } elseif ($i > 0) {
8585                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
8586                                         }
8587                                 }
8588                                 if ($chardata[$i]['level'] != $prevlevel) {
8589                                         $levcount = 0;
8590                                 } else {
8591                                         ++$levcount;
8592                                 }
8593                                 $prevlevel = $chardata[$i]['level'];
8594                         }
8595                         
8596                         // 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.
8597                         $prevlevel = -1;
8598                         $levcount = 0;
8599                         for ($i=0; $i < $numchars; ++$i) {
8600                                 if ($chardata[$i]['char'] == 'EN') {
8601                                         for ($j=$levcount; $j >= 0; $j--) {
8602                                                 if ($chardata[$j]['type'] == 'AL') {
8603                                                         $chardata[$i]['type'] = 'AN';
8604                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
8605                                                         break;
8606                                                 }
8607                                         }
8608                                 }
8609                                 if ($chardata[$i]['level'] != $prevlevel) {
8610                                         $levcount = 0;
8611                                 } else {
8612                                         ++$levcount;
8613                                 }
8614                                 $prevlevel = $chardata[$i]['level'];
8615                         }
8616                         
8617                         // W3. Change all ALs to R.
8618                         for ($i=0; $i < $numchars; ++$i) {
8619                                 if ($chardata[$i]['type'] == 'AL') {
8620                                         $chardata[$i]['type'] = 'R';
8621                                 } 
8622                         }
8623                         
8624                         // 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.
8625                         $prevlevel = -1;
8626                         $levcount = 0;
8627                         for ($i=0; $i < $numchars; ++$i) {
8628                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8629                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8630                                                 $chardata[$i]['type'] = 'EN';
8631                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
8632                                                 $chardata[$i]['type'] = 'EN';
8633                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
8634                                                 $chardata[$i]['type'] = 'AN';
8635                                         }
8636                                 }
8637                                 if ($chardata[$i]['level'] != $prevlevel) {
8638                                         $levcount = 0;
8639                                 } else {
8640                                         ++$levcount;
8641                                 }
8642                                 $prevlevel = $chardata[$i]['level'];
8643                         }
8644                         
8645                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
8646                         $prevlevel = -1;
8647                         $levcount = 0;
8648                         for ($i=0; $i < $numchars; ++$i) {
8649                                 if ($chardata[$i]['type'] == 'ET') {
8650                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
8651                                                 $chardata[$i]['type'] = 'EN';
8652                                         } else {
8653                                                 $j = $i+1;
8654                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
8655                                                         if ($chardata[$j]['type'] == 'EN') {
8656                                                                 $chardata[$i]['type'] = 'EN';
8657                                                                 break;
8658                                                         } elseif ($chardata[$j]['type'] != 'ET') {
8659                                                                 break;
8660                                                         }
8661                                                         ++$j;
8662                                                 }
8663                                         }
8664                                 }
8665                                 if ($chardata[$i]['level'] != $prevlevel) {
8666                                         $levcount = 0;
8667                                 } else {
8668                                         ++$levcount;
8669                                 }
8670                                 $prevlevel = $chardata[$i]['level'];
8671                         }
8672                         
8673                         // W6. Otherwise, separators and terminators change to Other Neutral.
8674                         $prevlevel = -1;
8675                         $levcount = 0;
8676                         for ($i=0; $i < $numchars; ++$i) {
8677                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
8678                                         $chardata[$i]['type'] = 'ON';
8679                                 }
8680                                 if ($chardata[$i]['level'] != $prevlevel) {
8681                                         $levcount = 0;
8682                                 } else {
8683                                         ++$levcount;
8684                                 }
8685                                 $prevlevel = $chardata[$i]['level'];
8686                         }
8687                         
8688                         //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.
8689                         $prevlevel = -1;
8690                         $levcount = 0;
8691                         for ($i=0; $i < $numchars; ++$i) {
8692                                 if ($chardata[$i]['char'] == 'EN') {
8693                                         for ($j=$levcount; $j >= 0; $j--) {
8694                                                 if ($chardata[$j]['type'] == 'L') {
8695                                                         $chardata[$i]['type'] = 'L';
8696                                                 } elseif ($chardata[$j]['type'] == 'R') {
8697                                                         break;
8698                                                 }
8699                                         }
8700                                 }
8701                                 if ($chardata[$i]['level'] != $prevlevel) {
8702                                         $levcount = 0;
8703                                 } else {
8704                                         ++$levcount;
8705                                 }
8706                                 $prevlevel = $chardata[$i]['level'];
8707                         }
8708                         
8709                         // 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.
8710                         $prevlevel = -1;
8711                         $levcount = 0;
8712                         for ($i=0; $i < $numchars; ++$i) {
8713                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8714                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8715                                                 $chardata[$i]['type'] = 'L';
8716                                         } elseif (($chardata[$i]['type'] == 'N') AND
8717                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8718                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8719                                                 $chardata[$i]['type'] = 'R';
8720                                         } elseif ($chardata[$i]['type'] == 'N') {
8721                                                 // N2. Any remaining neutrals take the embedding direction
8722                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8723                                         }
8724                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
8725                                         // first char
8726                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
8727                                                 $chardata[$i]['type'] = 'L';
8728                                         } elseif (($chardata[$i]['type'] == 'N') AND
8729                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
8730                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
8731                                                 $chardata[$i]['type'] = 'R';
8732                                         } elseif ($chardata[$i]['type'] == 'N') {
8733                                                 // N2. Any remaining neutrals take the embedding direction
8734                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8735                                         }
8736                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
8737                                         //last char
8738                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
8739                                                 $chardata[$i]['type'] = 'L';
8740                                         } elseif (($chardata[$i]['type'] == 'N') AND
8741                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
8742                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
8743                                                 $chardata[$i]['type'] = 'R';
8744                                         } elseif ($chardata[$i]['type'] == 'N') {
8745                                                 // N2. Any remaining neutrals take the embedding direction
8746                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
8747                                         }
8748                                 } elseif ($chardata[$i]['type'] == 'N') {
8749                                         // N2. Any remaining neutrals take the embedding direction
8750                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
8751                                 }
8752                                 if ($chardata[$i]['level'] != $prevlevel) {
8753                                         $levcount = 0;
8754                                 } else {
8755                                         ++$levcount;
8756                                 }
8757                                 $prevlevel = $chardata[$i]['level'];
8758                         }
8759                         
8760                         // 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.
8761                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
8762                         for ($i=0; $i < $numchars; ++$i) {
8763                                 $odd = $chardata[$i]['level'] % 2;
8764                                 if ($odd) {
8765                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8766                                                 $chardata[$i]['level'] += 1;
8767                                         }
8768                                 } else {
8769                                         if ($chardata[$i]['type'] == 'R') {
8770                                                 $chardata[$i]['level'] += 1;
8771                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
8772                                                 $chardata[$i]['level'] += 2;
8773                                         }
8774                                 }
8775                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
8776                         }
8777                         
8778                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
8779                         //      1. Segment separators,
8780                         //      2. Paragraph separators,
8781                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
8782                         //      4. Any sequence of white space characters at the end of the line.
8783                         for ($i=0; $i < $numchars; ++$i) {
8784                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
8785                                         $chardata[$i]['level'] = $pel;
8786                                 } elseif ($chardata[$i]['type'] == 'WS') {
8787                                         $j = $i+1;
8788                                         while ($j < $numchars) {
8789                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
8790                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
8791                                                         $chardata[$i]['level'] = $pel;
8792                                                         break;
8793                                                 } elseif ($chardata[$j]['type'] != 'WS') {
8794                                                         break;
8795                                                 }
8796                                                 ++$j;
8797                                         }
8798                                 }
8799                         }
8800                         
8801                         // Arabic Shaping
8802                         // 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. 
8803                         if ($arabic) {
8804                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
8805                                 $alfletter = array(1570,1571,1573,1575);
8806                                 $chardata2 = $chardata;
8807                                 $laaletter = false;
8808                                 $charAL = array();
8809                                 $x = 0;
8810                                 for ($i=0; $i < $numchars; ++$i) {
8811                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
8812                                                 $charAL[$x] = $chardata[$i];
8813                                                 $charAL[$x]['i'] = $i;
8814                                                 $chardata[$i]['x'] = $x;
8815                                                 ++$x;
8816                                         }
8817                                 }
8818                                 $numAL = $x;
8819                                 for ($i=0; $i < $numchars; ++$i) {
8820                                         $thischar = $chardata[$i];
8821                                         if ($i > 0) {
8822                                                 $prevchar = $chardata[($i-1)];
8823                                         } else {
8824                                                 $prevchar = false;
8825                                         }
8826                                         if (($i+1) < $numchars) {
8827                                                 $nextchar = $chardata[($i+1)];
8828                                         } else {
8829                                                 $nextchar = false;
8830                                         }
8831                                         if ($unicode[$thischar['char']] == 'AL') {
8832                                                 $x = $thischar['x'];
8833                                                 if ($x > 0) {
8834                                                         $prevchar = $charAL[($x-1)];
8835                                                 } else {
8836                                                         $prevchar = false;
8837                                                 }
8838                                                 if (($x+1) < $numAL) {
8839                                                         $nextchar = $charAL[($x+1)];
8840                                                 } else {
8841                                                         $nextchar = false;
8842                                                 }
8843                                                 // if laa letter
8844                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
8845                                                         $arabicarr = $laa_array;
8846                                                         $laaletter = true;
8847                                                         if ($x > 1) {
8848                                                                 $prevchar = $charAL[($x-2)];
8849                                                         } else {
8850                                                                 $prevchar = false;
8851                                                         }
8852                                                 } else {
8853                                                         $arabicarr = $unicode_arlet;
8854                                                         $laaletter = false;
8855                                                 }
8856                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
8857                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8858                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8859                                                         ($prevchar['type'] == $thischar['type']) AND
8860                                                         ($nextchar['type'] == $thischar['type']) AND
8861                                                         ($nextchar['char'] != 1567)) {
8862                                                         if (in_array($prevchar['char'], $endedletter)) {
8863                                                                 if (isset($arabicarr[$thischar['char']][2])) {
8864                                                                         // initial
8865                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8866                                                                 }
8867                                                         } else {
8868                                                                 if (isset($arabicarr[$thischar['char']][3])) {
8869                                                                         // medial
8870                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
8871                                                                 }
8872                                                         }
8873                                                 } elseif (($nextchar !== false) AND
8874                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
8875                                                         ($nextchar['type'] == $thischar['type']) AND
8876                                                         ($nextchar['char'] != 1567)) {
8877                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
8878                                                                 // initial
8879                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
8880                                                         }
8881                                                 } elseif ((($prevchar !== false) AND
8882                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
8883                                                         ($prevchar['type'] == $thischar['type'])) OR
8884                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
8885                                                         // final
8886                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
8887                                                                 ($chardata[$i-1]['char'] == 1604) AND
8888                                                                 ($chardata[$i-2]['char'] == 1604)) {
8889                                                                 //Allah Word
8890                                                                 // mark characters to delete with false
8891                                                                 $chardata2[$i-2]['char'] = false;
8892                                                                 $chardata2[$i-1]['char'] = false; 
8893                                                                 $chardata2[$i]['char'] = 65010;
8894                                                         } else {
8895                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
8896                                                                         if (isset($arabicarr[$thischar['char']][0])) {
8897                                                                                 // isolated
8898                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8899                                                                         }
8900                                                                 } else {
8901                                                                         if (isset($arabicarr[$thischar['char']][1])) {
8902                                                                                 // final
8903                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
8904                                                                         }
8905                                                                 }
8906                                                         }
8907                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
8908                                                         // isolated
8909                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
8910                                                 }
8911                                                 // if laa letter
8912                                                 if ($laaletter) {
8913                                                         // mark characters to delete with false
8914                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
8915                                                 }
8916                                         } // end if AL (Arabic Letter)
8917                                 } // end for each char
8918                                 /* 
8919                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594. 
8920                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
8921                                  */
8922                                 $cw = &$this->CurrentFont['cw'];
8923                                 for ($i = 0; $i < ($numchars-1); ++$i) {
8924                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
8925                                                 // check if the subtitution font is defined on current font
8926                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
8927                                                         $chardata2[$i]['char'] = false;
8928                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
8929                                                 }
8930                                         }
8931                                 }
8932                                 // remove marked characters
8933                                 foreach ($chardata2 as $key => $value) {
8934                                         if ($value['char'] === false) {
8935                                                 unset($chardata2[$key]);
8936                                         }
8937                                 }
8938                                 $chardata = array_values($chardata2);
8939                                 $numchars = count($chardata);
8940                                 unset($chardata2);
8941                                 unset($arabicarr);
8942                                 unset($laaletter);
8943                                 unset($charAL);
8944                         }
8945                         
8946                         // 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.
8947                         for ($j=$maxlevel; $j > 0; $j--) {
8948                                 $ordarray = Array();
8949                                 $revarr = Array();
8950                                 $onlevel = false;
8951                                 for ($i=0; $i < $numchars; ++$i) {
8952                                         if ($chardata[$i]['level'] >= $j) {
8953                                                 $onlevel = true;
8954                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
8955                                                         // 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.
8956                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
8957                                                 }
8958                                                 $revarr[] = $chardata[$i];
8959                                         } else {
8960                                                 if ($onlevel) {
8961                                                         $revarr = array_reverse($revarr);
8962                                                         $ordarray = array_merge($ordarray, $revarr);
8963                                                         $revarr = Array();
8964                                                         $onlevel = false;
8965                                                 }
8966                                                 $ordarray[] = $chardata[$i];
8967                                         }
8968                                 }
8969                                 if ($onlevel) {
8970                                         $revarr = array_reverse($revarr);
8971                                         $ordarray = array_merge($ordarray, $revarr);
8972                                 }
8973                                 $chardata = $ordarray;
8974                         }
8975                         
8976                         $ordarray = array();
8977                         for ($i=0; $i < $numchars; ++$i) {
8978                                 $ordarray[] = $chardata[$i]['char'];
8979                         }
8980                         
8981                         return $ordarray;
8982                 }
8983                 
8984                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
8985                 
8986                 /*
8987                 * Adds a bookmark.
8988                 * @param string $txt bookmark description.
8989                 * @param int $level bookmark level (minimum value is 0).
8990                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
8991                 * @param int $page target page number (leave empty for current page).
8992                 * @access public
8993                 * @author Olivier Plathey, Nicola Asuni
8994                 * @since 2.1.002 (2008-02-12)
8995                 */
8996                 public function Bookmark($txt, $level=0, $y=-1, $page='') {
8997                         if ($level < 0) {
8998                                 $level = 0;
8999                         }
9000                         if (isset($this->outlines[0])) {
9001                                 $lastoutline = end($this->outlines);
9002                                 $maxlevel = $lastoutline['l'] + 1;
9003                         } else {
9004                                 $maxlevel = 0;
9005                         }
9006                         if ($level > $maxlevel) {
9007                                 $level = $maxlevel;
9008                         }
9009                         if ($y == -1) {
9010                                 $y = $this->GetY();
9011                         }
9012                         if (empty($page)) {
9013                                 $page = $this->PageNo();
9014                         }
9015                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
9016                 }
9017                 
9018                 /*
9019                 * Create a bookmark PDF string.
9020                 * @access protected
9021                 * @author Olivier Plathey, Nicola Asuni
9022                 * @since 2.1.002 (2008-02-12)
9023                 */
9024                 protected function _putbookmarks() {
9025                         $nb = count($this->outlines);
9026                         if ($nb == 0) {
9027                                 return;
9028                         }
9029                         $lru = array();
9030                         $level = 0;
9031                         foreach ($this->outlines as $i => $o) {
9032                                 if ($o['l'] > 0) {
9033                                         $parent = $lru[($o['l'] - 1)];
9034                                         //Set parent and last pointers
9035                                         $this->outlines[$i]['parent'] = $parent;
9036                                         $this->outlines[$parent]['last'] = $i;
9037                                         if ($o['l'] > $level) {
9038                                                 //Level increasing: set first pointer
9039                                                 $this->outlines[$parent]['first'] = $i;
9040                                         }
9041                                 } else {
9042                                         $this->outlines[$i]['parent'] = $nb;
9043                                 }
9044                                 if (($o['l'] <= $level) AND ($i > 0)) {
9045                                         //Set prev and next pointers
9046                                         $prev = $lru[$o['l']];
9047                                         $this->outlines[$prev]['next'] = $i;
9048                                         $this->outlines[$i]['prev'] = $prev;
9049                                 }
9050                                 $lru[$o['l']] = $i;
9051                                 $level = $o['l'];
9052                         }
9053                         //Outline items
9054                         $n = $this->n + 1;
9055                         foreach ($this->outlines as $i => $o) {
9056                                 $this->_newobj();
9057                                 $this->_out('<</Title '.$this->_textstring($o['t']));
9058                                 $this->_out('/Parent '.($n + $o['parent']).' 0 R');
9059                                 if (isset($o['prev']))
9060                                 $this->_out('/Prev '.($n + $o['prev']).' 0 R');
9061                                 if (isset($o['next']))
9062                                 $this->_out('/Next '.($n + $o['next']).' 0 R');
9063                                 if (isset($o['first']))
9064                                 $this->_out('/First '.($n + $o['first']).' 0 R');
9065                                 if (isset($o['last']))
9066                                 $this->_out('/Last '.($n + $o['last']).' 0 R');
9067                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k))));
9068                                 $this->_out('/Count 0>>');
9069                                 $this->_out('endobj');
9070                         }
9071                         //Outline root
9072                         $this->_newobj();
9073                         $this->OutlineRoot = $this->n;
9074                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
9075                         $this->_out('/Last '.($n + $lru[0]).' 0 R>>');
9076                         $this->_out('endobj');
9077                 }
9078                 
9079                 
9080                 // --- JAVASCRIPT - FORMS ------------------------------
9081                 
9082                 /*
9083                 * Adds a javascript
9084                 * @access public
9085                 * @author Johannes G�ntert, Nicola Asuni
9086                 * @since 2.1.002 (2008-02-12)
9087                 */
9088                 public function IncludeJS($script) {
9089                         $this->javascript .= $script;
9090                 }
9091                 
9092                 /*
9093                 * Create a javascript PDF string.
9094                 * @access protected
9095                 * @author Johannes G�ntert, Nicola Asuni
9096                 * @since 2.1.002 (2008-02-12)
9097                 */
9098                 protected function _putjavascript() {
9099                         if (empty($this->javascript)) {
9100                                 return;
9101                         }
9102                         // the following two lines are uded to avoid form fields duplication after saving
9103                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
9104                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
9105                         $this->_newobj();
9106                         $this->n_js = $this->n;
9107                         $this->_out('<<');
9108                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
9109                         $this->_out('>>');
9110                         $this->_out('endobj');
9111                         $this->_newobj();
9112                         $this->_out('<<');
9113                         $this->_out('/S /JavaScript');
9114                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
9115                         $this->_out('>>');
9116                         $this->_out('endobj');
9117                 }
9118                 
9119                 /*
9120                 * Convert color to javascript color.
9121                 * @param string $color color name or #RRGGBB
9122                 * @access protected
9123                 * @author Denis Van Nuffelen, Nicola Asuni
9124                 * @since 2.1.002 (2008-02-12)
9125                 */
9126                 protected function _JScolor($color) {
9127                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
9128                         if (substr($color,0,1) == '#') {
9129                                 return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
9130                         }
9131                         if (!in_array($color,$aColors)) {
9132                                 $this->Error('Invalid color: '.$color);
9133                         }
9134                         return 'color.'.$color;
9135                 }
9136                 
9137                 /*
9138                 * Adds a javascript form field.
9139                 * @param string $type field type
9140                 * @param string $name field name
9141                 * @param int $x horizontal position
9142                 * @param int $y vertical position
9143                 * @param int $w width
9144                 * @param int $h height
9145                 * @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>
9146                 * @access protected
9147                 * @author Denis Van Nuffelen, Nicola Asuni
9148                 * @since 2.1.002 (2008-02-12)
9149                 */
9150                 protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
9151                         if ($this->rtl) {
9152                                 $x = $x - $w;
9153                         }
9154                         // the followind avoid fields duplication after saving the document
9155                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
9156                         $k = $this->k;
9157                         $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";
9158                         $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
9159                         while (list($key, $val) = each($prop)) {
9160                                 if (strcmp(substr($key, -5), 'Color') == 0) {
9161                                         $val = $this->_JScolor($val);
9162                                 } else {
9163                                         $val = "'".$val."'";
9164                                 }
9165                                 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
9166                         }
9167                         if ($this->rtl) {
9168                                 $this->x -= $w;
9169                         } else {
9170                                 $this->x += $w;
9171                         }
9172                         $this->javascript .= '}';
9173                 }
9174                 
9175                 /*
9176                 * Creates a text field
9177                 * @param string $name field name
9178                 * @param int $w width
9179                 * @param int $h height
9180                 * @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>
9181                 * @access public
9182                 * @author Denis Van Nuffelen, Nicola Asuni
9183                 * @since 2.1.002 (2008-02-12)
9184                 */
9185                 public function TextField($name, $w, $h, $prop=array()) {
9186                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
9187                 }
9188                 
9189                 /*
9190                 * Creates a RadioButton field
9191                 * @param string $name field name
9192                 * @param int $w width
9193                 * @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>
9194                 * @access public
9195                 * @author Nicola Asuni
9196                 * @since 2.2.003 (2008-03-03)
9197                 */
9198                 public function RadioButton($name, $w, $prop=array()) {
9199                         if (!isset($prop['strokeColor'])) {
9200                                 $prop['strokeColor']='black';
9201                         }
9202                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
9203                 }
9204                 
9205                 /*
9206                 * Creates a List-box field
9207                 * @param string $name field name
9208                 * @param int $w width
9209                 * @param int $h height
9210                 * @param array $values array containing the list of values.
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 Nicola Asuni
9214                 * @since 2.2.003 (2008-03-03)
9215                 */
9216                 public function ListBox($name, $w, $h, $values, $prop=array()) {
9217                         if (!isset($prop['strokeColor'])) {
9218                                 $prop['strokeColor'] = 'ltGray';
9219                         }
9220                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
9221                         $s = '';
9222                         foreach ($values as $value) {
9223                                 $s .= "'".addslashes($value)."',";
9224                         }
9225                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9226                 }
9227                 
9228                 /*
9229                 * Creates a Combo-box field
9230                 * @param string $name field name
9231                 * @param int $w width
9232                 * @param int $h height
9233                 * @param array $values array containing the list of values.
9234                 * @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>
9235                 * @access public
9236                 * @author Denis Van Nuffelen, Nicola Asuni
9237                 * @since 2.1.002 (2008-02-12)
9238                 */
9239                 public function ComboBox($name, $w, $h, $values, $prop=array()) {
9240                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
9241                         $s = '';
9242                         foreach ($values as $value) {
9243                                 $s .= "'".addslashes($value)."',";
9244                         }
9245                         $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
9246                 }
9247                 
9248                 /*
9249                 * Creates a CheckBox field
9250                 * @param string $name field name
9251                 * @param int $w width
9252                 * @param boolean $checked define the initial state (default = false).
9253                 * @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>
9254                 * @access public
9255                 * @author Denis Van Nuffelen, Nicola Asuni
9256                 * @since 2.1.002 (2008-02-12)
9257                 */
9258                 public function CheckBox($name, $w, $checked=false, $prop=array()) {
9259                         $prop['value'] = ($checked ? 'Yes' : 'Off');
9260                         if (!isset($prop['strokeColor'])) {
9261                                 $prop['strokeColor'] = 'black';
9262                         }
9263                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
9264                 }
9265                 
9266                 /*
9267                 * Creates a button field
9268                 * @param string $name field name
9269                 * @param int $w width
9270                 * @param int $h height
9271                 * @param string $caption caption.
9272                 * @param string $action action triggered by the button (JavaScript code).
9273                 * @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>
9274                 * @access public
9275                 * @author Denis Van Nuffelen, Nicola Asuni
9276                 * @since 2.1.002 (2008-02-12)
9277                 */
9278                 public function Button($name, $w, $h, $caption, $action, $prop=array()) {
9279                         if (!isset($prop['strokeColor'])) {
9280                                 $prop['strokeColor'] = 'black';
9281                         }
9282                         if (!isset($prop['borderStyle'])) {
9283                                 $prop['borderStyle'] = 'beveled';
9284                         }
9285                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
9286                         $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
9287                         $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
9288                         $this->javascript .= 'f'.$name.".highlight='push';\n";
9289                         $this->javascript .= 'f'.$name.".print=false;\n";
9290                 }
9291                 
9292                 // END JAVASCRIPT - FORMS ------------------------------
9293                 
9294                 /*
9295                 * Enable Write permissions for PDF Reader.
9296                 * WARNING: This works only using the Adobe private key with the setSignature() method.
9297                 * @access protected
9298                 * @author Nicola Asuni
9299                 * @since 2.9.000 (2008-03-26)
9300                 */
9301                 protected function _putuserrights() {
9302                         if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] > 0))) {
9303                                 return;
9304                         }
9305                         $this->_out('/Perms');
9306                         $this->_out('<<');
9307                         $this->_out('/UR3');
9308                         $this->_out('<<');
9309                         $this->_out('/Type/Sig');
9310                         $this->_out('/Filter/Adobe.PPKLite');
9311                         $this->_out('/SubFilter/adbe.pkcs7.detached');
9312                         $this->_out('/ByteRange[0 ********** ********** **********]');
9313                         $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9314                         if ($this->ur) {
9315                                 $this->_out('/Reference');
9316                                 $this->_out('[');
9317                                 $this->_out('<<');
9318                                 $this->_out('/Type/SigRef');
9319                                 $this->_out('/TransformMethod/UR3');
9320                                 $this->_out('/TransformParams');
9321                                 $this->_out('<<');
9322                                 $this->_out('/Type/TransformParams');
9323                                 $this->_out('/V/2.2');
9324                                 if (!$this->empty_string($this->ur_document)) {
9325                                         $this->_out('/Document['.$this->ur_document.']');
9326                                 }
9327                                 if (!$this->empty_string($this->ur_annots)) {
9328                                         $this->_out('/Annots['.$this->ur_annots.']');
9329                                 }
9330                                 if (!$this->empty_string($this->ur_form)) {
9331                                         $this->_out('/Form['.$this->ur_form.']');
9332                                 }
9333                                 if (!$this->empty_string($this->ur_signature)) {
9334                                         $this->_out('/Signature['.$this->ur_signature.']');
9335                                 }                       
9336                                 $this->_out('>>');
9337                                 $this->_out('>>');
9338                                 $this->_out(']');
9339                         }
9340                         $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9341                         $this->_out('>>');
9342                         $this->_out('>>');
9343                 }
9344                 
9345                 /*
9346                 * Add certification signature (DocMDP)
9347                 * @access protected
9348                 * @author Nicola Asuni
9349                 * @since 4.6.008 (2009-05-07)
9350                 */
9351                 protected function _putcertification() {
9352                         if ((!$this->sign) OR (isset($this->signature_data['cert_type']) AND ($this->signature_data['cert_type'] <= 0))) {
9353                                 return;
9354                         }
9355                         $this->_out('/Perms');
9356                         $this->_out('<<');
9357                         $this->_out('/DocMDP');
9358                         $this->_out('<<');
9359                         $this->_out('/Type/Sig');
9360                         $this->_out('/Filter/Adobe.PPKLite');
9361                         $this->_out('/SubFilter/adbe.pkcs7.detached');
9362                         $this->_out('/ByteRange[0 ********** ********** **********]');
9363                         $this->_out('/Contents<>'.str_repeat(' ', $this->signature_max_lenght));
9364                         $this->_out('/Reference');
9365                         $this->_out('[');
9366                         $this->_out('<<');
9367                         $this->_out('/Type/SigRef');
9368                         $this->_out('/TransformMethod/DocMDP');
9369                         $this->_out('/TransformParams');
9370                         $this->_out('<<');
9371                         $this->_out('/Type/TransformParams');
9372                         $this->_out('/V/1.2');
9373                         $this->_out('/P '.$this->signature_data['cert_type'].'');
9374                         $this->_out('>>');
9375                         $this->_out('>>');
9376                         $this->_out(']');
9377                         $this->_out('/M '.$this->_datastring('D:'.date('YmdHisO')));
9378                         if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
9379                                 $this->_out('/Name '.$this->_textstring($this->signature_data['info']['Name']).'');
9380                         }
9381                         if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
9382                                 $this->_out('/Location '.$this->_textstring($this->signature_data['info']['Location']).'');
9383                         }
9384                         if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
9385                                 $this->_out('/Reason '.$this->_textstring($this->signature_data['info']['Reason']).'');
9386                         }
9387                         if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
9388                                 $this->_out('/ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']).'');
9389                         }
9390                         $this->_out('>>');
9391                         $this->_out('>>');
9392                 }
9393                 
9394                 /*
9395                 * Set User's Rights for PDF Reader
9396                 * WARNING: This works only using the Adobe private key with the setSignature() method!.
9397                 * Check the PDF Reference 8.7.1 Transform Methods, 
9398                 * Table 8.105 Entries in the UR transform parameters dictionary
9399                 * @param boolean $enable if true enable user's rights on PDF reader
9400                 * @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.
9401                 * @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.
9402                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate 
9403                 * @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.
9404                 * @access public
9405                 * @author Nicola Asuni
9406                 * @since 2.9.000 (2008-03-26)
9407                 */
9408                 public function setUserRights(
9409                                 $enable=true, 
9410                                 $document='/FullSave',
9411                                 $annots='/Create/Delete/Modify/Copy/Import/Export',
9412                                 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
9413                                 $signature='/Modify') {
9414                         $this->ur = $enable;
9415                         $this->ur_document = $document;
9416                         $this->ur_annots = $annots;
9417                         $this->ur_form = $form;
9418                         $this->ur_signature = $signature;
9419                 }
9420                 
9421                 /*
9422                 * Enable document signature (requires the OpenSSL Library).
9423                 * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
9424                 * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
9425                 * @param mixed $private_key private key (string or filename prefixed with 'file://')
9426                 * @param string $private_key_password password
9427                 * @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.
9428                 * @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.
9429                 * @parm array $info array of option information: Name, Location, Reason, ContactInfo.
9430                 * @access public
9431                 * @author Nicola Asuni
9432                 * @since 4.6.005 (2009-04-24)
9433                 */
9434                 public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
9435                         // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.pem -out tcpdf.pem
9436                         $this->sign = true;
9437                         $this->signature_data = array();
9438                         if (strlen($signing_cert) == 0) {
9439                                 $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.pem';
9440                         }
9441                         if (strlen($private_key) == 0) {
9442                                 $private_key = $signing_cert;
9443                         }
9444                         $this->signature_data['signcert'] = $signing_cert;
9445                         $this->signature_data['privkey'] = $private_key;
9446                         $this->signature_data['password'] = $private_key_password;
9447                         $this->signature_data['extracerts'] = $extracerts;
9448                         $this->signature_data['cert_type'] = $cert_type;
9449                         $this->signature_data['info'] = array();
9450                 }
9451                 
9452                 /*
9453                 * Create a new page group.
9454                 * NOTE: call this function before calling AddPage()
9455                 * @param int $page starting group page (leave empty for next page).
9456                 * @access public
9457                 * @since 3.0.000 (2008-03-27)
9458                 */
9459                 public function startPageGroup($page='') {
9460                         if (empty($page)) {
9461                                 $page = $this->page + 1;
9462                         }
9463                         $this->newpagegroup[$page] = true;
9464                 }
9465
9466                 /**
9467                 * Defines an alias for the total number of pages.
9468                 * It will be substituted as the document is closed.
9469                 * @param string $alias The alias.
9470                 * @access public
9471                 * @since 1.4
9472                 * @see getAliasNbPages(), PageNo(), Footer()
9473                 */
9474                 public function AliasNbPages($alias='{nb}') {
9475                         $this->AliasNbPages = $alias;
9476                 }
9477                 
9478                 /**
9479                  * Returns the string alias used for the total number of pages.
9480          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9481                  * @return string
9482                  * @access public
9483                  * @since 4.0.018 (2008-08-08)
9484                  * @see AliasNbPages(), PageNo(), Footer()
9485                 */
9486                 public function getAliasNbPages() {
9487                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9488                                 return '{'.$this->AliasNbPages.'}';
9489             }
9490                         return $this->AliasNbPages;
9491                 }
9492
9493                 /**
9494                 * Defines an alias for the page number.
9495                 * It will be substituted as the document is closed.
9496                 * @param string $alias The alias.
9497                 * @access public
9498                 * @since 4.5.000 (2009-01-02)
9499                 * @see getAliasNbPages(), PageNo(), Footer()
9500                 */
9501                 public function AliasNumPage($alias='{pnb}') {
9502                         //Define an alias for total number of pages
9503                         $this->AliasNumPage = $alias;
9504                 }
9505                 
9506                 /**
9507                  * Returns the string alias used for the page number.
9508          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9509                  * @return string
9510                  * @access public
9511                  * @since 4.5.000 (2009-01-02)
9512                  * @see AliasNbPages(), PageNo(), Footer()
9513                 */
9514                 public function getAliasNumPage() {
9515                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9516                                 return '{'.$this->AliasNumPage.'}';
9517             }
9518                         return $this->AliasNumPage;
9519                 }
9520                 
9521                 /*
9522                 * Return the current page in the group.
9523                 * @return current page in the group
9524                 * @access public
9525                 * @since 3.0.000 (2008-03-27)
9526                 */
9527                 public function getGroupPageNo() {
9528                         return $this->pagegroups[$this->currpagegroup];
9529                 }
9530
9531                 /**
9532                 * Returns the current group page number formatted as a string.
9533                 * @access public
9534                 * @since 4.3.003 (2008-11-18)
9535                 * @see PaneNo(), formatPageNumber()
9536                 */
9537                 public function getGroupPageNoFormatted() {
9538                         return $this->formatPageNumber($this->getGroupPageNo());
9539         }
9540                 
9541                 /*
9542                  * Return the alias of the current page group
9543          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9544                  * (will be replaced by the total number of pages in this group).
9545                  * @return alias of the current page group
9546                  * @access public
9547                  * @since 3.0.000 (2008-03-27)
9548                 */
9549                 public function getPageGroupAlias() {
9550                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9551                                 return '{'.$this->currpagegroup.'}';
9552             }
9553                         return $this->currpagegroup;
9554                 }
9555                 
9556                 /*
9557                  * Return the alias for the page number on the current page group
9558          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
9559                  * (will be replaced by the total number of pages in this group).
9560                  * @return alias of the current page group
9561                  * @access public
9562                  * @since 4.5.000 (2009-01-02)
9563                 */
9564                 public function getPageNumGroupAlias() {
9565                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
9566                                 return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
9567             }
9568                         return str_replace('{nb', '{pnb', $this->currpagegroup);
9569                 }
9570
9571                 /**
9572                 * Format the page numbers.
9573                 * This method can be overriden for custom formats.
9574                 * @param int $num page number
9575                 * @access protected
9576                 * @since 4.2.005 (2008-11-06)
9577                 */
9578                 protected function formatPageNumber($num) {
9579                         return number_format((float)$num, 0, '', '.');
9580                 }
9581
9582                 /**
9583                 * Format the page numbers on the Table Of Content.
9584                 * This method can be overriden for custom formats.
9585                 * @param int $num page number
9586                 * @access protected
9587                 * @since 4.5.001 (2009-01-04)
9588                 * @see addTOC()
9589                 */
9590                 protected function formatTOCPageNumber($num) {
9591                         return number_format((float)$num, 0, '', '.');
9592                 }
9593
9594         /**
9595                 * Returns the current page number formatted as a string.
9596                 * @access public
9597                 * @since 4.2.005 (2008-11-06)
9598                 * @see PaneNo(), formatPageNumber()
9599                 */
9600                 public function PageNoFormatted() {
9601                         return $this->formatPageNumber($this->PageNo());
9602         }
9603
9604         /*
9605                 * Put visibility settings.
9606                 * @access protected
9607                 * @since 3.0.000 (2008-03-27)
9608                 */
9609                 protected function _putocg() {
9610                         $this->_newobj();
9611                         $this->n_ocg_print = $this->n;
9612                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
9613                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
9614                         $this->_out('endobj');
9615                         $this->_newobj();
9616                         $this->n_ocg_view=$this->n;
9617                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
9618                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
9619                         $this->_out('endobj');
9620                 }
9621                 
9622                 /*
9623                 * Set the visibility of the successive elements.
9624                 * This can be useful, for instance, to put a background 
9625                 * image or color that will show on screen but won't print.
9626                 * @param string $v visibility mode. Legal values are: all, print, screen.
9627                 * @access public
9628                 * @since 3.0.000 (2008-03-27)
9629                 */
9630                 public function setVisibility($v) {
9631                         if ($this->openMarkedContent) {
9632                                 // close existing open marked-content
9633                                 $this->_out('EMC');
9634                                 $this->openMarkedContent = false;
9635                         }
9636                         switch($v) {
9637                                 case 'print': {
9638                                         $this->_out('/OC /OC1 BDC');
9639                                         $this->openMarkedContent = true;
9640                                         break;
9641                                 }
9642                                 case 'screen': {
9643                                         $this->_out('/OC /OC2 BDC');
9644                                         $this->openMarkedContent = true;
9645                                         break;
9646                                 }
9647                                 case 'all': {
9648                                         $this->_out('');
9649                                         break;
9650                                 }
9651                                 default: {
9652                                         $this->Error('Incorrect visibility: '.$v);
9653                                         break;
9654                                 }
9655                         }
9656                         $this->visibility = $v;
9657                 }
9658                 
9659                 /*
9660                 * Add transparency parameters to the current extgstate
9661                 * @param array $params parameters
9662                 * @return the number of extgstates
9663                 * @access protected
9664                 * @since 3.0.000 (2008-03-27)
9665                 */
9666                 protected function addExtGState($parms) {
9667                         $n = count($this->extgstates) + 1;
9668                         $this->extgstates[$n]['parms'] = $parms;
9669                         return $n;
9670                 }
9671                 
9672                 /*
9673                 * Add an extgstate
9674                 * @param array $gs extgstate
9675                 * @access protected
9676                 * @since 3.0.000 (2008-03-27)
9677                 */
9678                 protected function setExtGState($gs) {
9679                         $this->_out(sprintf('/GS%d gs', $gs));
9680                 }
9681                 
9682                 /*
9683                 * Put extgstates for object transparency
9684                 * @param array $gs extgstate
9685                 * @access protected
9686                 * @since 3.0.000 (2008-03-27)
9687                 */
9688                 protected function _putextgstates() {
9689                         $ne = count($this->extgstates);
9690                         for ($i = 1; $i <= $ne; ++$i) {
9691                                 $this->_newobj();
9692                                 $this->extgstates[$i]['n'] = $this->n;
9693                                 $this->_out('<</Type /ExtGState');
9694                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
9695                                         $this->_out('/'.$k.' '.$v);
9696                                 }
9697                                 $this->_out('>>');
9698                                 $this->_out('endobj');
9699                         }
9700                 }
9701                 
9702                 /*
9703                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
9704                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
9705                 * @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
9706                 * @access public
9707                 * @since 3.0.000 (2008-03-27)
9708                 */
9709                 public function setAlpha($alpha, $bm='Normal') {
9710                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
9711                         $this->setExtGState($gs);
9712                 }
9713
9714                 /*
9715                 * Set the default JPEG compression quality (1-100)
9716                 * @param int $quality JPEG quality, integer between 1 and 100
9717                 * @access public
9718                 * @since 3.0.000 (2008-03-27)
9719                 */
9720                 public function setJPEGQuality($quality) {
9721                         if (($quality < 1) OR ($quality > 100)) {
9722                                 $quality = 75;
9723                         }
9724                         $this->jpeg_quality = intval($quality);
9725                 }
9726                 
9727                 /*
9728                 * Set the default number of columns in a row for HTML tables.
9729                 * @param int $cols number of columns
9730                 * @access public
9731                 * @since 3.0.014 (2008-06-04)
9732                 */
9733                 public function setDefaultTableColumns($cols=4) { 
9734                         $this->default_table_columns = intval($cols); 
9735                 }
9736                 
9737                 /*
9738                 * Set the height of cell repect font height.
9739                 * @param int $h cell proportion respect font height (typical value = 1.25).
9740                 * @access public
9741                 * @since 3.0.014 (2008-06-04)
9742                 */
9743                 public function setCellHeightRatio($h) { 
9744                         $this->cell_height_ratio = $h; 
9745                 }
9746                 
9747                 /*
9748                 * return the height of cell repect font height.
9749                 * @access public
9750                 * @since 4.0.012 (2008-07-24)
9751                 */
9752                 public function getCellHeightRatio() { 
9753                         return $this->cell_height_ratio; 
9754                 }
9755                 
9756                 /*
9757                 * Set the PDF version (check PDF reference for valid values).
9758                 * Default value is 1.t
9759                 * @access public
9760                 * @since 3.1.000 (2008-06-09)
9761                 */
9762                 public function setPDFVersion($version='1.7') { 
9763                         $this->PDFVersion = $version;
9764                 }
9765                 
9766                 /*
9767                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
9768                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
9769                 * <ul>
9770                 * <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>
9771                 * <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>
9772                 * <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>
9773                 * <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>
9774                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
9775                 * <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>
9776                 * <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>
9777                 * <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>
9778                 * <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>
9779                 * <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>
9780                 * <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>
9781                 * <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>
9782                 * <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>
9783                 * <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>
9784                 * <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>
9785                 * <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>
9786                 * </ul>
9787                 * @param array $preferences array of options.
9788                 * @author Nicola Asuni
9789                 * @access public
9790                 * @since 3.1.000 (2008-06-09)
9791                 */
9792                 public function setViewerPreferences($preferences) { 
9793                         $this->viewer_preferences = $preferences;
9794                 }
9795                 
9796                 /**
9797                 * Paints a linear colour gradient.
9798                 * @param float $x abscissa of the top left corner of the rectangle.
9799                 * @param float $y ordinate of the top left corner of the rectangle.
9800                 * @param float $w width of the rectangle.
9801                 * @param float $h height of the rectangle.
9802                 * @param array $col1 first color (RGB components).
9803                 * @param array $col2 second color (RGB components).
9804                 * @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).
9805                 * @author Andreas W�rmser, Nicola Asuni
9806                 * @since 3.1.000 (2008-06-09)
9807                 * @access public
9808                 */
9809                 public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
9810                         $this->Clip($x, $y, $w, $h);
9811                         $this->Gradient(2, $col1, $col2, $coords);
9812                 }
9813                 
9814                 /**
9815                 * Paints a radial colour gradient.
9816                 * @param float $x abscissa of the top left corner of the rectangle.
9817                 * @param float $y ordinate of the top left corner of the rectangle.
9818                 * @param float $w width of the rectangle.
9819                 * @param float $h height of the rectangle.
9820                 * @param array $col1 first color (RGB components).
9821                 * @param array $col2 second color (RGB components).
9822                 * @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.
9823                 * @author Andreas W�rmser, Nicola Asuni
9824                 * @since 3.1.000 (2008-06-09)
9825                 * @access public
9826                 */
9827                 public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
9828                         $this->Clip($x, $y, $w, $h);
9829                         $this->Gradient(3, $col1, $col2, $coords);
9830                 }
9831                 
9832                 /**
9833                 * Paints a coons patch mesh.
9834                 * @param float $x abscissa of the top left corner of the rectangle.
9835                 * @param float $y ordinate of the top left corner of the rectangle.
9836                 * @param float $w width of the rectangle.
9837                 * @param float $h height of the rectangle.
9838                 * @param array $col1 first color (lower left corner) (RGB components).
9839                 * @param array $col2 second color (lower right corner) (RGB components).
9840                 * @param array $col3 third color (upper right corner) (RGB components).
9841                 * @param array $col4 fourth color (upper left corner) (RGB components).
9842                 * @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>
9843                 * @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
9844                 * @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
9845                 * @author Andreas W�rmser, Nicola Asuni
9846                 * @since 3.1.000 (2008-06-09)
9847                 * @access public
9848                 */
9849                 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) {
9850                         $this->Clip($x, $y, $w, $h);        
9851                         $n = count($this->gradients) + 1;
9852                         $this->gradients[$n]['type'] = 6; //coons patch mesh
9853                         //check the coords array if it is the simple array or the multi patch array
9854                         if (!isset($coords[0]['f'])) {
9855                                 //simple array -> convert to multi patch array
9856                                 if (!isset($col1[1])) {
9857                                         $col1[1] = $col1[2] = $col1[0];
9858                                 }
9859                                 if (!isset($col2[1])) {
9860                                         $col2[1] = $col2[2] = $col2[0];
9861                                 }
9862                                 if (!isset($col3[1])) {
9863                                         $col3[1] = $col3[2] = $col3[0];
9864                                 }
9865                                 if (!isset($col4[1])) {
9866                                         $col4[1] = $col4[2] = $col4[0];
9867                                 }
9868                                 $patch_array[0]['f'] = 0;
9869                                 $patch_array[0]['points'] = $coords;
9870                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
9871                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
9872                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
9873                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
9874                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
9875                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
9876                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
9877                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
9878                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
9879                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
9880                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
9881                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
9882                         } else {
9883                                 //multi patch array
9884                                 $patch_array = $coords;
9885                         }
9886                         $bpcd = 65535; //16 BitsPerCoordinate
9887                         //build the data stream
9888                         $this->gradients[$n]['stream'] = '';
9889                         $count_patch = count($patch_array);
9890                         for ($i=0; $i < $count_patch; ++$i) {
9891                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
9892                                 $count_points = count($patch_array[$i]['points']);
9893                                 for ($j=0; $j < $count_points; ++$j) {
9894                                         //each point as 16 bit
9895                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
9896                                         if ($patch_array[$i]['points'][$j] < 0) {
9897                                                 $patch_array[$i]['points'][$j] = 0;
9898                                         }
9899                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
9900                                                 $patch_array[$i]['points'][$j] = $bpcd;
9901                                         }
9902                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
9903                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
9904                                 }
9905                                 $count_cols = count($patch_array[$i]['colors']);
9906                                 for ($j=0; $j < $count_cols; ++$j) {
9907                                         //each color component as 8 bit
9908                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
9909                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
9910                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
9911                                 }
9912                         }
9913                         //paint the gradient
9914                         $this->_out('/Sh'.$n.' sh');
9915                         //restore previous Graphic State
9916                         $this->_out('Q');
9917                 }
9918                 
9919                 /**
9920                 * Set a rectangular clipping area.
9921                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
9922                 * @param float $y ordinate of the top left corner of the rectangle.
9923                 * @param float $w width of the rectangle.
9924                 * @param float $h height of the rectangle.
9925                 * @author Andreas W�rmser, Nicola Asuni
9926                 * @since 3.1.000 (2008-06-09)
9927                 * @access protected
9928                 */
9929                 protected function Clip($x, $y, $w, $h) {
9930                         if ($this->rtl) {
9931                                 $x = $this->w - $x - $w;
9932                         }
9933                         //save current Graphic State
9934                         $s = 'q';
9935                         //set clipping area
9936                         $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
9937                         //set up transformation matrix for gradient
9938                         $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
9939                         $this->_out($s);
9940                 }
9941                                 
9942                 /**
9943                 * Output gradient.
9944                 * @param int $type type of gradient.
9945                 * @param array $col1 first color (RGB components).
9946                 * @param array $col2 second color (RGB components).
9947                 * @param array $coords array of coordinates.
9948                 * @author Andreas W�rmser, Nicola Asuni
9949                 * @since 3.1.000 (2008-06-09)
9950                 * @access protected
9951                 */
9952                 protected function Gradient($type, $col1, $col2, $coords) {
9953                         $n = count($this->gradients) + 1;
9954                         $this->gradients[$n]['type'] = $type;
9955                         if (!isset($col1[1])) {
9956                                 $col1[1]=$col1[2]=$col1[0];
9957                         }
9958                         $this->gradients[$n]['col1'] = sprintf('%.3F %.3F %.3F', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
9959                         if (!isset($col2[1])) {
9960                                 $col2[1] = $col2[2] = $col2[0];
9961                         }
9962                         $this->gradients[$n]['col2'] = sprintf('%.3F %.3F %.3F', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
9963                         $this->gradients[$n]['coords'] = $coords;
9964                         //paint the gradient
9965                         $this->_out('/Sh'.$n.' sh');
9966                         //restore previous Graphic State
9967                         $this->_out('Q');
9968                 }
9969                 
9970                 /**
9971                 * Output shaders.
9972                 * @author Andreas W�rmser, Nicola Asuni
9973                 * @since 3.1.000 (2008-06-09)
9974                 * @access protected
9975                 */
9976                 function _putshaders() {
9977                         foreach ($this->gradients as $id => $grad) {  
9978                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
9979                                         $this->_newobj();
9980                                         $this->_out('<<');
9981                                         $this->_out('/FunctionType 2');
9982                                         $this->_out('/Domain [0.0 1.0]');
9983                                         $this->_out('/C0 ['.$grad['col1'].']');
9984                                         $this->_out('/C1 ['.$grad['col2'].']');
9985                                         $this->_out('/N 1');
9986                                         $this->_out('>>');
9987                                         $this->_out('endobj');
9988                                         $f1 = $this->n;
9989                                 }
9990                                 $this->_newobj();
9991                                 $this->_out('<<');
9992                                 $this->_out('/ShadingType '.$grad['type']);
9993                                 $this->_out('/ColorSpace /DeviceRGB');
9994                                 if ($grad['type'] == 2) {
9995                                         $this->_out(sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
9996                                         $this->_out('/Function '.$f1.' 0 R');
9997                                         $this->_out('/Extend [true true] ');
9998                                         $this->_out('>>');
9999                                 } elseif ($grad['type'] == 3) {
10000                                         //x0, y0, r0, x1, y1, r1
10001                                         //at this this time radius of inner circle is 0
10002                                         $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]));
10003                                         $this->_out('/Function '.$f1.' 0 R');
10004                                         $this->_out('/Extend [true true] ');
10005                                         $this->_out('>>');
10006                                 } elseif ($grad['type'] == 6) {
10007                                         $this->_out('/BitsPerCoordinate 16');
10008                                         $this->_out('/BitsPerComponent 8');
10009                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
10010                                         $this->_out('/BitsPerFlag 8');
10011                                         $this->_out('/Length '.strlen($grad['stream']));
10012                                         $this->_out('>>');
10013                                         $this->_putstream($grad['stream']);
10014                                 }
10015                                 $this->_out('endobj');
10016                                 $this->gradients[$id]['id'] = $this->n;
10017                         }
10018                 }
10019
10020                 /**
10021                 * Output an arc
10022                 * @author Maxime Delorme, Nicola Asuni
10023                 * @since 3.1.000 (2008-06-09)
10024                 * @access protected
10025                 */
10026                 protected function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
10027                         $h = $this->h;
10028                         $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));
10029                 }
10030                 
10031                 /**
10032                 * Draw the sector of a circle.
10033                 * It can be used for instance to render pie charts.
10034                 * @param float $xc abscissa of the center.
10035                 * @param float $yc ordinate of the center.
10036                 * @param float $r radius.
10037                 * @param float $a start angle (in degrees).
10038                 * @param float $b end angle (in degrees).
10039                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
10040                 * @param float $cw: indicates whether to go clockwise (default: true).
10041                 * @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.
10042                 * @author Maxime Delorme, Nicola Asuni
10043                 * @since 3.1.000 (2008-06-09)
10044                 * @access public
10045                 */
10046                 public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
10047                         if ($this->rtl) {
10048                                 $xc = $this->w - $xc;
10049                         }
10050                         if ($cw) {
10051                                 $d = $b;
10052                                 $b = $o - $a;
10053                                 $a = $o - $d;
10054                         } else {
10055                                 $b += $o;
10056                                 $a += $o;
10057                         }
10058                         $a = ($a % 360) + 360;
10059                         $b = ($b % 360) + 360;
10060                         if ($a > $b) {
10061                                 $b +=360;
10062                         }
10063                         $b = $b / 360 * 2 * M_PI;
10064                         $a = $a / 360 * 2 * M_PI;
10065                         $d = $b - $a;
10066                         if ($d == 0 ) {
10067                                 $d = 2 * M_PI;
10068                         }
10069                         $k = $this->k;
10070                         $hp = $this->h;
10071                         if ($style=='F') {
10072                                 $op = 'f';
10073                         } elseif ($style=='FD' or $style=='DF') {
10074                                 $op = 'b';
10075                         } else {
10076                                 $op = 's';
10077                         }
10078                         if (sin($d/2)) {
10079                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
10080                         }
10081                         //first put the center
10082                         $this->_out(sprintf('%.2F %.2F m', ($xc)*$k, ($hp-$yc)*$k));
10083                         //put the first point
10084                         $this->_out(sprintf('%.2F %.2F l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
10085                         //draw the arc
10086                         if ($d < (M_PI/2)) {
10087                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
10088                         } else {
10089                                 $b = $a + $d/4;
10090                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
10091                                 $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));
10092                                 $a = $b;
10093                                 $b = $a + $d/4;
10094                                 $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));
10095                                 $a = $b;
10096                                 $b = $a + $d/4;
10097                                 $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) );
10098                                 $a = $b;
10099                                 $b = $a + $d/4;
10100                                 $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));
10101                         }
10102                         //terminate drawing
10103                         $this->_out($op);
10104                 }
10105                 
10106                 /**
10107                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
10108                 * Only vector drawing is supported, not text or bitmap. 
10109                 * 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).
10110                 * @param string $file Name of the file containing the image.
10111                 * @param float $x Abscissa of the upper-left corner.
10112                 * @param float $y Ordinate of the upper-left corner.
10113                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
10114                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
10115                 * @param mixed $link URL or identifier returned by AddLink().
10116                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
10117                 * @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>
10118                 * @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>
10119                 * @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>
10120                 * @author Valentin Schmidt, Nicola Asuni
10121                 * @since 3.1.000 (2008-06-09)
10122                 * @access public
10123                 */
10124                 public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0) {
10125                         if ($x === '') {
10126                                 $x = $this->x;
10127                         }
10128                         if ($y === '') {
10129                                 $y = $this->y;
10130                         }
10131                         $k = $this->k;
10132                         $data = file_get_contents($file);
10133                         if ($data === false) {
10134                                 $this->Error('EPS file not found: '.$file);
10135                         }
10136                         $regs = array();
10137                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
10138                         preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
10139                         if (count($regs) > 1) {
10140                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
10141                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
10142                                         $versexp = explode(' ', $version_str);
10143                                         $version = (float)array_pop($versexp);
10144                                         if ($version >= 9) {
10145                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
10146                                         }
10147                                 }
10148                         }
10149                         // strip binary bytes in front of PS-header
10150                         $start = strpos($data, '%!PS-Adobe');
10151                         if ($start > 0) {
10152                                 $data = substr($data, $start);
10153                         }
10154                         // find BoundingBox params
10155                         preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
10156                         if (count($regs) > 1) {
10157                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
10158                         } else {
10159                                 $this->Error('No BoundingBox found in EPS file: '.$file);
10160                         }
10161                         $start = strpos($data, '%%EndSetup');
10162                         if ($start === false) {
10163                                 $start = strpos($data, '%%EndProlog');
10164                         }
10165                         if ($start === false) {
10166                                 $start = strpos($data, '%%BoundingBox');
10167                         }
10168                         $data = substr($data, $start);
10169                         $end = strpos($data, '%%PageTrailer');
10170                         if ($end===false) {
10171                                 $end = strpos($data, 'showpage');
10172                         }
10173                         if ($end) {
10174                                 $data = substr($data, 0, $end);
10175                         }
10176                         if ($w > 0) {
10177                                 $scale_x = $w / (($x2 - $x1) / $k);
10178                                 if ($h > 0) {
10179                                         $scale_y = $h / (($y2 - $y1) / $k);
10180                                 } else {
10181                                         $scale_y = $scale_x;
10182                                         $h = ($y2 - $y1) / $k * $scale_y;
10183                                 }
10184                         } else {
10185                                 if ($h > 0) {
10186                                         $scale_y = $h / (($y2 - $y1) / $k);
10187                                         $scale_x = $scale_y;
10188                                         $w = ($x2-$x1) / $k * $scale_x;
10189                                 } else {
10190                                         $w = ($x2 - $x1) / $k;
10191                                         $h = ($y2 - $y1) / $k;
10192                                 }
10193                         }
10194                         // Check whether we need a new page first as this does not fit
10195                         if ($this->checkPageBreak($h, $y)) {
10196                                 $y = $this->GetY() + $this->cMargin;
10197                         }
10198                         // set bottomcoordinates
10199                         $this->img_rb_y = $y + $h;
10200                         // set alignment
10201                         if ($this->rtl) {
10202                                 if ($palign == 'L') {
10203                                         $ximg = $this->lMargin;
10204                                         // set right side coordinate
10205                                         $this->img_rb_x = $ximg + $w;
10206                                 } elseif ($palign == 'C') {
10207                                         $ximg = ($this->w - $x - $w) / 2;
10208                                         // set right side coordinate
10209                                         $this->img_rb_x = $ximg + $w;
10210                                 } else {
10211                                         $ximg = $this->w - $x - $w;
10212                                         // set left side coordinate
10213                                         $this->img_rb_x = $ximg;
10214                                 }
10215                         } else {
10216                                 if ($palign == 'R') {
10217                                         $ximg = $this->w - $this->rMargin - $w;
10218                                         // set left side coordinate
10219                                         $this->img_rb_x = $ximg;
10220                                 } elseif ($palign == 'C') {
10221                                         $ximg = ($this->w - $x - $w) / 2;
10222                                         // set right side coordinate
10223                                         $this->img_rb_x = $ximg + $w;
10224                                 } else {
10225                                         $ximg = $x;
10226                                         // set right side coordinate
10227                                         $this->img_rb_x = $ximg + $w;
10228                                 }
10229                         }
10230                         if ($useBoundingBox) {
10231                                 $dx = $ximg * $k - $x1;
10232                                 $dy = $y * $k - $y1;
10233                         } else {
10234                                 $dx = $ximg * $k;
10235                                 $dy = $y * $k;
10236                         }
10237                         // save the current graphic state
10238                         $this->_out('q'.$this->epsmarker);
10239                         // translate
10240                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
10241                         // scale
10242                         if (isset($scale_x)) {
10243                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
10244                         }
10245                         // handle pc/unix/mac line endings
10246                         preg_match('/[\r\n]+/s', $data, $regs);
10247                         $lines = explode($regs[0], $data);
10248                         $u=0;
10249                         $cnt = count($lines);
10250                         for ($i=0; $i < $cnt; ++$i) {
10251                                 $line = $lines[$i];
10252                                 if (($line == '') OR ($line{0} == '%')) {
10253                                         continue;
10254                                 }
10255                                 $len = strlen($line);
10256                                 $chunks = explode(' ', $line);
10257                                 $cmd = array_pop($chunks);
10258                                 // RGB
10259                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
10260                                         $b = array_pop($chunks); 
10261                                         $g = array_pop($chunks); 
10262                                         $r = array_pop($chunks);
10263                                         $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!
10264                                         continue;
10265                                 }
10266                                 switch ($cmd) {
10267                                         case 'm':
10268                                         case 'l':
10269                                         case 'v':
10270                                         case 'y':
10271                                         case 'c':
10272                                         case 'k':
10273                                         case 'K':
10274                                         case 'g':
10275                                         case 'G':
10276                                         case 's':
10277                                         case 'S':
10278                                         case 'J':
10279                                         case 'j':
10280                                         case 'w':
10281                                         case 'M':
10282                                         case 'd':
10283                                         case 'n':
10284                                         case 'v': {
10285                                                 $this->_out($line);
10286                                                 break;
10287                                         }
10288                                         case 'x': {// custom fill color
10289                                                 list($c,$m,$y,$k) = $chunks;
10290                                                 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
10291                                                 break;
10292                                         }
10293                                         case 'X': { // custom stroke color
10294                                                 list($c,$m,$y,$k) = $chunks;
10295                                                 $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
10296                                                 break;
10297                                         }
10298                                         case 'Y':
10299                                         case 'N':
10300                                         case 'V':
10301                                         case 'L':
10302                                         case 'C': {
10303                                                 $line{$len-1} = strtolower($cmd);
10304                                                 $this->_out($line);
10305                                                 break;
10306                                         }
10307                                         case 'b':
10308                                         case 'B': {
10309                                                 $this->_out($cmd . '*');
10310                                                 break;
10311                                         }
10312                                         case 'f':
10313                                         case 'F': {
10314                                                 if ($u > 0) {
10315                                                         $isU = false;
10316                                                         $max = min($i+5, $cnt);
10317                                                         for ($j=$i+1; $j < $max; ++$j)
10318                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
10319                                                         if ($isU) {
10320                                                                 $this->_out('f*');
10321                                                         }
10322                                                 } else {
10323                                                         $this->_out('f*');
10324                                                 }
10325                                                 break;
10326                                         }
10327                                         case '*u': {
10328                                                 ++$u;
10329                                                 break;
10330                                         }
10331                                         case '*U': {
10332                                                 --$u;
10333                                                 break;
10334                                         }
10335                                 }
10336                         }
10337                         // restore previous graphic state
10338                         $this->_out($this->epsmarker.'Q');
10339                         if (!empty($border)) {
10340                                 $bx = $x;
10341                                 $by = $y;
10342                                 $this->x = $x;
10343                                 $this->y = $y;
10344                                 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
10345                                 $this->x = $bx;
10346                                 $this->y = $by;
10347                         }
10348                         if ($link) {
10349                                 $this->Link($ximg, $y, $w, $h, $link, 0);
10350                         }
10351                         // set pointer to align the successive text/objects
10352                         switch($align) {
10353                                 case 'T':{
10354                                         $this->y = $y;
10355                                         $this->x = $this->img_rb_x;
10356                                         break;
10357                                 }
10358                                 case 'M':{
10359                                         $this->y = $y + round($h/2);
10360                                         $this->x = $this->img_rb_x;
10361                                         break;
10362                                 }
10363                                 case 'B':{
10364                                         $this->y = $this->img_rb_y;
10365                                         $this->x = $this->img_rb_x;
10366                                         break;
10367                                 }
10368                                 case 'N':{
10369                                         $this->SetY($this->img_rb_y);
10370                                         break;
10371                                 }
10372                                 default:{
10373                                         break;
10374                                 }
10375                         }
10376                         $this->endlinex = $this->img_rb_x;
10377                 }
10378                 
10379                 /**
10380                  * Set document barcode.
10381                  * @param string $bc barcode
10382                  * @access public
10383                  */
10384                 public function setBarcode($bc='') {
10385                         $this->barcode = $bc;
10386                 }
10387                 
10388                 /**
10389                  * Get current barcode.
10390                  * @return string
10391                  * @access public
10392                  * @since 4.0.012 (2008-07-24)
10393                  */
10394                 public function getBarcode() {
10395                         return $this->barcode;
10396                 }
10397                 
10398                 /**
10399                  * Print a Linear Barcode.
10400                  * @param string $code code to print
10401                  * @param string $type type of barcode.
10402                  * @param int $x x position in user units
10403                  * @param int $y y position in user units
10404                  * @param int $w width in user units
10405                  * @param int $h height in user units
10406                  * @param float $xres width of the smallest bar in user units
10407                  * @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>
10408                  * @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>
10409                  * @author Nicola Asuni
10410                  * @since 3.1.000 (2008-06-09)
10411                  * @access public
10412                  */
10413                 public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
10414                         if ($this->empty_string($code)) {
10415                                 return;
10416                         }
10417                         require_once(dirname(__FILE__).'/barcodes.php');
10418                         // save current graphic settings
10419                         $gvars = $this->getGraphicVars();
10420                         // create new barcode object
10421                         $barcodeobj = new TCPDFBarcode($code, $type);
10422                         $arrcode = $barcodeobj->getBarcodeArray();
10423                         if ($arrcode === false) {
10424                                 $this->Error('Error in 1D barcode string');
10425                         }
10426                         // set default values
10427                         if (!isset($style['position'])) {
10428                                 if ($this->rtl) {
10429                                         $style['position'] = 'R';
10430                                 } else {
10431                                         $style['position'] = 'L';
10432                                 }
10433                         }
10434                         if (!isset($style['padding'])) {
10435                                 $style['padding'] = 0;
10436                         }
10437                         if (!isset($style['fgcolor'])) {
10438                                 $style['fgcolor'] = array(0,0,0); // default black
10439                         }
10440                         if (!isset($style['bgcolor'])) {
10441                                 $style['bgcolor'] = false; // default transparent
10442                         }
10443                         if (!isset($style['border'])) {
10444                                 $style['border'] = false;
10445                         }
10446                         if (!isset($style['text'])) {
10447                                 $style['text'] = false;
10448                                 $fontsize = 0;
10449                         }
10450                         if ($style['text'] AND isset($style['font'])) {
10451                                 if (isset($style['fontsize'])) {
10452                                         $fontsize = $style['fontsize'];
10453                                 } else {
10454                                         $fontsize = 0;
10455                                 }
10456                                 $this->SetFont($style['font'], '', $fontsize);
10457                         }
10458                         if (!isset($style['stretchtext'])) {
10459                                 $style['stretchtext'] = 4;
10460                         }
10461                         // set foreground color
10462                         $this->SetDrawColorArray($style['fgcolor']);
10463                         $this->SetTextColorArray($style['fgcolor']);
10464                         if ($this->empty_string($w) OR ($w <= 0)) {
10465                                 if ($this->rtl) {
10466                                         $w = $this->x - $this->lMargin;
10467                                 } else {
10468                                         $w = $this->w - $this->rMargin - $this->x;
10469                                 }
10470                         }
10471                         if ($this->empty_string($x)) {
10472                                 $x = $this->GetX();
10473                         }
10474                         if ($this->rtl) {
10475                                 $x = $this->w - $x;
10476                         }
10477                         if ($this->empty_string($y)) {
10478                                 $y = $this->GetY();
10479                         }
10480                         if ($this->empty_string($xres)) {
10481                                 $xres = 0.4;
10482                         }
10483                         $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
10484                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
10485                         if ($this->empty_string($h) OR ($h <= 0)) {
10486                                 $h = 10 + $extraspace;
10487                         }
10488                         if ($this->checkPageBreak($h)) {
10489                                 $y = $this->y;
10490                         }
10491                         // maximum bar heigth
10492                         $barh = $h - $extraspace;
10493                         switch ($style['position']) {
10494                                 case 'L': { // left
10495                                         if ($this->rtl) {
10496                                                 $xpos = $x - $w;
10497                                         } else {
10498                                                 $xpos = $x;
10499                                         }
10500                                         break;
10501                                 }
10502                                 case 'C': { // center
10503                                         $xdiff = (($w - $fbw) / 2);
10504                                         if ($this->rtl) {
10505                                                 $xpos = $x - $w + $xdiff;
10506                                         } else {
10507                                                 $xpos = $x + $xdiff;
10508                                         }
10509                                         break;
10510                                 }
10511                                 case 'R': { // right
10512                                         if ($this->rtl) {
10513                                                 $xpos = $x - $fbw;
10514                                         } else {
10515                                                 $xpos = $x + $w - $fbw;
10516                                         }
10517                                         break;
10518                                 }
10519                                 case 'S': { // stretch
10520                                         $fbw = $w;
10521                                         $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
10522                                         if ($this->rtl) {
10523                                                 $xpos = $x - $w;
10524                                         } else {
10525                                                 $xpos = $x;
10526                                         }
10527                                         break;
10528                                 }
10529                         }
10530                         $xpos_rect = $xpos;
10531                         $xpos = $xpos_rect + $style['padding'];
10532                         $xpos_text = $xpos;
10533                         // barcode is always printed in LTR direction
10534                         $tempRTL = $this->rtl;
10535                         $this->rtl = false;
10536                         // print background color
10537                         if ($style['bgcolor']) {
10538                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style['bgcolor']);
10539                         } elseif ($style['border']) {
10540                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
10541                         }
10542                         // print bars
10543                         if ($arrcode !== false) {
10544                                 foreach ($arrcode['bcode'] as $k => $v) {
10545                                         $bw = ($v['w'] * $xres);
10546                                         if ($v['t']) {
10547                                                 // draw a vertical bar
10548                                                 $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
10549                                                 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
10550                                         }
10551                                         $xpos += $bw;
10552                                 }
10553                         }
10554                         // print text
10555                         if ($style['text']) {
10556                                 // print text
10557                                 $this->x = $xpos_text;
10558                                 $this->y = $y + $style['padding'] + $barh; 
10559                                 $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
10560                         }
10561                         // restore original direction
10562                         $this->rtl = $tempRTL;
10563                         // restore previous settings
10564                         $this->setGraphicVars($gvars);
10565                         // set bottomcoordinates
10566                         $this->img_rb_y = $y + $h;
10567                         if ($this->rtl) {
10568                                 // set left side coordinate
10569                                 $this->img_rb_x = ($this->w - $x - $w);
10570                         } else {
10571                                 // set right side coordinate
10572                                 $this->img_rb_x = $x + $w;
10573                         }
10574                         // set pointer to align the successive text/objects
10575                         switch($align) {
10576                                 case 'T':{
10577                                         $this->y = $y;
10578                                         $this->x = $this->img_rb_x;
10579                                         break;
10580                                 }
10581                                 case 'M':{
10582                                         $this->y = $y + round($h/2);
10583                                         $this->x = $this->img_rb_x;
10584                                         break;
10585                                 }
10586                                 case 'B':{
10587                                         $this->y = $this->img_rb_y;
10588                                         $this->x = $this->img_rb_x;
10589                                         break;
10590                                 }
10591                                 case 'N':{
10592                                         $this->SetY($this->img_rb_y);
10593                                         break;
10594                                 }
10595                                 default:{
10596                                         break;
10597                                 }
10598                         }
10599                 }
10600                 
10601                 /**
10602                  * This function is DEPRECATED, please use the new write1DBarcode() function.
10603                  * @param int $x x position in user units
10604                  * @param int $y y position in user units
10605                  * @param int $w width in user units
10606                  * @param int $h height position in user units
10607                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
10608                  * @param string $style barcode style
10609                  * @param string $font font for text
10610                  * @param int $xres x resolution
10611                  * @param string $code code to print
10612                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
10613                  * @access public
10614                  * @see write1DBarcode()
10615                  */
10616                 public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
10617                         // convert old settings for the new write1DBarcode() function.
10618                         $xres = 1 / $xres;
10619                         $newstyle = array(
10620                                 'position' => 'L',
10621                                 'border' => false,
10622                                 'padding' => 0,
10623                                 'fgcolor' => array(0,0,0),
10624                                 'bgcolor' => false,
10625                                 'text' => true,
10626                                 'font' => $font,
10627                                 'fontsize' => 8,
10628                                 'stretchtext' => 4
10629                         );
10630                         if ($style & 1) {
10631                                 $newstyle['border'] = true;
10632                         }
10633                         if ($style & 2) {
10634                                 $newstyle['bgcolor'] = false;
10635                         }
10636                         if ($style & 4) {
10637                                 $newstyle['position'] = 'C';
10638                         } elseif ($style & 8) {
10639                                 $newstyle['position'] = 'L';
10640                         } elseif ($style & 16) {
10641                                 $newstyle['position'] = 'R';
10642                         }
10643                         if ($style & 128) {
10644                                 $newstyle['text'] = true;
10645                         }
10646                         if ($style & 256) {
10647                                 $newstyle['stretchtext'] = 4;
10648                         }
10649                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
10650                 }
10651                 
10652                 /**
10653                  * Print 2D Barcode.
10654                  * @param string $code code to print
10655                  * @param string $type type of barcode.
10656                  * @param int $x x position in user units
10657                  * @param int $y y position in user units
10658                  * @param int $w width in user units
10659                  * @param int $h height in user units
10660                  * @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>
10661                  * @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>
10662                  * @author Nicola Asuni
10663                  * @since 4.5.037 (2009-04-07)
10664                  * @access public
10665                  */
10666                 public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
10667                         if ($this->empty_string($code)) {
10668                                 return;
10669                         }
10670                         require_once(dirname(__FILE__).'/2dbarcodes.php');
10671                         // save current graphic settings
10672                         $gvars = $this->getGraphicVars();
10673                         // create new barcode object
10674                         $barcodeobj = new TCPDF2DBarcode($code, $type);
10675                         $arrcode = $barcodeobj->getBarcodeArray();
10676                         if ($arrcode === false) {
10677                                 $this->Error('Error in 2D barcode string');
10678                         }
10679                         // set default values
10680                         if (!isset($style['padding'])) {
10681                                 $style['padding'] = 0;
10682                         }
10683                         if (!isset($style['fgcolor'])) {
10684                                 $style['fgcolor'] = array(0,0,0); // default black
10685                         }
10686                         if (!isset($style['bgcolor'])) {
10687                                 $style['bgcolor'] = false; // default transparent
10688                         }
10689                         if (!isset($style['border'])) {
10690                                 $style['border'] = false;
10691                         }
10692                         // set foreground color
10693                         $this->SetDrawColorArray($style['fgcolor']);
10694                         if ($this->empty_string($x)) {
10695                                 $x = $this->GetX();
10696                         }
10697                         if ($this->rtl) {
10698                                 $x = $this->w - $x;
10699                         }
10700                         if ($this->empty_string($y)) {
10701                                 $y = $this->GetY();
10702                         }
10703                         if ($this->empty_string($w) OR ($w <= 0)) {
10704                                 if ($this->rtl) {
10705                                         $w = $x - $this->lMargin;
10706                                 } else {
10707                                         $w = $this->w - $this->rMargin - $x;
10708                                 }
10709                         }
10710                         if ($this->empty_string($h) OR ($h <= 0)) {
10711                                 // 2d barcodes are square by default
10712                                 $h = $w;
10713                         }
10714                         if ($this->checkPageBreak($h)) {
10715                                 $y = $this->y;
10716                         }
10717                         // calculate barcode size (excluding padding)
10718                         $bw = $w - (2 * $style['padding']);
10719                         $bh = $h - (2 * $style['padding']);
10720                         // calculate starting coordinates
10721                         if ($this->rtl) {
10722                                 $xpos = $x - $w;
10723                         } else {
10724                                 $xpos = $x;
10725                         }
10726                         $xpos += $style['padding'];
10727                         $ypos = $y + $style['padding'];
10728                         // barcode is always printed in LTR direction
10729                         $tempRTL = $this->rtl;
10730                         $this->rtl = false;
10731                         // print background color
10732                         if ($style['bgcolor']) {
10733                                 $this->Rect($x, $y, $w, $h, 'DF', '', $style['bgcolor']);
10734                         } elseif ($style['border']) {
10735                                 $this->Rect($x, $y, $w, $h, 'D');
10736                         }
10737                         // print barcode cells
10738                         if ($arrcode !== false) {
10739                                 $rows = $arrcode['num_rows'];
10740                                 $cols = $arrcode['num_cols'];
10741                                 // calculate dimension of single barcode cell
10742                                 $cw = $bw / $cols;
10743                                 $ch = $bh / $rows;
10744                                 // for each row
10745                                 for ($r = 0; $r < $rows; ++$r) {
10746                                         $xr = $xpos;
10747                                         // for each column
10748                                         for ($c = 0; $c < $cols; ++$c) {
10749                                                 if ($arrcode['bcode'][$r][$c] == 1) {
10750                                                         // draw a single barcode cell
10751                                                         $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
10752                                                 }
10753                                                 $xr += $cw;
10754                                         }
10755                                         $ypos += $ch;
10756                                 }
10757                         }
10758                         // restore original direction
10759                         $this->rtl = $tempRTL;
10760                         // restore previous settings
10761                         $this->setGraphicVars($gvars);
10762                         // set bottomcoordinates
10763                         $this->img_rb_y = $y + $h;
10764                         if ($this->rtl) {
10765                                 // set left side coordinate
10766                                 $this->img_rb_x = ($this->w - $x - $w);
10767                         } else {
10768                                 // set right side coordinate
10769                                 $this->img_rb_x = $x + $w;
10770                         }
10771                         // set pointer to align the successive text/objects
10772                         switch($align) {
10773                                 case 'T':{
10774                                         $this->y = $y;
10775                                         $this->x = $this->img_rb_x;
10776                                         break;
10777                                 }
10778                                 case 'M':{
10779                                         $this->y = $y + round($h/2);
10780                                         $this->x = $this->img_rb_x;
10781                                         break;
10782                                 }
10783                                 case 'B':{
10784                                         $this->y = $this->img_rb_y;
10785                                         $this->x = $this->img_rb_x;
10786                                         break;
10787                                 }
10788                                 case 'N':{
10789                                         $this->SetY($this->img_rb_y);
10790                                         break;
10791                                 }
10792                                 default:{
10793                                         break;
10794                                 }
10795                         }
10796                 }
10797                 
10798                 /**
10799                  * Returns an array containing current margins:
10800                  * <ul>
10801                                 <li>$ret['left'] = left  margin</li>
10802                                 <li>$ret['right'] = right margin</li>
10803                                 <li>$ret['top'] = top margin</li>
10804                                 <li>$ret['bottom'] = bottom margin</li>
10805                                 <li>$ret['header'] = header margin</li>
10806                                 <li>$ret['footer'] = footer margin</li>
10807                                 <li>$ret['cell'] = cell margin</li>
10808                  * </ul>
10809                  * @return array containing all margins measures 
10810                  * @access public
10811                  * @since 3.2.000 (2008-06-23)
10812                  */
10813                 public function getMargins() {
10814                         $ret = array(
10815                                 'left' => $this->lMargin,
10816                                 'right' => $this->rMargin,
10817                                 'top' => $this->tMargin,
10818                                 'bottom' => $this->bMargin,
10819                                 'header' => $this->header_margin,
10820                                 'footer' => $this->footer_margin,
10821                                 'cell' => $this->cMargin,
10822                         );
10823                         return $ret;
10824                 }
10825                 
10826                 /**
10827                  * Returns an array containing original margins:
10828                  * <ul>
10829                                 <li>$ret['left'] = left  margin</li>
10830                                 <li>$ret['right'] = right margin</li>
10831                  * </ul>
10832                  * @return array containing all margins measures 
10833                  * @access public
10834                  * @since 4.0.012 (2008-07-24)
10835                  */
10836                 public function getOriginalMargins() {
10837                         $ret = array(
10838                                 'left' => $this->original_lMargin,
10839                                 'right' => $this->original_rMargin
10840                         );
10841                         return $ret;
10842                 }
10843                 
10844                 /**
10845                  * Returns the current font size.
10846                  * @return current font size
10847                  * @access public
10848                  * @since 3.2.000 (2008-06-23)
10849                  */
10850                 public function getFontSize() {
10851                         return $this->FontSize;
10852                 }
10853                 
10854                 /**
10855                  * Returns the current font size in points unit.
10856                  * @return current font size in points unit
10857                  * @access public
10858                  * @since 3.2.000 (2008-06-23)
10859                  */
10860                 public function getFontSizePt() {
10861                         return $this->FontSizePt;
10862                 }
10863
10864                 /**
10865                  * Returns the current font family name.
10866                  * @return string current font family name
10867                  * @access public
10868                  * @since 4.3.008 (2008-12-05)
10869                  */
10870                 public function getFontFamily() {
10871                         return $this->FontFamily;
10872                 }
10873
10874                 /**
10875                  * Returns the current font style.
10876                  * @return string current font style
10877                  * @access public
10878                  * @since 4.3.008 (2008-12-05)
10879                  */
10880                 public function getFontStyle() {
10881                         return $this->FontStyle;
10882                 }
10883                 
10884                 /**
10885                  * Prints a cell (rectangular area) with optional borders, background color and html text string. 
10886                  * 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 />
10887                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
10888                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
10889                  * @param float $h Cell minimum height. The cell extends automatically if needed.
10890                  * @param float $x upper-left corner X coordinate
10891                  * @param float $y upper-left corner Y coordinate
10892                  * @param string $html html text to print. Default value: empty string.
10893                  * @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>
10894                  * @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>
10895         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
10896                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
10897                  * @param boolean $reseth if true reset the last cell height (default true).
10898                  * @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>
10899                  * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
10900                  * @access public
10901                  * @uses MultiCell()
10902                  * @see Multicell(), writeHTML()
10903                  */
10904                 public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
10905                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
10906                 }
10907                 
10908                 /**
10909                  * Returns the HTML DOM array.
10910                  * <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>
10911                  * @param string $html html code
10912                  * @return array
10913                  * @access protected
10914                  * @since 3.2.000 (2008-06-20)
10915                  */
10916                 protected function getHtmlDomArray($html) {
10917                         // remove all unsupported tags (the line below lists all supported tags)
10918                         $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>');
10919                         //replace some blank characters
10920                         $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
10921                         $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);
10922                         $html = preg_replace('@(\r\n|\r)@', "\n", $html);
10923                         $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
10924                         $html = strtr($html, $repTable);
10925                         while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html)) {
10926                                 // preserve newlines on <pre> tag
10927                                 $html = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html);
10928                         }
10929                         $html = str_replace("\n", ' ', $html);
10930                         // remove extra spaces from code
10931                         $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li)>/', '</\\1>', $html);
10932                         $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|br)/', '<\\1', $html);
10933                         $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);
10934                         $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
10935                         $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
10936                         $html = preg_replace('/<img/', ' <img', $html);
10937                         $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span></span>', $html);
10938                         $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
10939                         // trim string
10940                         $html = preg_replace('/^[\s]+/', '', $html);
10941                         $html = preg_replace('/[\s]+$/', '', $html);
10942                         // pattern for generic tag
10943                         $tagpattern = '/(<[^>]+>)/';
10944                         // explodes the string
10945                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
10946                         // count elements
10947                         $maxel = count($a);
10948                         $elkey = 0;
10949                         $key = 0;
10950                         // create an array of elements
10951                         $dom = array();
10952                         $dom[$key] = array();
10953                         // set first void element
10954                         $dom[$key]['tag'] = false;
10955                         $dom[$key]['value'] = '';
10956                         $dom[$key]['parent'] = 0;
10957                         $dom[$key]['fontname'] = $this->FontFamily;
10958                         $dom[$key]['fontstyle'] = $this->FontStyle;
10959                         $dom[$key]['fontsize'] = $this->FontSizePt;
10960                         $dom[$key]['bgcolor'] = false;
10961                         $dom[$key]['fgcolor'] = $this->fgcolor;
10962                         $dom[$key]['align'] = '';
10963                         $dom[$key]['listtype'] = '';
10964                         $thead = false; // true when we are inside the THEAD tag
10965                         ++$key;
10966                         $level = array();
10967                         array_push($level, 0); // root
10968                         while ($elkey < $maxel) {
10969                                 $dom[$key] = array();
10970                                 $element = $a[$elkey];
10971                                 $dom[$key]['elkey'] = $elkey;
10972                                 if (preg_match($tagpattern, $element)) {
10973                                         // html tag
10974                                         $element = substr($element, 1, -1);
10975                                         // get tag name
10976                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
10977                                         $tagname = strtolower($tag[1]);
10978                                         // check if we are inside a table header
10979                                         if ($tagname == 'thead') {
10980                                                 if ($element{0} == '/') {
10981                                                         $thead = false;
10982                                                 } else {
10983                                                         $thead = true;
10984                                                 }
10985                                                 ++$elkey;
10986                                                 continue;
10987                                         }
10988                                         $dom[$key]['tag'] = true;
10989                                         $dom[$key]['value'] = $tagname;
10990                                         if ($element{0} == '/') {
10991                                                 // closing html tag
10992                                                 $dom[$key]['opening'] = false;
10993                                                 $dom[$key]['parent'] = end($level);
10994                                                 array_pop($level);
10995                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
10996                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
10997                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
10998                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
10999                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
11000                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
11001                                                 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
11002                                                         $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
11003                                                 }
11004                                                 // set the number of columns in table tag
11005                                                 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
11006                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
11007                                                 }
11008                                                 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11009                                                         $dom[($dom[$key]['parent'])]['content'] = '';
11010                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
11011                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
11012                                                         }
11013                                                         $key = $i;
11014                                                 }
11015                                                 // store header rows on a new table
11016                                                 if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] == true)) {
11017                                                         if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
11018                                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
11019                                                         }
11020                                                         for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
11021                                                                 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
11022                                                         }
11023                                                 }
11024                                                 if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
11025                                                         $dom[($dom[$key]['parent'])]['thead'] .= '</table>';
11026                                                 }
11027                                         } else {
11028                                                 // opening html tag
11029                                                 $dom[$key]['opening'] = true;
11030                                                 $dom[$key]['parent'] = end($level);
11031                                                 if (substr($element, -1, 1) != '/') {
11032                                                         // not self-closing tag
11033                                                         array_push($level, $key);
11034                                                         $dom[$key]['self'] = false;
11035                                                 } else {
11036                                                         $dom[$key]['self'] = true;
11037                                                 }
11038                                                 // copy some values from parent
11039                                                 $parentkey = 0;
11040                                                 if ($key > 0) {
11041                                                         $parentkey = $dom[$key]['parent'];
11042                                                         $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
11043                                                         $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
11044                                                         $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
11045                                                         $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
11046                                                         $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
11047                                                         $dom[$key]['align'] = $dom[$parentkey]['align'];
11048                                                         $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11049                                                 }
11050                                                 // get attributes
11051                                                 preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
11052                                                 $dom[$key]['attribute'] = array(); // reset attribute array
11053                                                 while (list($id, $name) = each($attr_array[1])) {
11054                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
11055                                                 }
11056                                                 // split style attributes
11057                                                 if (isset($dom[$key]['attribute']['style'])) {
11058                                                         // get style attributes
11059                                                         preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
11060                                                         $dom[$key]['style'] = array(); // reset style attribute array
11061                                                         while (list($id, $name) = each($style_array[1])) {
11062                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
11063                                                         }
11064                                                         // --- get some style attributes ---
11065                                                         if (isset($dom[$key]['style']['font-family'])) {
11066                                                                 // font family
11067                                                                 if (isset($dom[$key]['style']['font-family'])) {
11068                                                                         $fontslist = explode(',', strtolower($dom[$key]['style']['font-family']));
11069                                                                         foreach ($fontslist as $font) {
11070                                                                                 $font = trim(strtolower($font));
11071                                                                                 if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11072                                                                                         $dom[$key]['fontname'] = $font;
11073                                                                                         break;
11074                                                                                 }
11075                                                                         }
11076                                                                 }
11077                                                         }
11078                                                         // list-style-type
11079                                                         if (isset($dom[$key]['style']['list-style-type'])) {
11080                                                                 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
11081                                                                 if ($dom[$key]['listtype'] == 'inherit') {
11082                                                                         $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
11083                                                                 }
11084                                                         }
11085                                                         // font size
11086                                                         if (isset($dom[$key]['style']['font-size'])) {
11087                                                                 $fsize = trim($dom[$key]['style']['font-size']);
11088                                                                 switch ($fsize) {
11089                                                                         // absolute-size
11090                                                                         case 'xx-small': {
11091                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
11092                                                                                 break;
11093                                                                         }
11094                                                                         case 'x-small': {
11095                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
11096                                                                                 break;
11097                                                                         }
11098                                                                         case 'small': {
11099                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
11100                                                                                 break;
11101                                                                         }
11102                                                                         case 'medium': {
11103                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
11104                                                                                 break;
11105                                                                         }
11106                                                                         case 'large': {
11107                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
11108                                                                                 break;
11109                                                                         }
11110                                                                         case 'x-large': {
11111                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
11112                                                                                 break;
11113                                                                         }
11114                                                                         case 'xx-large': {
11115                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
11116                                                                                 break;
11117                                                                         }
11118                                                                         // relative-size
11119                                                                         case 'smaller': {
11120                                                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
11121                                                                                 break;
11122                                                                         }
11123                                                                         case 'larger': {
11124                                                                                 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
11125                                                                                 break;
11126                                                                         }
11127                                                                         default: {
11128                                                                                 $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
11129                                                                         }
11130                                                                 }
11131                                                         }
11132                                                         // font style
11133                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
11134                                                                 $dom[$key]['fontstyle'] .= 'B';
11135                                                         }
11136                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
11137                                                                 $dom[$key]['fontstyle'] .= '"I';
11138                                                         }
11139                                                         // font color
11140                                                         if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
11141                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
11142                                                         }
11143                                                         // background color
11144                                                         if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
11145                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
11146                                                         }
11147                                                         // text-decoration
11148                                                         if (isset($dom[$key]['style']['text-decoration'])) {
11149                                                                 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
11150                                                                 foreach ($decors as $dec) {
11151                                                                         $dec = trim($dec);
11152                                                                         if (!$this->empty_string($dec)) {
11153                                                                                 if ($dec{0} == 'u') {
11154                                                                                         $dom[$key]['fontstyle'] .= 'U';
11155                                                                                 } elseif ($dec{0} == 'l') {
11156                                                                                         $dom[$key]['fontstyle'] .= 'D';
11157                                                                                 }
11158                                                                         }
11159                                                                 }
11160                                                         }
11161                                                         // check for width attribute
11162                                                         if (isset($dom[$key]['style']['width'])) {
11163                                                                 $dom[$key]['width'] = $dom[$key]['style']['width'];
11164                                                         }
11165                                                         // check for height attribute
11166                                                         if (isset($dom[$key]['style']['height'])) {
11167                                                                 $dom[$key]['height'] = $dom[$key]['style']['height'];
11168                                                         }
11169                                                         // check for text alignment
11170                                                         if (isset($dom[$key]['style']['text-align'])) {
11171                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
11172                                                         }
11173                                                         // check for border attribute
11174                                                         if (isset($dom[$key]['style']['border'])) {
11175                                                                 $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
11176                                                         }
11177                                                 }
11178                                                 // check for font tag
11179                                                 if ($dom[$key]['value'] == 'font') {
11180                                                         // font family
11181                                                         if (isset($dom[$key]['attribute']['face'])) {
11182                                                                 $fontslist = explode(',', strtolower($dom[$key]['attribute']['face']));
11183                                                                 foreach ($fontslist as $font) {
11184                                                                         $font = trim(strtolower($font));
11185                                                                         if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
11186                                                                                 $dom[$key]['fontname'] = $font;
11187                                                                                 break;
11188                                                                         }
11189                                                                 }
11190                                                         }
11191                                                         // font size
11192                                                         if (isset($dom[$key]['attribute']['size'])) {
11193                                                                 if ($key > 0) {
11194                                                                         if ($dom[$key]['attribute']['size']{0} == '+') {
11195                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
11196                                                                         } elseif ($dom[$key]['attribute']['size']{0} == '-') {
11197                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
11198                                                                         } else {
11199                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11200                                                                         }
11201                                                                 } else {
11202                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
11203                                                                 }
11204                                                         }
11205                                                 }
11206                                                 // force natural alignment for lists
11207                                                 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
11208                                                         AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
11209                                                         if ($this->rtl) {
11210                                                                 $dom[$key]['align'] = 'R';
11211                                                         } else {
11212                                                                 $dom[$key]['align'] = 'L';
11213                                                         }
11214                                                 }
11215                                                 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
11216                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
11217                                                 }
11218                                                 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
11219                                                         $dom[$key]['fontstyle'] .= 'B';
11220                                                 }
11221                                                 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
11222                                                         $dom[$key]['fontstyle'] .= 'I';
11223                                                 }
11224                                                 if ($dom[$key]['value'] == 'u') {
11225                                                         $dom[$key]['fontstyle'] .= 'U';
11226                                                 }
11227                                                 if ($dom[$key]['value'] == 'del') {
11228                                                         $dom[$key]['fontstyle'] .= 'D';
11229                                                 }
11230                                                 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
11231                                                         $dom[$key]['fontname'] = $this->default_monospaced_font;
11232                                                 }
11233                                                 if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
11234                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
11235                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
11236                                                         $dom[$key]['fontstyle'] .= 'B';
11237                                                 }
11238                                                 if (($dom[$key]['value'] == 'table')) {
11239                                                         $dom[$key]['rows'] = 0; // number of rows
11240                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
11241                                                         $dom[$key]['thead'] = ''; // table header rows
11242                                                 }
11243                                                 if (($dom[$key]['value'] == 'tr')) {
11244                                                         $dom[$key]['cols'] = 0;
11245                                                         // store the number of rows on table element
11246                                                         ++$dom[($dom[$key]['parent'])]['rows'];
11247                                                         // store the TR elements IDs on table element
11248                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
11249                                                         if ($thead) {
11250                                                                 $dom[$key]['thead'] = true;
11251                                                         } else {
11252                                                                 $dom[$key]['thead'] = false;
11253                                                         }
11254                                                 }
11255                                                 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
11256                                                         if (isset($dom[$key]['attribute']['colspan'])) {
11257                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
11258                                                         } else {
11259                                                                 $colspan = 1;
11260                                                         }
11261                                                         $dom[$key]['attribute']['colspan'] = $colspan;
11262                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
11263                                                 }
11264                                                 // set foreground color attribute
11265                                                 if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
11266                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
11267                                                 }
11268                                                 // set background color attribute
11269                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
11270                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
11271                                                 }
11272                                                 // check for width attribute
11273                                                 if (isset($dom[$key]['attribute']['width'])) {
11274                                                         $dom[$key]['width'] = $dom[$key]['attribute']['width'];
11275                                                 }
11276                                                 // check for height attribute
11277                                                 if (isset($dom[$key]['attribute']['height'])) {
11278                                                         $dom[$key]['height'] = $dom[$key]['attribute']['height'];
11279                                                 }
11280                                                 // check for text alignment
11281                                                 if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
11282                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
11283                                                 }
11284                                         } // end opening tag
11285                                 } else {
11286                                         // text
11287                                         $dom[$key]['tag'] = false;
11288                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
11289                                         $dom[$key]['parent'] = end($level);
11290                                 }
11291                                 ++$elkey;
11292                                 ++$key;
11293                         }
11294                         return $dom;
11295                 }
11296                 
11297                 /**
11298                  * Allows to preserve some HTML formatting (limited support).<br />
11299                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
11300                  * 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
11301                  * @param string $html text to display
11302                  * @param boolean $ln if true add a new line after text (default = true)
11303                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
11304                  * @param boolean $reseth if true reset the last cell height (default false).
11305                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
11306                  * @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>
11307                  * @access public
11308                  */
11309                 public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
11310                         $gvars = $this->getGraphicVars();
11311                         // store current values
11312                         $prevPage = $this->page;
11313                         $prevlMargin = $this->lMargin;
11314                         $prevrMargin = $this->rMargin;
11315                         $curfontname = $this->FontFamily;
11316                         $curfontstyle = $this->FontStyle;
11317                         $curfontsize = $this->FontSizePt;       
11318                         $this->newline = true;
11319                         $minstartliney = $this->y;
11320                         $yshift = 0;
11321                         $startlinepage = $this->page;
11322                         $newline = true;
11323                         $loop = 0;
11324                         $curpos = 0;
11325                         $blocktags = array('blockquote','br','dd','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','ul','tcpdf');
11326                         $this->premode = false;
11327                         if (isset($this->PageAnnots[$this->page])) {
11328                                 $pask = count($this->PageAnnots[$this->page]);
11329                         } else {
11330                                 $pask = 0;
11331                         }
11332                         if (isset($this->footerlen[$this->page])) {
11333                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11334                         } else {
11335                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11336                         }
11337                         $startlinepos = $this->footerpos[$this->page];
11338                         $lalign = $align;
11339                         $plalign = $align;
11340                         if ($this->rtl) {
11341                                 $w = $this->x - $this->lMargin;
11342                         } else {
11343                                 $w = $this->w - $this->rMargin - $this->x;
11344                         }
11345                         $w -= (2 * $this->cMargin);
11346                         if ($cell) {
11347                                 if ($this->rtl) {
11348                                         $this->x -= $this->cMargin;
11349                                 } else {
11350                                         $this->x += $this->cMargin;
11351                                 }
11352                         }
11353                         if ($this->customlistindent >= 0) {
11354                                 $this->listindent = $this->customlistindent;
11355                         } else {
11356                                 $this->listindent = $this->GetStringWidth('0000');
11357                         }
11358                         $this->listnum = 0;
11359                         if (($this->empty_string($this->lasth)) OR ($reseth)) {
11360                                 //set row height
11361                                 $this->lasth = $this->FontSize * $this->cell_height_ratio; 
11362                         }
11363                         $dom = $this->getHtmlDomArray($html);
11364                         $maxel = count($dom);
11365                         $key = 0;
11366                         while ($key < $maxel) {
11367                                 if ($dom[$key]['tag'] OR ($key == 0)) {
11368                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
11369                                                 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
11370                                         }
11371                                         // vertically align image in line
11372                                         if ((!$this->newline)
11373                                                 AND ($dom[$key]['value'] == 'img')
11374                                                 AND (isset($dom[$key]['attribute']['height']))
11375                                                 AND ($dom[$key]['attribute']['height'] > 0)) {
11376                                                 // get image height
11377                                                 $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
11378                                                 if (!$this->InFooter) {
11379                                                         // check for page break
11380                                                         $this->checkPageBreak($imgh);
11381                                                 }
11382                                                 if ($this->page > $startlinepage) {
11383                                                         // fix lines splitted over two pages
11384                                                         if (isset($this->footerlen[$startlinepage])) {
11385                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11386                                                         }
11387                                                         // line to be moved one page forward
11388                                                         $pagebuff = $this->getPageBuffer($startlinepage);
11389                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11390                                                         $tstart = substr($pagebuff, 0, $startlinepos);
11391                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11392                                                         // remove line start from previous page
11393                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11394                                                         $pagebuff = $this->getPageBuffer($this->page);
11395                                                         $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11396                                                         $tend = substr($pagebuff, $this->intmrk[$this->page]);
11397                                                         // add line start to current page
11398                                                         $yshift = $minstartliney - $this->y;
11399                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11400                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11401                                                         // shift the annotations and links
11402                                                         if (isset($this->PageAnnots[$startlinepage])) {
11403                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11404                                                                         if ($pak >= $pask) {
11405                                                                                 $this->PageAnnots[$this->page][] = $pac;
11406                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11407                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11408                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11409                                                                         }
11410                                                                 }
11411                                                         }
11412                                                         $startlinepos = $this->intmrk[$this->page];
11413                                                         $startlinepage = $this->page;
11414                                                         $startliney = $this->y;
11415                                                 }
11416                                                 
11417                                                 $this->y += (($curfontsize / $this->k) - $imgh);
11418                                                 $minstartliney = min($this->y, $minstartliney);
11419                                                 
11420                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
11421                                                 // account for different font size
11422                                                 $pfontname = $curfontname;
11423                                                 $pfontstyle = $curfontstyle;
11424                                                 $pfontsize = $curfontsize;
11425                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
11426                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
11427                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
11428                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
11429                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
11430                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
11431                                                         if (is_numeric($fontsize) AND ($fontsize > 0)
11432                                                                 AND is_numeric($curfontsize) AND ($curfontsize > 0)
11433                                                                 AND ($fontsize != $curfontsize) AND (!$this->newline)
11434                                                                 AND ($key < ($maxel - 1))
11435                                                                 ) {
11436                                                                 if ((!$this->newline) AND ($this->page > $startlinepage)) {
11437                                                                         // fix lines splitted over two pages
11438                                                                         if (isset($this->footerlen[$startlinepage])) {
11439                                                                                 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11440                                                                         }
11441                                                                         // line to be moved one page forward
11442                                                                         $pagebuff = $this->getPageBuffer($startlinepage);
11443                                                                         $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
11444                                                                         $tstart = substr($pagebuff, 0, $startlinepos);
11445                                                                         $tend = substr($this->getPageBuffer($startlinepage), $curpos);
11446                                                                         // remove line start from previous page
11447                                                                         $this->setPageBuffer($startlinepage, $tstart.''.$tend);
11448                                                                         $pagebuff = $this->getPageBuffer($this->page);
11449                                                                         $tstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
11450                                                                         $tend = substr($pagebuff, $this->intmrk[$this->page]);
11451                                                                         // add line start to current page
11452                                                                         $yshift = $minstartliney - $this->y;
11453                                                                         $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
11454                                                                         $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
11455                                                                         // shift the annotations and links
11456                                                                         if (isset($this->PageAnnots[$startlinepage])) {
11457                                                                                 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
11458                                                                                         if ($pak >= $pask) {
11459                                                                                                 $this->PageAnnots[$this->page][] = $pac;
11460                                                                                                 unset($this->PageAnnots[$startlinepage][$pak]);
11461                                                                                                 $npak = count($this->PageAnnots[$this->page]) - 1;
11462                                                                                                 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
11463                                                                                         }
11464                                                                                 }
11465                                                                         }
11466                                                                 }
11467                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
11468                                                                 $minstartliney = min($this->y, $minstartliney);
11469                                                         }
11470                                                         $curfontname = $fontname;
11471                                                         $curfontstyle = $fontstyle;
11472                                                         $curfontsize = $fontsize;
11473                                                 }
11474                                         }
11475                                         if (($plalign == 'J') AND (in_array($dom[$key]['value'], $blocktags))) {
11476                                                 $plalign = '';
11477                                         }
11478                                         // get current position on page buffer
11479                                         $curpos = $this->pagelen[$startlinepage];
11480                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
11481                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
11482                                                 $wfill = true;
11483                                         } else {
11484                                                 $wfill = $fill | false;
11485                                         }
11486                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
11487                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
11488                                         }
11489                                         if (isset($dom[$key]['align'])) {
11490                                                 $lalign = $dom[$key]['align'];
11491                                         }
11492                                         if ($this->empty_string($lalign)) {
11493                                                 $lalign = $align;
11494                                         }
11495                                 }
11496                                 // align lines
11497                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
11498                                         $newline = true;
11499                                         // we are at the beginning of a new line
11500                                         if (isset($startlinex)) {
11501                                                 $yshift = $minstartliney - $startliney;
11502                                                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
11503                                                         $yshift = 0;
11504                                                 }
11505                                                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
11506                                                         // the last line must be shifted to be aligned as requested
11507                                                         $linew = abs($this->endlinex - $startlinex);
11508                                                         $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
11509                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11510                                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11511                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
11512                                                         } elseif (isset($opentagpos)) {
11513                                                                 $midpos = $opentagpos;
11514                                                         } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
11515                                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
11516                                                                 $midpos = $this->footerpos[$startlinepage];
11517                                                         } else {
11518                                                                 $midpos = 0;
11519                                                         }
11520                                                         if ($midpos > 0) {
11521                                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
11522                                                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
11523                                                         } else {
11524                                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
11525                                                                 $pend = '';
11526                                                         }
11527                                                         // calculate shifting amount
11528                                                         $tw = $w;
11529                                                         if ($this->lMargin != $prevlMargin) {
11530                                                                 $tw += ($prevlMargin - $this->lMargin);
11531                                                         }
11532                                                         if ($this->rMargin != $prevrMargin) {
11533                                                                 $tw += ($prevrMargin - $this->rMargin);
11534                                                         }
11535                                                         $mdiff = abs($tw - $linew);
11536                                                         $t_x = 0;
11537                                                         if ($plalign == 'C') {
11538                                                                 if ($this->rtl) {
11539                                                                         $t_x = -($mdiff / 2);
11540                                                                 } else {
11541                                                                         $t_x = ($mdiff / 2);
11542                                                                 }
11543                                                         } elseif (($plalign == 'R') AND (!$this->rtl)) {
11544                                                                 // right alignment on LTR document
11545                                                                 $t_x = $mdiff;  
11546                                                         } elseif (($plalign == 'L') AND ($this->rtl)) {
11547                                                                 // left alignment on RTL document
11548                                                                 $t_x = -$mdiff;
11549                                                         } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
11550                                                                 // Justification
11551                                                                 if ($this->rtl OR $this->tmprtl) {
11552                                                                         $t_x = $this->lMargin - $this->endlinex;
11553                                                                 }
11554                                                                 $no = 0;
11555                                                                 $ns = 0;
11556                                                                 $pmidtemp = $pmid;
11557                                                                 // escape special characters
11558                                                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11559                                                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11560                                                                 // search spaces
11561                                                                 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
11562                                                                         $maxkk = count($lnstring[1]) - 1;
11563                                                                         for ($kk=0; $kk <= $maxkk; ++$kk) {
11564                                                                                 // restore special characters
11565                                                                                 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
11566                                                                                 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
11567                                                                                 if ($kk == $maxkk) {
11568                                                                                         if ($this->rtl OR $this->tmprtl) {
11569                                                                                                 $tvalue = ltrim($lnstring[1][$kk]);
11570                                                                                         } else {
11571                                                                                                 $tvalue = rtrim($lnstring[1][$kk]);
11572                                                                                         }
11573                                                                                 } else {
11574                                                                                         $tvalue = $lnstring[1][$kk];
11575                                                                                 }
11576                                                                                 // count spaces on line
11577                                                                                 $no += substr_count($lnstring[1][$kk], chr(32));
11578                                                                                 $ns += substr_count($tvalue, chr(32));
11579                                                                         }
11580                                                                         if ($this->rtl OR $this->tmprtl) {
11581                                                                                 $t_x = $this->lMargin - $this->endlinex - (($no - $ns - 1) * $this->GetStringWidth(chr(32)));
11582                                                                         }
11583                                                                         // calculate additional space to add to each space
11584                                                                         $spacewidth = (($tw - $linew + (($no - $ns) * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1)) * $this->k;
11585                                                                         $spacewidthu = ($tw - $linew + ($no * $this->GetStringWidth(chr(32)))) / ($ns?$ns:1) / $this->FontSize / $this->k;
11586                                                                         $nsmax = $ns;
11587                                                                         $ns = 0;
11588                                                                         reset($lnstring);
11589                                                                         $offset = 0;
11590                                                                         $strcount = 0;
11591                                                                         $prev_epsposbeg = 0;
11592                                                                         global $spacew;
11593                                                                         while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
11594                                                                                 if ($this->rtl OR $this->tmprtl) {
11595                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
11596                                                                                 } else {
11597                                                                                         $spacew = ($spacewidth * $ns);
11598                                                                                 }
11599                                                                                 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
11600                                                                                 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
11601                                                                                 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
11602                                                                                 if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
11603                                                                                         OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
11604                                                                                         // shift EPS images
11605                                                                                         $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
11606                                                                                         $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
11607                                                                                         $pmid_b = substr($pmid, 0, $epsposbeg);
11608                                                                                         $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
11609                                                                                         $pmid_e = substr($pmid, $epsposend);
11610                                                                                         $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
11611                                                                                         $offset = $epsposend;
11612                                                                                         continue;
11613                                                                                 }
11614                                                                                 $prev_epsposbeg = $epsposbeg;
11615                                                                                 $currentxpos = 0;
11616                                                                                 // shift blocks of code
11617                                                                                 switch ($strpiece[2][0]) {
11618                                                                                         case 'Td':
11619                                                                                         case 'cm':
11620                                                                                         case 'm':
11621                                                                                         case 'l': {
11622                                                                                                 // get current X position
11623                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11624                                                                                                 $currentxpos = $xmatches[1];
11625                                                                                                 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
11626                                                                                                         if ($strcount == $maxkk) {
11627                                                                                                                 if ($this->rtl OR $this->tmprtl) {
11628                                                                                                                         $tvalue = $lnstring[1][$strcount];
11629                                                                                                                 } else {
11630                                                                                                                         $tvalue = rtrim($lnstring[1][$strcount]);
11631                                                                                                                 }
11632                                                                                                         } else {
11633                                                                                                                 $tvalue = $lnstring[1][$strcount];
11634                                                                                                         }
11635                                                                                                         $ns += substr_count($tvalue, chr(32));
11636                                                                                                         ++$strcount;
11637                                                                                                 }
11638                                                                                                 if ($this->rtl OR $this->tmprtl) {
11639                                                                                                         $spacew = ($spacewidth * ($nsmax - $ns));
11640                                                                                                 }
11641                                                                                                 // justify block
11642                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11643                                                                                                         create_function('$matches', 'global $spacew;
11644                                                                                                         $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11645                                                                                                         return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
11646                                                                                                 break;
11647                                                                                         }
11648                                                                                         case 're': {
11649                                                                                                 // get current X position
11650                                                                                                 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
11651                                                                                                 $currentxpos = $xmatches[1];
11652                                                                                                 // justify block
11653                                                                                                 $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
11654                                                                                                         create_function('$matches', 'global $spacew;
11655                                                                                                         $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
11656                                                                                                         return "".$newx." ".$matches[2]." ".$matches[3]." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
11657                                                                                                 break;
11658                                                                                         }
11659                                                                                         case 'c': {
11660                                                                                                 // get current X position
11661                                                                                                 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);
11662                                                                                                 $currentxpos = $xmatches[1];
11663                                                                                                 // justify block
11664                                                                                                 $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',
11665                                                                                                         create_function('$matches', 'global $spacew;
11666                                                                                                         $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
11667                                                                                                         $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
11668                                                                                                         $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
11669                                                                                                         return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
11670                                                                                                 break;
11671                                                                                         }
11672                                                                                 }
11673                                                                                 // shift the annotations and links
11674                                                                                 if (isset($this->PageAnnots[$this->page])) {
11675                                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11676                                                                                                 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
11677                                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
11678                                                                                                         $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
11679                                                                                                         break;
11680                                                                                                 }
11681                                                                                         }
11682                                                                                 }
11683                                                                         } // end of while
11684                                                                         // remove markers
11685                                                                         $pmid = str_replace('x*#!#*x', '', $pmid);
11686                                                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11687                                                                                 // multibyte characters
11688                                                                                 $spacew = $spacewidthu;
11689                                                                                 $pmidtemp = $pmid;
11690                                                                                 // escape special characters
11691                                                                                 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
11692                                                                                 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
11693                                                                                 $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
11694                                                                                                         create_function('$matches', 'global $spacew;
11695                                                                                                         $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
11696                                                                                                         $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
11697                                                                                                         return "[(".str_replace(chr(0).chr(32), ") ".(-2830 * $spacew)." (", $matches[1]).")]";'), $pmidtemp);
11698                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
11699                                                                                 $endlinepos = strlen($pstart."\n".$pmid."\n");
11700                                                                         } else {
11701                                                                                 // non-unicode (single-byte characters)
11702                                                                                 $rs = sprintf("%.3F Tw", $spacewidth);
11703                                                                                 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
11704                                                                                 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
11705                                                                                 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
11706                                                                         }
11707                                                                 }
11708                                                         } // end of J
11709                                                         if (($t_x != 0) OR ($yshift < 0)) {
11710                                                                 // shift the line
11711                                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
11712                                                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
11713                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
11714                                                                 // shift the annotations and links
11715                                                                 if (isset($this->PageAnnots[$this->page])) {
11716                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
11717                                                                                 if ($pak >= $pask) {
11718                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
11719                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
11720                                                                                 }
11721                                                                         }
11722                                                                 }
11723                                                                 $this->y -= $yshift;
11724                                                         }
11725                                                 }
11726                                         }
11727                                         $this->newline = false;
11728                                         $pbrk = $this->checkPageBreak($this->lasth);
11729                                         $this->SetFont($fontname, $fontstyle, $fontsize);
11730                                         if ($wfill) {
11731                                                 $this->SetFillColorArray($this->bgcolor);
11732                                         }
11733                                         $startlinex = $this->x;
11734                                         $startliney = $this->y;
11735                                         $minstartliney = $this->y;
11736                                         $startlinepage = $this->page;
11737                                         if (isset($endlinepos) AND (!$pbrk)) {
11738                                                 $startlinepos = $endlinepos;
11739                                                 unset($endlinepos);
11740                                         } else {
11741                                                 if (isset($this->footerlen[$this->page])) {
11742                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11743                                                 } else {
11744                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11745                                                 }
11746                                                 $startlinepos = $this->footerpos[$this->page];
11747                                         }
11748                                         $plalign = $lalign;
11749                                         if (isset($this->PageAnnots[$this->page])) {
11750                                                 $pask = count($this->PageAnnots[$this->page]);
11751                                         } else {
11752                                                 $pask = 0;
11753                                         }
11754                                 }
11755                                 if (isset($opentagpos)) {
11756                                         unset($opentagpos);
11757                                 }
11758                                 if ($dom[$key]['tag']) {
11759                                         if ($dom[$key]['opening']) {
11760                                                 if ($dom[$key]['value'] == 'table') {
11761                                                         if ($this->rtl) {
11762                                                                 $wtmp = $this->x - $this->lMargin;
11763                                                         } else {
11764                                                                 $wtmp = $this->w - $this->rMargin - $this->x;
11765                                                         }
11766                                                         $wtmp -= (2 * $this->cMargin);
11767                                                         // calculate cell width
11768                                                         if (isset($dom[$key]['width'])) {
11769                                                                 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11770                                                         } else {
11771                                                                 $table_width = $wtmp;
11772                                                         }
11773                                                 }
11774                                                 // table content is handled in a special way
11775                                                 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
11776                                                         $trid = $dom[$key]['parent'];
11777                                                         $table_el = $dom[$trid]['parent'];
11778                                                         if (!isset($dom[$table_el]['cols'])) {
11779                                                                 $dom[$table_el]['cols'] = $trid['cols'];
11780                                                         }
11781                                                         $oldmargin = $this->cMargin;
11782                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
11783                                                                 $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
11784                                                         } else {
11785                                                                 $currentcmargin = 0;            
11786                                                         }
11787                                                         $this->cMargin = $currentcmargin;
11788                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
11789                                                                 $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
11790                                                         } else {
11791                                                                 $cellspacing = 0;
11792                                                         }
11793                                                         if ($this->rtl) {
11794                                                                 $cellspacingx = -$cellspacing;
11795                                                         } else {
11796                                                                 $cellspacingx = $cellspacing;
11797                                                         }
11798                                                         $colspan = $dom[$key]['attribute']['colspan'];
11799                                                         $wtmp = ($colspan * ($table_width / $dom[$table_el]['cols']));
11800                                                         if (isset($dom[$key]['width'])) {
11801                                                                 $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
11802                                                         } else {
11803                                                                 $cellw = $wtmp;
11804                                                         }
11805                                                         if (isset($dom[$key]['height'])) {
11806                                                                 // minimum cell height
11807                                                                 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
11808                                                         } else {
11809                                                                 $cellh = 0;
11810                                                         }
11811                                                         $cellw -= $cellspacing;
11812                                                         if (isset($dom[$key]['content'])) {
11813                                                                 $cell_content = $dom[$key]['content'];
11814                                                         } else {
11815                                                                 $cell_content = '&nbsp;';
11816                                                         }
11817                                                         $tagtype = $dom[$key]['value'];
11818                                                         $parentid = $key;
11819                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
11820                                                                 // move $key index forward
11821                                                                 ++$key;
11822                                                         }
11823                                                         if (!isset($dom[$trid]['startpage'])) {
11824                                                                 $dom[$trid]['startpage'] = $this->page;
11825                                                         } else {
11826                                                                 $this->setPage($dom[$trid]['startpage']);
11827                                                         }
11828                                                         if (!isset($dom[$trid]['starty'])) {
11829                                                                 $dom[$trid]['starty'] = $this->y;
11830                                                         } else {
11831                                                                 $this->y = $dom[$trid]['starty'];
11832                                                         }
11833                                                         if (!isset($dom[$trid]['startx'])) {
11834                                                                 $dom[$trid]['startx'] = $this->x;
11835                                                         }
11836                                                         $this->x += ($cellspacingx / 2);                                                
11837                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
11838                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
11839                                                         } else {
11840                                                                 $rowspan = 1;
11841                                                         }
11842                                                         // skip row-spanned cells started on the previous rows
11843                                                         if (isset($dom[$table_el]['rowspans'])) {
11844                                                                 $rsk = 0;
11845                                                                 $rskmax = count($dom[$table_el]['rowspans']);
11846                                                                 while ($rsk < $rskmax) {
11847                                                                         $trwsp = $dom[$table_el]['rowspans'][$rsk];
11848                                                                         $rsstartx = $trwsp['startx'];
11849                                                                         $rsendx = $trwsp['endx'];
11850                                                                         // account for margin changes
11851                                                                         if ($trwsp['startpage'] < $this->page) {
11852                                                                                 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
11853                                                                                         $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
11854                                                                                         $rsstartx -= $dl;
11855                                                                                         $rsendx -= $dl;
11856                                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
11857                                                                                         $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
11858                                                                                         $rsstartx += $dl;
11859                                                                                         $rsendx += $dl;
11860                                                                                 }
11861                                                                         }
11862                                                                         if  (($trwsp['rowspan'] > 0)
11863                                                                                 AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
11864                                                                                 AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
11865                                                                                 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
11866                                                                                 // set the starting X position of the current cell
11867                                                                                 $this->x = $rsendx + $cellspacingx;
11868                                                                                 if (($trwsp['rowspan'] == 1)
11869                                                                                         AND (isset($dom[$trid]['endy']))
11870                                                                                         AND (isset($dom[$trid]['endpage']))
11871                                                                                         AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
11872                                                                                         // set ending Y position for row
11873                                                                                         $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11874                                                                                         $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
11875                                                                                 }
11876                                                                                 $rsk = 0;
11877                                                                         } else {
11878                                                                                 ++$rsk;
11879                                                                         }
11880                                                                 }
11881                                                         }
11882                                                         // add rowspan information to table element
11883                                                         if ($rowspan > 1) {
11884                                                                 if (isset($this->footerlen[$this->page])) {
11885                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11886                                                                 } else {
11887                                                                         $this->footerpos[$this->page] = $this->pagelen[$this->page];
11888                                                                 }
11889                                                                 $trintmrkpos = $this->footerpos[$this->page];
11890                                                                 $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));
11891                                                         }
11892                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
11893                                                         if ($rowspan > 1) {
11894                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
11895                                                         }
11896                                                         // push background colors
11897                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
11898                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
11899                                                         }
11900                                                         $prevLastH = $this->lasth;
11901                                                         // ****** write the cell content ******
11902                                                         $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
11903                                                         $this->lasth = $prevLastH;
11904                                                         $this->cMargin = $oldmargin;
11905                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
11906                                                         // update the end of row position
11907                                                         if ($rowspan <= 1) {
11908                                                                 if (isset($dom[$trid]['endy'])) {
11909                                                                         if ($this->page == $dom[$trid]['endpage']) {
11910                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
11911                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
11912                                                                                 $dom[$trid]['endy'] = $this->y;
11913                                                                         }
11914                                                                 } else {
11915                                                                         $dom[$trid]['endy'] = $this->y;
11916                                                                 }
11917                                                                 if (isset($dom[$trid]['endpage'])) {
11918                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
11919                                                                 } else {
11920                                                                         $dom[$trid]['endpage'] = $this->page;
11921                                                                 }                                                               
11922                                                         } else {
11923                                                                 // account for row-spanned cells
11924                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
11925                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
11926                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
11927                                                         }
11928                                                         if (isset($dom[$table_el]['rowspans'])) {
11929                                                                 // update endy and endpage on rowspanned cells
11930                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
11931                                                                         if ($trwsp['rowspan'] > 0) {
11932                                                                                 if (isset($dom[$trid]['endpage'])) {
11933                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
11934                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
11935                                                                                         } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
11936                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
11937                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
11938                                                                                         } else {
11939                                                                                                 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
11940                                                                                         }
11941                                                                                 }
11942                                                                         }
11943                                                                 }
11944                                                         }
11945                                                         $this->x += ($cellspacingx / 2);                                                        
11946                                                 } else {
11947                                                         // opening tag (or self-closing tag)
11948                                                         if (!isset($opentagpos)) {
11949                                                                 if (!$this->InFooter) {
11950                                                                         if (isset($this->footerlen[$this->page])) {
11951                                                                                 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
11952                                                                         } else {
11953                                                                                 $this->footerpos[$this->page] = $this->pagelen[$this->page];
11954                                                                         }
11955                                                                         $opentagpos = $this->footerpos[$this->page];
11956                                                                 }
11957                                                         }
11958                                                         $this->openHTMLTagHandler($dom, $key, $cell);
11959                                                 }
11960                                         } else {
11961                                                 // closing tag
11962                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
11963                                         }
11964                                 } elseif (strlen($dom[$key]['value']) > 0) {
11965                                         // print list-item
11966                                         if (!$this->empty_string($this->lispacer)) {
11967                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
11968                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
11969                                                 $minstartliney = $this->y;
11970                                                 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
11971                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
11972                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
11973                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
11974                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
11975                                                         $minstartliney = min($this->y, $minstartliney);
11976                                                 }
11977                                         }
11978                                         // text
11979                                         $this->htmlvspace = 0;
11980                                         if ((!$this->premode) AND ($this->rtl OR $this->tmprtl)) {
11981                                                 // reverse spaces order
11982                                                 $len1 = strlen($dom[$key]['value']);
11983                                                 $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
11984                                                 $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
11985                                                 $tmpstr = '';
11986                                                 if ($rsp > 0) {
11987                                                         $tmpstr .= substr($dom[$key]['value'], -$rsp);
11988                                                 }
11989                                                 $tmpstr .= trim($dom[$key]['value']);
11990                                                 if ($lsp > 0) {
11991                                                         $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
11992                                                 }
11993                                                 $dom[$key]['value'] = $tmpstr;
11994                                         }
11995                                         if ($newline) {
11996                                                 if (!$this->premode) {
11997                                                         if (($this->rtl OR $this->tmprtl)) {
11998                                                                 $dom[$key]['value'] = rtrim($dom[$key]['value']);
11999                                                         } else {
12000                                                                 $dom[$key]['value'] = ltrim($dom[$key]['value']);
12001                                                         }
12002                                                 }
12003                                                 $newline = false;
12004                                                 $firstblock = true;
12005                                         } else {
12006                                                 $firstblock = false;
12007                                         }
12008                                         $strrest = '';
12009                                         if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
12010                                                 // HTML <a> Link
12011                                                 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style']);
12012                                         } else {
12013                                                 $ctmpmargin = $this->cMargin;
12014                                                 $this->cMargin = 0;
12015                                                 // ****** write only until the end of the line and get the rest ******
12016                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock);
12017                                                 $this->cMargin = $ctmpmargin;
12018                                         }
12019                                         if (strlen($strrest) > 0) {
12020                                                 // store the remaining string on the previous $key position
12021                                                 $this->newline = true;
12022                                                 if ($cell) {
12023                                                         if ($this->rtl) {
12024                                                                 $this->x -= $this->cMargin;
12025                                                         } else {
12026                                                                 $this->x += $this->cMargin;
12027                                                         }
12028                                                 }
12029                                                 if ($strrest == $dom[$key]['value']) {
12030                                                         // used to avoid infinite loop
12031                                                         ++$loop;
12032                                                 } else {
12033                                                         $loop = 0;
12034                                                 }
12035                                                 $dom[$key]['value'] = ltrim($strrest);
12036                                                 if ($loop < 3) {
12037                                                         --$key;
12038                                                 }
12039                                         } else {
12040                                                 $loop = 0;
12041                                         }
12042                                 }
12043                                 ++$key;
12044                         } // end for each $key
12045                         // align the last line
12046                         if (isset($startlinex)) {
12047                                 $yshift = $minstartliney - $startliney;
12048                                 if (($yshift > 0) OR ($this->page > $startlinepage)) {
12049                                         $yshift = 0;
12050                                 }
12051                                 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
12052                                         // the last line must be shifted to be aligned as requested
12053                                         $linew = abs($this->endlinex - $startlinex);
12054                                         $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
12055                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12056                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12057                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
12058                                         } elseif (isset($opentagpos)) {
12059                                                 $midpos = $opentagpos;
12060                                         } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
12061                                                 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
12062                                                 $midpos = $this->footerpos[$startlinepage];
12063                                         } else {
12064                                                 $midpos = 0;
12065                                         }
12066                                         if ($midpos > 0) {
12067                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
12068                                                 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
12069                                         } else {
12070                                                 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
12071                                                 $pend = '';
12072                                         }       
12073                                         // calculate shifting amount
12074                                         $tw = $w;
12075                                         if ($this->lMargin != $prevlMargin) {
12076                                                 $tw += ($prevlMargin - $this->lMargin);
12077                                         }
12078                                         if ($this->rMargin != $prevrMargin) {
12079                                                 $tw += ($prevrMargin - $this->rMargin);
12080                                         }
12081                                         $mdiff = abs($tw - $linew);
12082                                         if ($plalign == 'C') {
12083                                                 if ($this->rtl) {
12084                                                         $t_x = -($mdiff / 2);
12085                                                 } else {
12086                                                         $t_x = ($mdiff / 2);
12087                                                 }
12088                                         } elseif (($plalign == 'R') AND (!$this->rtl)) {
12089                                                 // right alignment on LTR document
12090                                                 $t_x = $mdiff;
12091                                         } elseif (($plalign == 'L') AND ($this->rtl)) {
12092                                                 // left alignment on RTL document
12093                                                 $t_x = -$mdiff;
12094                                         } else {
12095                                                 $t_x = 0;
12096                                         }
12097                                         if (($t_x != 0) OR ($yshift < 0)) {
12098                                                 // shift the line
12099                                                 $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
12100                                                 $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
12101                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
12102                                                 // shift the annotations and links
12103                                                 if (isset($this->PageAnnots[$this->page])) {
12104                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
12105                                                                 if ($pak >= $pask) {
12106                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
12107                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
12108                                                                 }
12109                                                         }
12110                                                 }
12111                                                 $this->y -= $yshift;
12112                                         }
12113                                 }
12114                         }
12115                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
12116                                 $this->Ln($this->lasth);
12117                         }
12118                         // restore previous values
12119                         $this->setGraphicVars($gvars);
12120                         if ($this->page > $prevPage) {
12121                                 $this->lMargin = $this->pagedim[$this->page]['olm'];
12122                                 $this->rMargin = $this->pagedim[$this->page]['orm'];
12123                         }
12124                         unset($dom);
12125                 }
12126                 
12127                 /**
12128                  * Process opening tags.
12129                  * @param array $dom html dom array 
12130                  * @param int $key current element id
12131                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12132                  * @access protected
12133                  */
12134                 protected function openHTMLTagHandler(&$dom, $key, $cell=false) {
12135                         $tag = $dom[$key];
12136                         $parent = $dom[($dom[$key]['parent'])];
12137                         $firstorlast = ($key == 1);
12138                         // check for text direction attribute
12139                         if (isset($tag['attribute']['dir'])) {
12140                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
12141                         } else {
12142                                 $this->tmprtl = false;
12143                         }
12144                         //Opening tag
12145                         switch($tag['value']) {
12146                                 case 'table': {
12147                                         $cp = 0;
12148                                         $cs = 0;
12149                                         $dom[$key]['rowspans'] = array();
12150                                         if (!$this->empty_string($dom[$key]['thead'])) {
12151                                                 // set table header
12152                                                 $this->thead = $dom[$key]['thead'];
12153                                         }
12154                                         if (isset($tag['attribute']['cellpadding'])) {
12155                                                 $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
12156                                                 $this->oldcMargin = $this->cMargin;
12157                                                 $this->cMargin = $cp;
12158                                         }
12159                                         if (isset($tag['attribute']['cellspacing'])) {
12160                                                 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
12161                                         }
12162                                         $this->checkPageBreak((2 * $cp) + (2 * $cs) + $this->lasth);
12163                                         break;
12164                                 }
12165                                 case 'tr': {
12166                                         // array of columns positions
12167                                         $dom[$key]['cellpos'] = array();
12168                                         break;
12169                                 }
12170                                 case 'hr': {
12171                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12172                                         $this->htmlvspace = 0;
12173                                         $wtmp = $this->w - $this->lMargin - $this->rMargin;
12174                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
12175                                                 $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
12176                                         } else {
12177                                                 $hrWidth = $wtmp;
12178                                         }
12179                                         $x = $this->GetX();
12180                                         $y = $this->GetY();
12181                                         $prevlinewidth = $this->GetLineWidth();
12182                                         $this->Line($x, $y, $x + $hrWidth, $y);
12183                                         $this->SetLineWidth($prevlinewidth);
12184                                         $this->addHTMLVertSpace(1, $cell, '', !isset($dom[($key + 1)]), $tag['value'], false);
12185                                         break;
12186                                 }
12187                                 case 'a': {
12188                                         if (array_key_exists('href', $tag['attribute'])) {
12189                                                 $this->HREF['url'] = $tag['attribute']['href'];
12190                                         }
12191                                         $this->HREF['color'] = $this->htmlLinkColorArray;
12192                                         $this->HREF['style'] = $this->htmlLinkFontStyle;
12193                                         if (array_key_exists('style', $tag['attribute'])) {
12194                                                 // get style attributes
12195                                                 preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
12196                                                 $astyle = array();
12197                                                 while (list($id, $name) = each($style_array[1])) {
12198                                                         $name = strtolower($name);
12199                                                         $astyle[$name] = trim($style_array[2][$id]);
12200                                                 }
12201                                                 if (isset($astyle['color'])) {
12202                                                         $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
12203                                                 }
12204                                                 if (isset($astyle['text-decoration'])) {
12205                                                         $this->HREF['style'] = '';
12206                                                         $decors = explode(' ', strtolower($astyle['text-decoration']));
12207                                                         foreach ($decors as $dec) {
12208                                                                 $dec = trim($dec);
12209                                                                 if (!$this->empty_string($dec)) {
12210                                                                         if ($dec{0} == 'u') {
12211                                                                                 $this->HREF['style'] .= 'U';
12212                                                                         } elseif ($dec{0} == 'l') {
12213                                                                                 $this->HREF['style'] .= 'D';
12214                                                                         }
12215                                                                 }
12216                                                         }
12217                                                 }
12218                                         }               
12219                                         break;
12220                                 }
12221                                 case 'img': {
12222                                         if (isset($tag['attribute']['src'])) {
12223                                                 // replace relative path with real server path
12224                                                 if ($tag['attribute']['src'][0] == '/') {
12225                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
12226                                                 }
12227                                                 $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
12228                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
12229                                                 if (!isset($tag['attribute']['width'])) {
12230                                                         $tag['attribute']['width'] = 0;
12231                                                 }
12232                                                 if (!isset($tag['attribute']['height'])) {
12233                                                         $tag['attribute']['height'] = 0;
12234                                                 }
12235                                                 //if (!isset($tag['attribute']['align'])) {
12236                                                         // the only alignment supported is "bottom"
12237                                                         // further development is required for other modes.
12238                                                         $tag['attribute']['align'] = 'bottom';
12239                                                 //} 
12240                                                 switch($tag['attribute']['align']) {
12241                                                         case 'top': {
12242                                                                 $align = 'T';
12243                                                                 break;
12244                                                         }
12245                                                         case 'middle': {
12246                                                                 $align = 'M';
12247                                                                 break;
12248                                                         }
12249                                                         case 'bottom': {
12250                                                                 $align = 'B';
12251                                                                 break;
12252                                                         }
12253                                                         default: {
12254                                                                 $align = 'B';
12255                                                                 break;
12256                                                         }
12257                                                 }
12258                                                 $fileinfo = pathinfo($tag['attribute']['src']);
12259                                                 if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
12260                                                         $type = strtolower($fileinfo['extension']);
12261                                                 }
12262                                                 $prevy = $this->y;
12263                                                 $xpos = $this->GetX();
12264                                                 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == ' ')) {
12265                                                         if ($this->rtl) {
12266                                                                 $xpos += $this->GetStringWidth(' ');
12267                                                         } else {
12268                                                                 $xpos -= $this->GetStringWidth(' ');
12269                                                         }
12270                                                 }
12271                                                 $imglink = '';
12272                                                 if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
12273                                                         $imglink = $this->HREF['url'];
12274                                                         if ($imglink{0} == '#') {
12275                                                                 // convert url to internal link
12276                                                                 $page = intval(substr($imglink, 1));
12277                                                                 $imglink = $this->AddLink();
12278                                                                 $this->SetLink($imglink, 0, $page);
12279                                                         }
12280                                                 }
12281                                                 $border = 0;
12282                                                 if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
12283                                                         // currently only support 1 (frame) or a combination of 'LTRB'
12284                                                         $border = $tag['attribute']['border'];
12285                                                 }
12286                                                 $iw = '';
12287                                                 if (isset($tag['attribute']['width'])) {
12288                                                         $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
12289                                                 }
12290                                                 $ih = '';
12291                                                 if (isset($tag['attribute']['height'])) {
12292                                                         $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
12293                                                 }
12294                                                 if (($type == 'eps') OR ($type == 'ai')) {
12295                                                         $this->ImageEps($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, $imglink, true, $align, '', $border);
12296                                                 } else {
12297                                                         $this->Image($tag['attribute']['src'], $xpos, $this->GetY(), $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border);
12298                                                 }
12299                                                 switch($align) {
12300                                                         case 'T': {
12301                                                                 $this->y = $prevy;
12302                                                                 break;
12303                                                         }
12304                                                         case 'M': {
12305                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
12306                                                                 break;
12307                                                         }
12308                                                         case 'B': {
12309                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
12310                                                                 break;
12311                                                         }
12312                                                 }
12313                                         }
12314                                         break;
12315                                 }
12316                                 case 'dl': {
12317                                         ++$this->listnum;
12318                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12319                                         break;
12320                                 }
12321                                 case 'dt': {
12322                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12323                                         break;
12324                                 }
12325                                 case 'dd': {
12326                                         if ($this->rtl) {
12327                                                 $this->rMargin += $this->listindent;
12328                                         } else {
12329                                                 $this->lMargin += $this->listindent;
12330                                         }
12331                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12332                                         break;
12333                                 }
12334                                 case 'ul':
12335                                 case 'ol': {
12336                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12337                                         $this->htmlvspace = 0;
12338                                         ++$this->listnum;
12339                                         if ($tag['value'] == 'ol') {
12340                                                 $this->listordered[$this->listnum] = true;
12341                                         } else {
12342                                                 $this->listordered[$this->listnum] = false;
12343                                         }
12344                                         if (isset($tag['attribute']['start'])) {
12345                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
12346                                         } else {
12347                                                 $this->listcount[$this->listnum] = 0;
12348                                         }
12349                                         if ($this->rtl) {
12350                                                 $this->rMargin += $this->listindent;
12351                                         } else {
12352                                                 $this->lMargin += $this->listindent;
12353                                         }
12354                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], false);
12355                                         $this->htmlvspace = 0;
12356                                         break;
12357                                 }
12358                                 case 'li': {
12359                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12360                                         if ($this->listordered[$this->listnum]) {
12361                                                 // ordered item
12362                                                 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12363                                                         $this->lispacer = $parent['attribute']['type'];
12364                                                 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12365                                                         $this->lispacer = $parent['listtype'];
12366                                                 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12367                                                         $this->lispacer = $this->lisymbol;
12368                                                 } else {
12369                                                         $this->lispacer = '#';
12370                                                 }
12371                                                 ++$this->listcount[$this->listnum];
12372                                                 if (isset($tag['attribute']['value'])) {
12373                                                         $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
12374                                                 }
12375                                         } else {
12376                                                 // unordered item
12377                                                 if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
12378                                                         $this->lispacer = $parent['attribute']['type'];
12379                                                 } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
12380                                                         $this->lispacer = $parent['listtype'];
12381                                                 } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
12382                                                         $this->lispacer = $this->lisymbol;
12383                                                 } else {
12384                                                         $this->lispacer = '!';
12385                                                 }
12386                                         }
12387                                         break;
12388                                 }
12389                                 case 'blockquote': {
12390                                         if ($this->rtl) {
12391                                                 $this->rMargin += $this->listindent;
12392                                         } else {
12393                                                 $this->lMargin += $this->listindent;
12394                                         }
12395                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12396                                         break;
12397                                 }
12398                                 case 'br': {
12399                                         $this->Ln('', $cell);
12400                                         break;
12401                                 }
12402                                 case 'div': {
12403                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12404                                         break;
12405                                 }
12406                                 case 'p': {
12407                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], false);
12408                                         break;
12409                                 }
12410                                 case 'pre': {
12411                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], false);
12412                                         $this->premode = true;
12413                                         break;
12414                                 }
12415                                 case 'sup': {
12416                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
12417                                         break;
12418                                 }
12419                                 case 'sub': {
12420                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
12421                                         break;
12422                                 }
12423                                 case 'h1': 
12424                                 case 'h2': 
12425                                 case 'h3': 
12426                                 case 'h4': 
12427                                 case 'h5': 
12428                                 case 'h6': {
12429                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], false);
12430                                         break;
12431                                 }
12432                                 case 'tcpdf': {
12433                                         if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
12434                                                  // Special tag used to call TCPDF methods
12435                         if (isset($tag['attribute']['method'])) {
12436                             $tcpdf_method = $tag['attribute']['method'];
12437                             if (method_exists($this, $tcpdf_method)) {
12438                                 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
12439                                     $params = unserialize(urldecode($tag['attribute']['params']));
12440                                     call_user_func_array(array($this, $tcpdf_method), $params);
12441                                 } else {
12442                                     $this->$tcpdf_method();
12443                                 }
12444                                 $this->newline = true;
12445                             }
12446                         }
12447                     }
12448                                 }
12449                                 default: {
12450                                         break;
12451                                 }
12452                         }
12453                 }
12454                 
12455                 /**
12456                  * Process closing tags.
12457                  * @param array $dom html dom array 
12458                  * @param int $key current element id
12459                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12460                  * @access protected
12461                  */
12462                 protected function closeHTMLTagHandler(&$dom, $key, $cell=false) {
12463                         $tag = $dom[$key];
12464                         $parent = $dom[($dom[$key]['parent'])];
12465                         $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
12466                         //Closing tag
12467                         switch($tag['value']) {
12468                                 case 'tr': {
12469                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
12470                                         if(!isset($parent['endy'])) {
12471                                                 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
12472                                                 $parent['endy'] = $this->y;
12473                                         }
12474                                         if(!isset($parent['endpage'])) {
12475                                                 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
12476                                                 $parent['endpage'] = $this->page;
12477                                         }
12478                                         // update row-spanned cells
12479                                         if (isset($dom[$table_el]['rowspans'])) {
12480                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12481                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
12482                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12483                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
12484                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
12485                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
12486                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12487                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12488                                                                 }
12489                                                         }
12490                                                 }
12491                                                 // report new endy and endpage to the rowspanned cells
12492                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12493                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12494                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
12495                                                                 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
12496                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
12497                                                                 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
12498                                                         }
12499                                                 }
12500                                                 // update remaining rowspanned cells
12501                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
12502                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
12503                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
12504                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
12505                                                         }
12506                                                 }
12507                                         }
12508                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
12509                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];                                        
12510                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
12511                                                 $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
12512                                                 $this->y += $cellspacing;
12513                                         }                               
12514                                         $this->Ln(0, $cell);
12515                                         $this->x = $parent['startx'];
12516                                         // account for booklet mode
12517                                         if ($this->page > $parent['startpage']) {
12518                                                 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
12519                                                         $this->x += ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
12520                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
12521                                                         $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
12522                                                 }
12523                                         }
12524                                         break;
12525                                 }
12526                                 case 'table': {
12527                                         // draw borders
12528                                         $table_el = $parent;
12529                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) 
12530                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
12531                                                         $border = 1;
12532                                         } else {
12533                                                 $border = 0;
12534                                         }
12535                                         // fix bottom line alignment of last line before page break
12536                                         foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
12537                                                 // update row-spanned cells
12538                                                 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12539                                                         foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12540                                                                 if ($trwsp['trid'] == $trkey) {
12541                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
12542                                                                 }
12543                                                                 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
12544                                                                         $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
12545                                                                 }
12546                                                         }
12547                                                 }
12548                                                 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
12549                                                         $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
12550                                                         $dom[$prevtrkey]['endy'] = $pgendy;
12551                                                         // update row-spanned cells
12552                                                         if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
12553                                                                 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
12554                                                                         if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] == 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
12555                                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
12556                                                                                 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
12557                                                                         }
12558                                                                 }
12559                                                         }
12560                                                 }
12561                                                 $prevtrkey = $trkey;
12562                                                 $table_el = $dom[($dom[$key]['parent'])];
12563                                         }
12564                                         // for each row
12565                                         foreach ($table_el['trids'] as $j => $trkey) {
12566                                                 $parent = $dom[$trkey];
12567                                                 // for each cell on the row
12568                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
12569                                                         if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
12570                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
12571                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
12572                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
12573                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
12574                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
12575                                                         } else {
12576                                                                 $endy = $parent['endy'];
12577                                                                 $startpage = $parent['startpage'];
12578                                                                 $endpage = $parent['endpage'];
12579                                                         }
12580                                                         if ($endpage > $startpage) {
12581                                                                 // design borders around HTML cells.
12582                                                                 for ($page=$startpage; $page <= $endpage; ++$page) {
12583                                                                         $this->setPage($page);
12584                                                                         if ($page == $startpage) {
12585                                                                                 $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
12586                                                                                 $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
12587                                                                                 $cborder = $this->getBorderMode($border, $position='start');
12588                                                                         } elseif ($page == $endpage) {
12589                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of last page
12590                                                                                 $ch = $endy - $this->tMargin;
12591                                                                                 $cborder = $this->getBorderMode($border, $position='end');
12592                                                                         } else {
12593                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of the current page
12594                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
12595                                                                                 $cborder = $this->getBorderMode($border, $position='middle');
12596                                                                         }
12597                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12598                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
12599                                                                                 $fill = true;
12600                                                                         } else {
12601                                                                                 $fill = false;
12602                                                                         }
12603                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
12604                                                                         $this->x = $cellpos['startx'];
12605                                                                         // account for margin changes
12606                                                                         if ($page > $startpage) {
12607                                                                                 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
12608                                                                                         $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
12609                                                                                 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
12610                                                                                         $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
12611                                                                                 }
12612                                                                         }
12613                                                                         // design a cell around the text
12614                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
12615                                                                         if ($cborder OR $fill) {
12616                                                                                 $pagebuff = $this->getPageBuffer($this->page);
12617                                                                                 $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
12618                                                                                 $pend = substr($pagebuff, $this->intmrk[$this->page]);
12619                                                                                 $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12620                                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
12621                                                                         }
12622                                                                 }
12623                                                         } else {
12624                                                                 $this->setPage($startpage);
12625                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
12626                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
12627                                                                         $fill = true;
12628                                                                 } else {
12629                                                                         $fill = false;
12630                                                                 }
12631                                                                 $this->x = $cellpos['startx'];
12632                                                                 $this->y = $parent['starty'];
12633                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
12634                                                                 $ch = $endy - $parent['starty'];
12635                                                                 // design a cell around the text
12636                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
12637                                                                 if ($border OR $fill) {
12638                                                                         if (end($this->transfmrk[$this->page]) !== false) {
12639                                                                                 $pagemarkkey = key($this->transfmrk[$this->page]);
12640                                                                                 $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
12641                                                                         } elseif ($this->InFooter) {
12642                                                                                 $pagemark = &$this->footerpos[$this->page];
12643                                                                         } else {
12644                                                                                 $pagemark = &$this->intmrk[$this->page];
12645                                                                         }
12646                                                                         $pagebuff = $this->getPageBuffer($this->page);
12647                                                                         $pstart = substr($pagebuff, 0, $pagemark);
12648                                                                         $pend = substr($pagebuff, $pagemark);
12649                                                                         $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
12650                                                                         $pagemark += strlen($ccode."\n");
12651                                                                 }                                       
12652                                                         }
12653                                                 }                                       
12654                                                 if (isset($table_el['attribute']['cellspacing'])) {
12655                                                         $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
12656                                                         $this->y += $cellspacing;
12657                                                 }                               
12658                                                 $this->Ln(0, $cell);
12659                                                 $this->x = $parent['startx'];
12660                                                 if ($endpage > $startpage) {
12661                                                         if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
12662                                                                 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
12663                                                         } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
12664                                                                 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
12665                                                         }
12666                                                 }
12667                                         }
12668                                         if (isset($parent['cellpadding'])) {
12669                                                 $this->cMargin = $this->oldcMargin;
12670                                         }
12671                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
12672                                         if (!$this->empty_string($this->theadMargin)) {
12673                                                 // restore top margin
12674                                                 $this->tMargin = $this->theadMargin;
12675                                                 $this->pagedim[$this->page]['tm'] = $this->theadMargin;
12676                                         }
12677                                         // reset table header
12678                                         $this->thead = '';
12679                                         $this->theadMargin = '';
12680                                         break;
12681                                 }
12682                                 case 'a': {
12683                                         $this->HREF = '';
12684                                         break;
12685                                 }
12686                                 case 'sup': {
12687                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
12688                                         break;
12689                                 }
12690                                 case 'sub': {
12691                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
12692                                         break;
12693                                 }
12694                                 case 'div': {
12695                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12696                                         break;
12697                                 }
12698                                 case 'blockquote': {
12699                                         if ($this->rtl) {
12700                                                 $this->rMargin -= $this->listindent;
12701                                         } else {
12702                                                 $this->lMargin -= $this->listindent;
12703                                         }
12704                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12705                                         break;
12706                                 }
12707                                 case 'p': {
12708                                         $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12709                                         break;
12710                                 }
12711                                 case 'pre': {
12712                                         $this->addHTMLVertSpace(1, $cell, '', $firstorlast, $tag['value'], true);
12713                                         $this->premode = false;
12714                                         break;
12715                                 }
12716                                 case 'dl': {
12717                                         --$this->listnum;
12718                                         if ($this->listnum <= 0) {
12719                                                 $this->listnum = 0;
12720                                                 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12721                                         }
12722                                         break;
12723                                 }
12724                                 case 'dt': {
12725                                         $this->lispacer = '';
12726                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12727                                         break;
12728                                 }
12729                                 case 'dd': {
12730                                         $this->lispacer = '';
12731                                         if ($this->rtl) {
12732                                                 $this->rMargin -= $this->listindent;
12733                                         } else {
12734                                                 $this->lMargin -= $this->listindent;
12735                                         }
12736                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12737                                         break;
12738                                 }
12739                                 case 'ul':
12740                                 case 'ol': {
12741                                         --$this->listnum;
12742                                         $this->lispacer = '';
12743                                         if ($this->rtl) {
12744                                                 $this->rMargin -= $this->listindent;
12745                                         } else {
12746                                                 $this->lMargin -= $this->listindent;
12747                                         }
12748                                         if ($this->listnum <= 0) {
12749                                                 $this->listnum = 0;
12750                                                 $this->addHTMLVertSpace(2, $cell, '', $firstorlast, $tag['value'], true);
12751                                         }
12752                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
12753                                         break;
12754                                 }
12755                                 case 'li': {
12756                                         $this->lispacer = '';
12757                                         $this->addHTMLVertSpace(0, $cell, '', $firstorlast, $tag['value'], true);
12758                                         break;
12759                                 }
12760                                 case 'h1': 
12761                                 case 'h2': 
12762                                 case 'h3': 
12763                                 case 'h4': 
12764                                 case 'h5': 
12765                                 case 'h6': {
12766                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k, $firstorlast, $tag['value'], true);
12767                                         break;
12768                                 }
12769                                 default : {
12770                                         break;
12771                                 }
12772                         }
12773                         $this->tmprtl = false;
12774                 }
12775                 
12776                 /**
12777                  * Add vertical spaces if needed.
12778                  * @param int $n number of spaces to add
12779                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
12780                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
12781                  * @param boolean $firstorlast if true do not print additional empty lines.
12782                  * @param string $tag HTML tag to which this space will be applied
12783                  * @param boolean $closing true if this space will be applied to a closing tag, false otherwise
12784                  * @access protected
12785                  */
12786                 protected function addHTMLVertSpace($n, $cell=false, $h='', $firstorlast=false, $tag='', $closing=false) {
12787                         if ($firstorlast) {
12788                                 $this->Ln(0, $cell);
12789                                 $this->htmlvspace = 0;
12790                                 return;
12791                         }
12792                         if (isset($this->tagvspaces[$tag][intval($closing)]['n'])) {
12793                                 $n = $this->tagvspaces[$tag][intval($closing)]['n'];
12794                         }
12795                         if (isset($this->tagvspaces[$tag][intval($closing)]['h'])) {
12796                                 $h = $this->tagvspaces[$tag][intval($closing)]['h'];
12797                         }
12798                         if (is_string($h)) {
12799                                 $vsize = $n * $this->lasth;
12800                         } else {
12801                                 $vsize = $n * $h;
12802                         }
12803                         if ($vsize > $this->htmlvspace) {
12804                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
12805                                 $this->htmlvspace = $vsize;
12806                         }
12807                 }
12808                 
12809                 /**
12810                  * Set the default bullet to be used as LI bullet symbol
12811                  * @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')
12812                  * @access public
12813                  * @since 4.0.028 (2008-09-26)
12814                  */
12815                 public function setLIsymbol($symbol='!') {
12816                         $symbol = strtolower($symbol);
12817                         switch ($symbol) {
12818                                 case '!' :
12819                                 case '#' :
12820                                 case 'disc' :
12821                                 case 'disc' :
12822                                 case 'circle' :
12823                                 case 'square' :
12824                                 case '1':
12825                                 case 'decimal':
12826                                 case 'decimal-leading-zero':
12827                                 case 'i':
12828                                 case 'lower-roman':
12829                                 case 'I':
12830                                 case 'upper-roman':
12831                                 case 'a':
12832                                 case 'lower-alpha':
12833                                 case 'lower-latin':
12834                                 case 'A':
12835                                 case 'upper-alpha':
12836                                 case 'upper-latin':
12837                                 case 'lower-greek': {
12838                                         $this->lisymbol = $symbol;
12839                                         break;
12840                                 }
12841                                 default : {
12842                                         $this->lisymbol = '';
12843                                 }
12844                         }
12845                 }
12846                 
12847                 /**
12848                 * Set the booklet mode for double-sided pages.
12849                 * @param boolean $booklet true set the booklet mode on, fals eotherwise.
12850                 * @param float $inner Inner page margin.
12851                 * @param float $outer Outer page margin.
12852                 * @access public
12853                 * @since 4.2.000 (2008-10-29)
12854                 */
12855                 public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
12856                         $this->booklet = $booklet;
12857                         if ($inner >= 0) {
12858                                 $this->lMargin = $inner;
12859                         }
12860                         if ($outer >= 0) {
12861                                 $this->rMargin = $outer;
12862                         }
12863                 }
12864                 
12865                 /**
12866                 * Swap the left and right margins.
12867                 * @param boolean $reverse if true swap left and right margins.
12868                 * @access protected
12869                 * @since 4.2.000 (2008-10-29)
12870                 */
12871                 protected function swapMargins($reverse=true) {
12872                         if ($reverse) {
12873                                 // swap left and right margins
12874                                 $mtemp = $this->original_lMargin;
12875                                 $this->original_lMargin = $this->original_rMargin;
12876                                 $this->original_rMargin = $mtemp;
12877                                 $deltam = $this->original_lMargin - $this->original_rMargin;
12878                                 $this->lMargin += $deltam;
12879                                 $this->rMargin -= $deltam;
12880                         }
12881                 }
12882
12883                 /**
12884                 * Set the vertical spaces for HTML tags.
12885                 * The array must have the following structure (example):
12886                 * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
12887                 * The first array level contains the tag names,
12888                 * the second level contains 0 for opening tags or 1 for closing tags,
12889                 * the third level contains the vertical space unit (h) and the number spaces to add (n).
12890                 * If the h parameter is not specified, default values are used.
12891                 * @param array $tagvs array of tags and relative vertical spaces.
12892                 * @access public
12893                 * @since 4.2.001 (2008-10-30)
12894                 */
12895                 public function setHtmlVSpace($tagvs) {
12896                         $this->tagvspaces = $tagvs;
12897                 }
12898
12899         /**
12900                 * Set custom width for list indentation.
12901                 * @param float $width width of the indentation. Use negative value to disable it.
12902                 * @access public
12903                 * @since 4.2.007 (2008-11-12)
12904                 */
12905                 public function setListIndentWidth($width) {
12906                         return $this->customlistindent = floatval($width);
12907         }
12908
12909         /**
12910                 * Set the top/bottom cell sides to be open or closed when the cell cross the page.
12911                 * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page.
12912                 * @access public
12913                 * @since 4.2.010 (2008-11-14)
12914                 */
12915                 public function setOpenCell($isopen) {
12916                         $this->opencell = $isopen;
12917         }
12918
12919         /**
12920                 * Set the color and font style for HTML links.
12921                 * @param array $color RGB array of colors
12922                 * @param string $fontstyle additional font styles to add
12923                 * @access public
12924                 * @since 4.4.003 (2008-12-09)
12925                 */
12926                 public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
12927                         $this->htmlLinkColorArray = $color;
12928                         $this->htmlLinkFontStyle = $fontstyle;
12929         }
12930
12931         /**
12932                 * convert html string containing value and unit of measure to user's units or points.
12933                 * @param string $htmlval string containing values and unit
12934                 * @param string $refsize reference value in points
12935                 * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
12936                 * @param boolean $point if true returns points, otherwise returns value in user's units
12937                 * @return float value in user's unit or point if $points=true
12938                 * @access public
12939                 * @since 4.4.004 (2008-12-10)
12940                 */
12941         public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
12942                         $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
12943                         $retval = 0;
12944                         $value = 0;
12945                         $unit = 'px';
12946                         $k = $this->k;
12947                         if ($points) {
12948                                 $k = 1;
12949                         }
12950                         if (in_array($defaultunit, $supportedunits)) {
12951                                 $unit = $defaultunit;
12952                         }
12953                         if (is_numeric($htmlval)) {
12954                                 $value = floatval($htmlval);
12955                         } elseif (preg_match('/([0-9\.]+)/', $htmlval, $mnum)) {
12956                                 $value = floatval($mnum[1]);
12957                                 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
12958                                         if (in_array($munit[1], $supportedunits)) {
12959                                                 $unit = $munit[1];
12960                                         }
12961                                 }
12962                         }
12963                         switch ($unit) {
12964                                 // percentage
12965                                 case '%': {
12966                                         $retval = (($value * $refsize) / 100);
12967                                         break;
12968                                 }
12969                                 // relative-size
12970                                 case 'em': {
12971                                         $retval = ($value * $refsize);
12972                                         break;
12973                                 }
12974                                 case 'ex': {
12975                                         $retval = $value * ($refsize / 2);
12976                                         break;
12977                                 }
12978                                 // absolute-size
12979                                 case 'in': {
12980                                         $retval = ($value * $this->dpi) / $k;
12981                                         break;
12982                                 }
12983                                 case 'cm': {
12984                                         $retval = ($value / 2.54 * $this->dpi) / $k;
12985                                         break;
12986                                 }
12987                                 case 'mm': {
12988                                         $retval = ($value / 25.4 * $this->dpi) / $k;
12989                                         break;
12990                                 }
12991                                 case 'pc': {
12992                                         // one pica is 12 points
12993                                         $retval = ($value * 12) / $k;
12994                                         break;
12995                                 }
12996                                 case 'pt': {
12997                                         $retval = $value / $k;
12998                                         break;
12999                                 }
13000                                 case 'px': {
13001                                         $retval = $this->pixelsToUnits($value);
13002                                         break;
13003                                 }
13004                         }
13005                         return $retval;
13006                 }
13007
13008                 /**
13009                 * Returns the Roman representation of an integer number
13010                 * @param int number to convert
13011                 * @return string roman representation of the specified number
13012                 * @access public
13013                 * @since 4.4.004 (2008-12-10)
13014                 */
13015                 public function intToRoman($number) {
13016                         $roman = '';
13017                         while ($number >= 1000) {
13018                                 $roman .= 'M';
13019                                 $number -= 1000;
13020                         }
13021                         while ($number >= 900) {
13022                                 $roman .= 'CM';
13023                                 $number -= 900;
13024                         }
13025                         while ($number >= 500) {
13026                                 $roman .= 'D';
13027                                 $number -= 500;
13028                         }
13029                         while ($number >= 400) {
13030                                 $roman .= 'CD';
13031                                 $number -= 400;
13032                         }
13033                         while ($number >= 100) {
13034                                 $roman .= 'C';
13035                                 $number -= 100;
13036                         }
13037                         while ($number >= 90) {
13038                         $roman .= 'XC';
13039                         $number -= 90;
13040                         }
13041                         while ($number >= 50) {
13042                                 $roman .= 'L';
13043                                 $number -= 50;
13044                         }
13045                         while ($number >= 40) {
13046                                 $roman .= 'XL';
13047                                 $number -= 40;
13048                         }
13049                         while ($number >= 10) {
13050                         $roman .= 'X';
13051                         $number -= 10;
13052                         }
13053                         while ($number >= 9) {
13054                                 $roman .= 'IX';
13055                                 $number -= 9;
13056                         }
13057                         while ($number >= 5) {
13058                                 $roman .= 'V';
13059                                 $number -= 5;
13060                         }
13061                         while ($number >= 4) {
13062                         $roman .= 'IV';
13063                         $number -= 4;
13064                         }
13065                         while ($number >= 1) {
13066                                 $roman .= 'I';
13067                                 --$number;
13068                         }
13069                         return $roman;
13070                 }
13071
13072                 /**
13073                 * Output an HTML list bullet or ordered item symbol
13074                 * @param int $listdepth list nesting level
13075                 * @param string $listtype type of list
13076                 * @param float $size current font size
13077                 * @access protected
13078                 * @since 4.4.004 (2008-12-10)
13079                 */
13080                 protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
13081                     $size /= $this->k;
13082                     $fill = '';
13083                     $color = $this->fgcolor;
13084                     $width = 0;
13085                     $textitem = '';
13086                     $tmpx = $this->x;           
13087                         $lspace = $this->GetStringWidth('  ');
13088                         if ($listtype == '!') {
13089                                 // set default list type for unordered list
13090                                 $deftypes = array('disc', 'circle', 'square');
13091                                 $listtype = $deftypes[($listdepth - 1) % 3];
13092                         } elseif ($listtype == '#') {
13093                                 // set default list type for ordered list
13094                                 $listtype = 'decimal';
13095                         }
13096                 switch ($listtype) {
13097                         // unordered types
13098                                 case 'none': {
13099                                         break;
13100                                 }
13101                                 case 'disc': {
13102                                         $fill = 'F';
13103                                 }
13104                                 case 'circle': {
13105                                         $fill .= 'D';
13106                                         $r = $size / 6;
13107                                         $lspace += (2 * $r);
13108                                         if ($this->rtl) {
13109                                                 $this->x = $this->w - $this->x - $lspace;
13110                                         } else {
13111                                                 $this->x -= $lspace;
13112                                         }
13113                                         $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
13114                                         break;
13115                                 }
13116                                 case 'square': {
13117                                         $l = $size / 3;
13118                                         $lspace += $l;
13119                                         if ($this->rtl) {
13120                                                 $this->x = $this->w - $this->x - $lspace;
13121                                         } else {
13122                                                 $this->x -= $lspace;
13123                                         }
13124                                         $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
13125                                         break;
13126                                 }
13127                                 // ordered types
13128
13129                                 // $this->listcount[$this->listnum];
13130                                 // $textitem
13131                                 case '1':
13132                                 case 'decimal': {
13133                                         $textitem = $this->listcount[$this->listnum];
13134                                         break;
13135                                 }
13136                                 case 'decimal-leading-zero': {
13137                                         $textitem = sprintf("%02d", $this->listcount[$this->listnum]);
13138                                         break;
13139                                 }
13140                                 case 'i':
13141                                 case 'lower-roman': {
13142                                         $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
13143                                         break;
13144                                 }
13145                                 case 'I':
13146                                 case 'upper-roman': {
13147                                         $textitem = $this->intToRoman($this->listcount[$this->listnum]);
13148                                         break;
13149                                 }
13150                                 case 'a':
13151                                 case 'lower-alpha':
13152                                 case 'lower-latin': {
13153                                         $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
13154                                         break;
13155                                 }
13156                                 case 'A':
13157                                 case 'upper-alpha':
13158                                 case 'upper-latin': {
13159                                         $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
13160                                         break;
13161                                 }
13162                                 case 'lower-greek': {
13163                                         $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
13164                                         break;
13165                                 }
13166                                 /*
13167                                 // Types to be implemented (special handling)
13168                                 case 'hebrew': {
13169                                         break;
13170                                 }
13171                                 case 'armenian': {
13172                                         break;
13173                                 }
13174                                 case 'georgian': {
13175                                         break;
13176                                 }
13177                                 case 'cjk-ideographic': {
13178                                         break;
13179                                 }
13180                                 case 'hiragana': {
13181                                         break;
13182                                 }
13183                                 case 'katakana': {
13184                                         break;
13185                                 }
13186                                 case 'hiragana-iroha': {
13187                                         break;
13188                                 }
13189                                 case 'katakana-iroha': {
13190                                         break;
13191                                 }
13192                                 */
13193                                 default: {
13194                                         $textitem = $this->listcount[$this->listnum];
13195                                 }
13196                         }
13197                         if (!$this->empty_string($textitem)) {
13198                                 // print ordered item
13199                                 if ($this->rtl) {
13200                                         $textitem = '.'.$textitem;
13201                                 } else {
13202                                         $textitem = $textitem.'.';
13203                                 }
13204                                 $lspace += $this->GetStringWidth($textitem);
13205                                 if ($this->rtl) {
13206                                         $this->x += $lspace;
13207                                 } else {
13208                                         $this->x -= $lspace;
13209                                 }
13210                                 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
13211                         }
13212                         $this->x = $tmpx;
13213                         $this->lispacer = '';
13214                 }
13215
13216         /**
13217                 * Returns current graphic variables as array.
13218                 * @return array graphic variables
13219                 * @access protected
13220                 * @since 4.2.010 (2008-11-14)
13221                 */
13222                 protected function getGraphicVars() {
13223                         $grapvars = array(
13224                                 'FontFamily' => $this->FontFamily,
13225                                 'FontStyle' => $this->FontStyle,
13226                                 'FontSizePt' => $this->FontSizePt,
13227                                 'rMargin' => $this->rMargin,
13228                                 'lMargin' => $this->lMargin,
13229                                 'cMargin' => $this->cMargin,
13230                                 'LineWidth' => $this->LineWidth,
13231                                 'linestyleWidth' => $this->linestyleWidth,
13232                                 'linestyleCap' => $this->linestyleCap,
13233                                 'linestyleJoin' => $this->linestyleJoin,
13234                                 'linestyleDash' => $this->linestyleDash,
13235                                 'DrawColor' => $this->DrawColor,
13236                                 'FillColor' => $this->FillColor,
13237                                 'TextColor' => $this->TextColor,
13238                                 'ColorFlag' => $this->ColorFlag,
13239                                 'bgcolor' => $this->bgcolor,
13240                                 'fgcolor' => $this->fgcolor,
13241                                 'htmlvspace' => $this->htmlvspace,
13242                                 'lasth' => $this->lasth
13243                                 );
13244                         return $grapvars;
13245                 }
13246
13247         /**
13248                 * Set graphic variables.
13249                 * @param $gvars array graphic variables
13250                 * @access protected
13251                 * @since 4.2.010 (2008-11-14)
13252                 */
13253                 protected function setGraphicVars($gvars) {
13254                         $this->FontFamily = $gvars['FontFamily'];
13255                         $this->FontStyle = $gvars['FontStyle'];
13256                         $this->FontSizePt = $gvars['FontSizePt'];
13257                         $this->rMargin = $gvars['rMargin'];
13258                         $this->lMargin = $gvars['lMargin'];
13259                         $this->cMargin = $gvars['cMargin'];
13260                         $this->LineWidth = $gvars['LineWidth'];
13261                         $this->linestyleWidth = $gvars['linestyleWidth'];
13262                         $this->linestyleCap = $gvars['linestyleCap'];
13263                         $this->linestyleJoin = $gvars['linestyleJoin'];
13264                         $this->linestyleDash = $gvars['linestyleDash'];
13265                         $this->DrawColor = $gvars['DrawColor'];
13266                         $this->FillColor = $gvars['FillColor'];
13267                         $this->TextColor = $gvars['TextColor'];
13268                         $this->ColorFlag = $gvars['ColorFlag'];
13269                         $this->bgcolor = $gvars['bgcolor'];
13270                         $this->fgcolor = $gvars['fgcolor'];
13271                         $this->htmlvspace = $gvars['htmlvspace'];
13272                         //$this->lasth = $gvars['lasth'];
13273                         $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
13274                         if (!$this->empty_string($this->FontFamily)) {
13275                                 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
13276                         }
13277                 }
13278
13279                 /**
13280                 * Returns a temporary filename for caching object on filesystem.
13281                 * @param string $prefix prefix to add to filename
13282                 * return string filename.
13283                 * @access protected
13284                 * @since 4.5.000 (2008-12-31)
13285                 */
13286                 protected function getObjFilename($name) {
13287                         return tempnam(K_PATH_CACHE, $name.'_');
13288                 }
13289
13290         /**
13291                 * Writes data to a temporary file on filesystem.
13292                 * @param string $file file name
13293                 * @param mixed $data data to write on file
13294                 * @param boolean $append if true append data, false replace.
13295                 * @access protected
13296                 * @since 4.5.000 (2008-12-31)
13297                 */
13298                 protected function writeDiskCache($filename, $data, $append=false) {
13299                         if ($append) {
13300                                 $fmode = 'ab+';
13301                         } else {
13302                                 $fmode = 'wb+';
13303                         }
13304                         $f = @fopen($filename, $fmode);
13305                         if (!$f) {
13306                                 $this->Error('Unable to write cache file: '.$filename);
13307                         } else {
13308                                 fwrite($f, $data);
13309                                 fclose($f);
13310                         }
13311                         // update file lenght (needed for transactions)
13312                         if (!isset($this->cache_file_lenght['_'.$filename])) {
13313                                 $this->cache_file_lenght['_'.$filename] = strlen($data);
13314                         } else {
13315                                 $this->cache_file_lenght['_'.$filename] += strlen($data);
13316                         }
13317                 }
13318
13319         /**
13320                 * Read data from a temporary file on filesystem.
13321                 * @param string $file file name
13322                 * @return mixed retrieved data
13323                 * @access protected
13324                 * @since 4.5.000 (2008-12-31)
13325                 */
13326                 protected function readDiskCache($filename) {
13327                         return file_get_contents($filename);
13328                 }
13329
13330                 /**
13331                 * Set buffer content (always append data).
13332                 * @param string $data data
13333                 * @access protected
13334                 * @since 4.5.000 (2009-01-02)
13335                 */
13336                 protected function setBuffer($data) {
13337                         $this->bufferlen += strlen($data);
13338                         if ($this->diskcache) {
13339                                 if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
13340                                         $this->buffer = $this->getObjFilename('buffer');
13341                                 }
13342                                 $this->writeDiskCache($this->buffer, $data, true);
13343                         } else {
13344                                 $this->buffer .= $data;
13345                         }
13346                 }
13347
13348         /**
13349                 * Get buffer content.
13350                 * @return string buffer content
13351                 * @access protected
13352                 * @since 4.5.000 (2009-01-02)
13353                 */
13354                 protected function getBuffer() {
13355                         if ($this->diskcache) {
13356                                 return $this->readDiskCache($this->buffer);
13357                         } else {
13358                                 return $this->buffer;
13359                         }
13360                 }
13361
13362         /**
13363                 * Set page buffer content.
13364                 * @param int $page page number
13365                 * @param string $data page data
13366                 * @param boolean $append if true append data, false replace.
13367                 * @access protected
13368                 * @since 4.5.000 (2008-12-31)
13369                 */
13370                 protected function setPageBuffer($page, $data, $append=false) {
13371                         if ($this->diskcache) {
13372                                 if (!isset($this->pages[$page])) {
13373                                         $this->pages[$page] = $this->getObjFilename('page'.$page);
13374                                 }
13375                                 $this->writeDiskCache($this->pages[$page], $data, $append);
13376                         } else {
13377                                 if ($append) {
13378                                         $this->pages[$page] .= $data;
13379                                 } else {
13380                                         $this->pages[$page] = $data;
13381                                 }
13382                         }
13383                         if ($append AND isset($this->pagelen[$page])) {
13384                                 $this->pagelen[$page] += strlen($data);
13385                         } else {
13386                                 $this->pagelen[$page] = strlen($data);
13387                         }
13388                 }
13389
13390         /**
13391                 * Get page buffer content.
13392                 * @param int $page page number
13393                 * @return string page buffer content or false in case of error
13394                 * @access protected
13395                 * @since 4.5.000 (2008-12-31)
13396                 */
13397                 protected function getPageBuffer($page) {
13398                         if ($this->diskcache) {
13399                                 return $this->readDiskCache($this->pages[$page]);
13400                         } elseif (isset($this->pages[$page])) {
13401                                 return $this->pages[$page];
13402                         }
13403                         return false;
13404                 }
13405
13406         /**
13407                 * Set image buffer content.
13408                 * @param string $image image key
13409                 * @param array $data image data
13410                 * @access protected
13411                 * @since 4.5.000 (2008-12-31)
13412                 */
13413                 protected function setImageBuffer($image, $data) {
13414                         if ($this->diskcache) {
13415                                 if (!isset($this->images[$image])) {
13416                                         $this->images[$image] = $this->getObjFilename('image'.$image);
13417                                 }
13418                                 $this->writeDiskCache($this->images[$image], serialize($data));
13419                         } else {
13420                                 $this->images[$image] = $data;
13421                         }
13422                         if (!in_array($image, $this->imagekeys)) {
13423                                 $this->imagekeys[] = $image;
13424                         }
13425                         ++$this->numimages;
13426                 }
13427
13428         /**
13429                 * Set image buffer content.
13430                 * @param string $image image key
13431                 * @param string $key image sub-key
13432                 * @param array $data image data
13433                 * @access protected
13434                 * @since 4.5.000 (2008-12-31)
13435                 */
13436                 protected function setImageSubBuffer($image, $key, $data) {
13437                         if (!isset($this->images[$image])) {
13438                                 $this->setImageBuffer($image, array());
13439                         }
13440                         if ($this->diskcache) {
13441                                 $tmpimg = $this->getImageBuffer($image);
13442                                 $tmpimg[$key] = $data;
13443                                 $this->writeDiskCache($this->images[$image], serialize($tmpimg));
13444                         } else {
13445                                 $this->images[$image][$key] = $data;
13446                         }
13447                 }
13448
13449         /**
13450                 * Get page buffer content.
13451                 * @param string $image image key
13452                 * @return string image buffer content or false in case of error
13453                 * @access protected
13454                 * @since 4.5.000 (2008-12-31)
13455                 */
13456                 protected function getImageBuffer($image) {
13457                         if ($this->diskcache AND isset($this->images[$image])) {
13458                                 return unserialize($this->readDiskCache($this->images[$image]));
13459                         } elseif (isset($this->images[$image])) {
13460                                 return $this->images[$image];
13461                         }
13462                         return false;
13463                 }
13464
13465                 /**
13466                 * Set font buffer content.
13467                 * @param string $font font key
13468                 * @param array $data font data
13469                 * @access protected
13470                 * @since 4.5.000 (2009-01-02)
13471                 */
13472                 protected function setFontBuffer($font, $data) {
13473                         if ($this->diskcache) {
13474                                 if (!isset($this->fonts[$font])) {
13475                                         $this->fonts[$font] = $this->getObjFilename('font');
13476                                 }
13477                                 $this->writeDiskCache($this->fonts[$font], serialize($data));
13478                         } else {
13479                                 $this->fonts[$font] = $data;
13480                         }
13481                         if (!in_array($font, $this->fontkeys)) {
13482                                 $this->fontkeys[] = $font;
13483                         }
13484                 }
13485
13486         /**
13487                 * Set font buffer content.
13488                 * @param string $font font key
13489                 * @param string $key font sub-key
13490                 * @param array $data font data
13491                 * @access protected
13492                 * @since 4.5.000 (2009-01-02)
13493                 */
13494                 protected function setFontSubBuffer($font, $key, $data) {
13495                         if (!isset($this->fonts[$font])) {
13496                                 $this->setFontBuffer($font, array());
13497                         }
13498                         if ($this->diskcache) {
13499                                 $tmpfont = $this->getFontBuffer($font);
13500                                 $tmpfont[$key] = $data;
13501                                 $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
13502                         } else {
13503                                 $this->fonts[$font][$key] = $data;
13504                         }
13505                 }
13506
13507         /**
13508                 * Get font buffer content.
13509                 * @param string $font font key
13510                 * @return string font buffer content or false in case of error
13511                 * @access protected
13512                 * @since 4.5.000 (2009-01-02)
13513                 */
13514                 protected function getFontBuffer($font) {
13515                         if ($this->diskcache AND isset($this->fonts[$font])) {
13516                                 return unserialize($this->readDiskCache($this->fonts[$font]));
13517                         } elseif (isset($this->fonts[$font])) {
13518                                 return $this->fonts[$font];
13519                         }
13520                         return false;
13521                 }
13522
13523         /**
13524                 * Move a page to a previous position.
13525                 * @param int $frompage number of the source page
13526                 * @param int $topage number of the destination page (must be less than $frompage)
13527                 * @return true in case of success, false in case of error.
13528                 * @access public
13529                 * @since 4.5.000 (2009-01-02)
13530                 */
13531                 public function movePage($frompage, $topage) {
13532                         if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
13533                                 return false;
13534                         }
13535                         if ($frompage == $this->page) {
13536                                 // close the page before moving it
13537                                 $this->endPage();
13538                         }
13539                         // move all page-related states
13540                         $tmppage = $this->pages[$frompage];
13541                         $tmppagedim = $this->pagedim[$frompage];
13542                         $tmppagelen = $this->pagelen[$frompage];
13543                         $tmpintmrk = $this->intmrk[$frompage];
13544                         if (isset($this->footerpos[$frompage])) {
13545                                 $tmpfooterpos = $this->footerpos[$frompage];
13546                         }
13547                         if (isset($this->footerlen[$frompage])) {
13548                                 $tmpfooterlen = $this->footerlen[$frompage];
13549                         }
13550                         if (isset($this->transfmrk[$frompage])) {
13551                                 $tmptransfmrk = $this->transfmrk[$frompage];
13552                         }
13553                         if (isset($this->PageAnnots[$frompage])) {
13554                                 $tmpannots = $this->PageAnnots[$frompage];
13555                         }
13556                         if (isset($this->newpagegroup[$frompage])) {
13557                                 $tmpnewpagegroup = $this->newpagegroup[$frompage];
13558                         }
13559                         for ($i = $frompage; $i > $topage; --$i) {
13560                                 $j = $i - 1;
13561                                 // shift pages down
13562                                 $this->pages[$i] = $this->pages[$j];
13563                                 $this->pagedim[$i] = $this->pagedim[$j];
13564                                 $this->pagelen[$i] = $this->pagelen[$j];
13565                                 $this->intmrk[$i] = $this->intmrk[$j];
13566                                 if (isset($this->footerpos[$j])) {
13567                                         $this->footerpos[$i] = $this->footerpos[$j];
13568                                 } elseif (isset($this->footerpos[$i])) {
13569                                         unset($this->footerpos[$i]);
13570                                 }
13571                                 if (isset($this->footerlen[$j])) {
13572                                         $this->footerlen[$i] = $this->footerlen[$j];
13573                                 } elseif (isset($this->footerlen[$i])) {
13574                                         unset($this->footerlen[$i]);
13575                                 }
13576                                 if (isset($this->transfmrk[$j])) {
13577                                         $this->transfmrk[$i] = $this->transfmrk[$j];
13578                                 } elseif (isset($this->transfmrk[$i])) {
13579                                         unset($this->transfmrk[$i]);
13580                                 }
13581                                 if (isset($this->PageAnnots[$j])) {
13582                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
13583                                 } elseif (isset($this->PageAnnots[$i])) {
13584                                         unset($this->PageAnnots[$i]);
13585                                 }
13586                                 if (isset($this->newpagegroup[$j])) {
13587                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
13588                                 } elseif (isset($this->newpagegroup[$i])) {
13589                                         unset($this->newpagegroup[$i]);
13590                                 }
13591                         }
13592                         $this->pages[$topage] = $tmppage;
13593                         $this->pagedim[$topage] = $tmppagedim;
13594                         $this->pagelen[$topage] = $tmppagelen;
13595                         $this->intmrk[$topage] = $tmpintmrk;
13596                         if (isset($tmpfooterpos)) {
13597                                 $this->footerpos[$topage] = $tmpfooterpos;
13598                         } elseif (isset($this->footerpos[$topage])) {
13599                                 unset($this->footerpos[$topage]);
13600                         }
13601                         if (isset($tmpfooterlen)) {
13602                                 $this->footerlen[$topage] = $tmpfooterlen;
13603                         } elseif (isset($this->footerlen[$topage])) {
13604                                 unset($this->footerlen[$topage]);
13605                         }
13606                         if (isset($tmptransfmrk)) {
13607                                 $this->transfmrk[$topage] = $tmptransfmrk;
13608                         } elseif (isset($this->transfmrk[$topage])) {
13609                                 unset($this->transfmrk[$topage]);
13610                         }
13611                         if (isset($tmpannots)) {
13612                                 $this->PageAnnots[$topage] = $tmpannots;
13613                         } elseif (isset($this->PageAnnots[$topage])) {
13614                                 unset($this->PageAnnots[$topage]);
13615                         }
13616                         if (isset($tmpnewpagegroup)) {
13617                                 $this->newpagegroup[$topage] = $tmpnewpagegroup;
13618                         } elseif (isset($this->newpagegroup[$topage])) {
13619                                 unset($this->newpagegroup[$topage]);
13620                         }
13621                         // adjust outlines
13622                         $tmpoutlines = $this->outlines;
13623                         foreach ($tmpoutlines as $key => $outline) {
13624                                 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
13625                                         $this->outlines[$key]['p'] = $outline['p'] + 1;
13626                                 } elseif ($outline['p'] == $frompage) {
13627                                         $this->outlines[$key]['p'] = $topage;
13628                                 }
13629                         }
13630                         // adjust links
13631                         $tmplinks = $this->links;
13632                         foreach ($tmplinks as $key => $link) {
13633                                 if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
13634                                         $this->links[$key][0] = $link[0] + 1;
13635                                 } elseif ($link[0] == $frompage) {
13636                                         $this->links[$key][0] = $topage;
13637                                 }
13638                         }
13639                         // adjust javascript
13640                         $tmpjavascript = $this->javascript;
13641                         global $jfrompage, $jtopage;
13642                         $jfrompage = $frompage;
13643                         $jtopage = $topage;
13644                         $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13645                                 create_function('$matches', 'global $jfrompage, $jtopage;
13646                                 $pagenum = intval($matches[3]) + 1;
13647                                 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
13648                                         $newpage = ($pagenum + 1);
13649                                 } elseif ($pagenum == $jfrompage) {
13650                                         $newpage = $jtopage;
13651                                 } else {
13652                                         $newpage = $pagenum;
13653                                 }
13654                                 --$newpage;
13655                                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13656                         // return to last page
13657                         $this->lastPage(true);
13658                         return true;
13659                 }
13660
13661         /**
13662                 * Remove the specified page.
13663                 * @param int $page page to remove
13664                 * @return true in case of success, false in case of error.
13665                 * @access public
13666                 * @since 4.6.004 (2009-04-23)
13667                 */
13668                 public function deletePage($page) {
13669                         if ($page > $this->numpages) {
13670                                 return false;
13671                         }
13672                         // delete current page
13673                         unset($this->pages[$page]);
13674                         unset($this->pagedim[$page]);
13675                         unset($this->pagelen[$page]);
13676                         unset($this->intmrk[$page]);
13677                         if (isset($this->footerpos[$page])) {
13678                                 unset($this->footerpos[$page]);
13679                         }
13680                         if (isset($this->footerlen[$page])) {
13681                                 unset($this->footerlen[$page]);
13682                         }
13683                         if (isset($this->transfmrk[$page])) {
13684                                 unset($this->transfmrk[$page]);
13685                         }
13686                         if (isset($this->PageAnnots[$page])) {
13687                                 unset($this->PageAnnots[$page]);
13688                         }
13689                         if (isset($this->newpagegroup[$page])) {
13690                                 unset($this->newpagegroup[$page]);
13691                         }
13692                         if (isset($this->pageopen[$page])) {
13693                                 unset($this->pageopen[$page]);
13694                         }
13695                         // update remaining pages
13696                         for ($i = $page; $i < $this->numpages; ++$i) {
13697                                 $j = $i + 1;
13698                                 // shift pages
13699                                 $this->pages[$i] = $this->pages[$j];
13700                                 $this->pagedim[$i] = $this->pagedim[$j];
13701                                 $this->pagelen[$i] = $this->pagelen[$j];
13702                                 $this->intmrk[$i] = $this->intmrk[$j];
13703                                 if (isset($this->footerpos[$j])) {
13704                                         $this->footerpos[$i] = $this->footerpos[$j];
13705                                 } elseif (isset($this->footerpos[$i])) {
13706                                         unset($this->footerpos[$i]);
13707                                 }
13708                                 if (isset($this->footerlen[$j])) {
13709                                         $this->footerlen[$i] = $this->footerlen[$j];
13710                                 } elseif (isset($this->footerlen[$i])) {
13711                                         unset($this->footerlen[$i]);
13712                                 }
13713                                 if (isset($this->transfmrk[$j])) {
13714                                         $this->transfmrk[$i] = $this->transfmrk[$j];
13715                                 } elseif (isset($this->transfmrk[$i])) {
13716                                         unset($this->transfmrk[$i]);
13717                                 }
13718                                 if (isset($this->PageAnnots[$j])) {
13719                                         $this->PageAnnots[$i] = $this->PageAnnots[$j];
13720                                 } elseif (isset($this->PageAnnots[$i])) {
13721                                         unset($this->PageAnnots[$i]);
13722                                 }
13723                                 if (isset($this->newpagegroup[$j])) {
13724                                         $this->newpagegroup[$i] = $this->newpagegroup[$j];
13725                                 } elseif (isset($this->newpagegroup[$i])) {
13726                                         unset($this->newpagegroup[$i]);
13727                                 }
13728                                 if (isset($this->pageopen[$j])) {
13729                                         $this->pageopen[$i] = $this->pageopen[$j];
13730                                 } elseif (isset($this->pageopen[$i])) {
13731                                         unset($this->pageopen[$i]);
13732                                 }
13733                         }
13734                         // remove last page
13735                         unset($this->pages[$this->numpages]);
13736                         unset($this->pagedim[$this->numpages]);
13737                         unset($this->pagelen[$this->numpages]);
13738                         unset($this->intmrk[$this->numpages]);
13739                         if (isset($this->footerpos[$this->numpages])) {
13740                                 unset($this->footerpos[$this->numpages]);
13741                         }
13742                         if (isset($this->footerlen[$this->numpages])) {
13743                                 unset($this->footerlen[$this->numpages]);
13744                         }
13745                         if (isset($this->transfmrk[$this->numpages])) {
13746                                 unset($this->transfmrk[$this->numpages]);
13747                         }
13748                         if (isset($this->PageAnnots[$this->numpages])) {
13749                                 unset($this->PageAnnots[$this->numpages]);
13750                         }
13751                         if (isset($this->newpagegroup[$this->numpages])) {
13752                                 unset($this->newpagegroup[$this->numpages]);
13753                         }
13754                         if (isset($this->pageopen[$this->numpages])) {
13755                                 unset($this->pageopen[$this->numpages]);
13756                         }
13757                         --$this->numpages;
13758                         $this->page = $this->numpages;
13759                         // adjust outlines
13760                         $tmpoutlines = $this->outlines;
13761                         foreach ($tmpoutlines as $key => $outline) {
13762                                 if ($outline['p'] > $page) {
13763                                         $this->outlines[$key]['p'] = $outline['p'] - 1;
13764                                 } elseif ($outline['p'] == $page) {
13765                                         unset($this->outlines[$key]);
13766                                 }
13767                         }
13768                         // adjust links
13769                         $tmplinks = $this->links;
13770                         foreach ($tmplinks as $key => $link) {
13771                                 if ($link[0] > $page) {
13772                                         $this->links[$key][0] = $link[0] - 1;
13773                                 } elseif ($link[0] == $page) {
13774                                         unset($this->links[$key]);
13775                                 }
13776                         }
13777                         // adjust javascript
13778                         $tmpjavascript = $this->javascript;
13779                         global $jpage;
13780                         $jpage = $page;
13781                         $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
13782                                 create_function('$matches', 'global $jpage;
13783                                 $pagenum = intval($matches[3]) + 1;
13784                                 if ($pagenum >= $jpage) {
13785                                         $newpage = ($pagenum - 1);
13786                                 } elseif ($pagenum == $jpage) {
13787                                         $newpage = 1;
13788                                 } else {
13789                                         $newpage = $pagenum;
13790                                 }
13791                                 --$newpage;
13792                                 return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
13793                         // return to last page
13794                         $this->lastPage(true);
13795                         return true;
13796                 }
13797
13798                 /**
13799                 * Output a Table of Content Index (TOC).
13800                 * You can override this method to achieve different styles.
13801                 * @param int $page page number where this TOC should be inserted (leave empty for current page).
13802                 * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment).
13803                 * @param string $filler string used to fill the space between text and page number.
13804                 * @access public
13805                 * @author Nicola Asuni
13806                 * @since 4.5.000 (2009-01-02)
13807                 */
13808                 public function addTOC($page='', $numbersfont='', $filler='.') {
13809                         $fontsize = $this->FontSizePt;
13810                         $fontfamily = $this->FontFamily;
13811                         $fontstyle = $this->FontStyle;
13812                         $w = $this->w - $this->lMargin - $this->rMargin;
13813                         $spacer = $this->GetStringWidth(' ') * 4;
13814                         $page_first = $this->getPage();
13815                         $lmargin = $this->lMargin;
13816                         $rmargin = $this->rMargin;
13817                         $x_start = $this->GetX();
13818                         if ($this->empty_string($numbersfont)) {
13819                                 $numbersfont = $this->default_monospaced_font;
13820                         }
13821                         if ($this->empty_string($filler)) {
13822                                 $filler = ' ';
13823                         }
13824                         if ($this->empty_string($page)) {
13825                                 $gap = ' ';
13826                         } else {
13827                                 $gap = '';
13828                         }
13829                         foreach ($this->outlines as $key => $outline) {
13830                                 if ($this->rtl) {
13831                                         $aligntext = 'R';
13832                                         $alignnum = 'L';
13833                                 } else {
13834                                         $aligntext = 'L';
13835                                         $alignnum = 'R';
13836                                 }
13837                                 if ($outline['l'] == 0) {
13838                                         $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
13839                                 } else {
13840                                         $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
13841                                 }
13842                                 $indent = ($spacer * $outline['l']);
13843                                 if ($this->rtl) {
13844                                         $this->rMargin += $indent;
13845                                         $this->x -= $indent;
13846                                 } else {
13847                                         $this->lMargin += $indent;
13848                                         $this->x += $indent;
13849                                 }
13850                                 $link = $this->AddLink();
13851                                 $this->SetLink($link, 0, $outline['p']);
13852                                 // write the text
13853                                 $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
13854                                 $this->SetFont($numbersfont, $fontstyle, $fontsize);
13855                                 if ($this->empty_string($page)) {
13856                                         $pagenum = $outline['p'];
13857                                 } else {
13858                                         // placemark to be replaced with the correct number
13859                                         $pagenum = '{#'.($outline['p']).'}';
13860                                         if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
13861                                                 $pagenum = '{'.$pagenum.'}';
13862                                     }
13863                                 }
13864                                 $numwidth = $this->GetStringWidth($pagenum);
13865                                 if ($this->rtl) {
13866                                         $tw = $this->x - $this->lMargin;
13867                                 } else {
13868                                         $tw = $this->w - $this->rMargin - $this->x;
13869                                 }
13870                                 $fw = $tw - $numwidth - $this->GetStringWidth(' ');
13871                                 $numfills = floor($fw / $this->GetStringWidth($filler));
13872                                 if ($numfills > 0) {
13873                                         $rowfill = str_repeat($filler, $numfills);
13874                                 } else {
13875                                         $rowfill = '';
13876                                 }
13877                                 if ($this->rtl) {
13878                                         $pagenum = $pagenum.$gap.$rowfill.' ';
13879                                 } else {
13880                                         $pagenum = ' '.$rowfill.$gap.$pagenum;
13881                                 }
13882                                 // write the number
13883                                 //$this->SetX($x_start);
13884                                 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
13885                                 $this->SetX($x_start);
13886                                 $this->lMargin = $lmargin;
13887                                 $this->rMargin = $rmargin;
13888                         }
13889                         $page_last = $this->getPage();
13890                         $numpages = $page_last - $page_first + 1;
13891                         if (!$this->empty_string($page)) {
13892                                 for ($p = $page_first; $p <= $page_last; ++$p) {
13893                                         // get page data
13894                                         $temppage = $this->getPageBuffer($p);
13895                                         for ($n = 1; $n <= $this->numpages; ++$n) {
13896                                                 // update page numbers
13897                                                 $k = '{#'.$n.'}';
13898                                                 $ku = '{'.$k.'}';
13899                                                 $alias_a = $this->_escape($k);
13900                                                 $alias_au = $this->_escape('{'.$k.'}');
13901                                                 if ($this->isunicode) {
13902                                                         $alias_b = $this->_escape($this->UTF8ToLatin1($k));
13903                                                         $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
13904                                                         $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
13905                                                         $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
13906                                                 }
13907                                                 if ($n >= $page) {
13908                                                         $np = $n + $numpages;
13909                                                 } else {
13910                                                         $np = $n;
13911                                                 }
13912                                                 $ns = $this->formatTOCPageNumber($np);
13913                                                 $nu = $ns;
13914                                                 $sdiff = strlen($k) - strlen($ns) - 1;
13915                                                 $sdiffu = strlen($ku) - strlen($ns) - 1;
13916                                                 $sfill = str_repeat($filler, $sdiff);
13917                                                 $sfillu = str_repeat($filler, $sdiffu);
13918                                                 if ($this->rtl) {
13919                                                         $ns = $ns.' '.$sfill;
13920                                                         $nu = $nu.' '.$sfillu;
13921                                                 } else {
13922                                                         $ns = $sfill.' '.$ns;
13923                                                         $nu = $sfillu.' '.$nu;
13924                                                 }
13925                                                 $nu = $this->UTF8ToUTF16BE($nu, false);
13926                                                 $temppage = str_replace($alias_au, $nu, $temppage);
13927                                                 if ($this->isunicode) {
13928                                                         $temppage = str_replace($alias_bu, $nu, $temppage);
13929                                                         $temppage = str_replace($alias_cu, $nu, $temppage);
13930                                                         $temppage = str_replace($alias_b, $ns, $temppage);
13931                                                         $temppage = str_replace($alias_c, $ns, $temppage);
13932                                                 }
13933                                                 $temppage = str_replace($alias_a, $ns, $temppage);
13934                                         }
13935                                         // save changes
13936                                         $this->setPageBuffer($p, $temppage);
13937                                 }
13938                                 // move pages
13939                                 for ($i = 0; $i < $numpages; ++$i) {
13940                                         $this->movePage($page_last, $page);
13941                                 }
13942                         }
13943                         $this->SetFont($fontfamily, $fontstyle, $fontsize);
13944                 }
13945
13946                 /**
13947                 * Stores a copy of the current TCPDF object used for undo operation.
13948                 * @access public
13949                 * @since 4.5.029 (2009-03-19)
13950                 */
13951                 public function startTransaction() {
13952                         if (isset($this->objcopy)) {
13953                                 // remove previous copy
13954                                 $this->commitTransaction();
13955                         }
13956                         // clone current object
13957                         $this->objcopy = $this->objclone($this);
13958                 }
13959
13960                 /**
13961                 * Delete the copy of the current TCPDF object used for undo operation.
13962                 * @access public
13963                 * @since 4.5.029 (2009-03-19)
13964                 */
13965                 public function commitTransaction() {
13966                         if (isset($this->objcopy)) {
13967                                 $this->objcopy->_destroy(true, true);
13968                                 unset($this->objcopy);
13969                         }
13970                 }
13971
13972                 /**
13973                 * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
13974                 * @return TCPDF object.
13975                 * @access public
13976                 * @since 4.5.029 (2009-03-19)
13977                 */
13978                 public function rollbackTransaction() {
13979                         if (isset($this->objcopy)) {
13980                                 if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
13981                                         // truncate files to previous values
13982                                         foreach ($this->objcopy->cache_file_lenght as $file => $lenght) {
13983                                                 $file = substr($file, 1);
13984                                                 $handle = fopen($file, 'r+');
13985                                                 ftruncate($handle, $lenght);
13986                                         }
13987                                 }
13988                                 $this->_destroy(true, true);
13989                                 return $this->objcopy;
13990                         }
13991                         return $this;
13992                 }
13993
13994                 /**
13995                 * Creates a copy of a class object
13996                 * @param object $object class object to be cloned
13997                 * @return cloned object
13998                 * @access public
13999                 * @since 4.5.029 (2009-03-19)
14000                 */
14001                 public function objclone($object) {
14002                         return @clone($object);
14003                 }
14004
14005                 /**
14006                 * Determine whether a string is empty.
14007                 * @param srting $str string to be checked
14008                 * @return boolean true if string is empty
14009                 * @access public
14010                 * @since 4.5.044 (2009-04-16)
14011                 */
14012                 public function empty_string($str) {
14013                         return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
14014                 }
14015                 
14016         } // END OF TCPDF CLASS
14017 }
14018 //============================================================+
14019 // END OF FILE
14020 //============================================================+
14021 ?>