Fixed TCPDF error message display.
[fa-stable.git] / reporting / includes / tcpdf.php
1 <?php
2 //============================================================+
3 // File name   : tcpdf.php
4 // Begin       : 2002-08-03
5 // Last Update : 2008-09-19
6 // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
7 // Version     : 4.0.027_PHP4
8 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
9 //      ----------------------------------------------------------------------------
10 //  Copyright (C) 2002-2008  Nicola Asuni - Tecnick.com S.r.l.
11 //
12 //      This program is free software: you can redistribute it and/or modify
13 //      it under the terms of the GNU Lesser General Public License as published by
14 //      the Free Software Foundation, either version 2.1 of the License, or
15 //      (at your option) any later version.
16 //
17 //      This program is distributed in the hope that it will be useful,
18 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 //      GNU Lesser General Public License for more details.
21 //
22 //      You should have received a copy of the GNU Lesser General Public License
23 //      along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 //
25 //      See LICENSE.TXT file for more information.
26 //  ----------------------------------------------------------------------------
27 //
28 // Description : This is a PHP class for generating PDF documents without
29 //               requiring external extensions.
30 //
31 // NOTE:
32 // This class was originally derived in 2002 from the Public
33 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
34 // but now is almost entirely rewritten.
35 //
36 // Main features:
37 //  * no external libraries are required for the basic functions;
38 //      * supports all ISO page formats;
39 //      * supports UTF-8 Unicode and Right-To-Left languages;
40 //      * supports document encryption;
41 //      * includes methods to publish some XHTML code;
42 //      * includes graphic (geometric) and transformation methods;
43 //      * includes bookmarks;
44 //      * includes Javascript and forms support;
45 //      * includes a method to print various barcode formats;
46 //      * supports TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
47 //      * supports custom page formats, margins and units of measure;
48 //      * includes methods for page header and footer management;
49 //      * supports automatic page break;
50 //      * supports automatic page numbering and page groups;
51 //      * supports automatic line break and text justification;
52 //      * supports JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
53 //      * supports stroke and clipping mode for text;
54 //      * supports clipping masks;
55 //      * supports Grayscale, RGB, CMYK, Spot colors and transparency;
56 //      * supports links and annotations;
57 //      * supports page compression (requires zlib extension);
58 //      * supports PDF user's rights.
59 //
60 // -----------------------------------------------------------
61 // THANKS TO:
62 //
63 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
64 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
65 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
66 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
67 // dullus for text Justification.
68 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
69 // Patrick Benny for text stretch suggestion on Cell().
70 // Johannes Güntert for JavaScript support.
71 // Denis Van Nuffelen for Dynamic Form.
72 // Jacek Czekaj for multibyte justification
73 // Anthony Ferrara for the reintroduction of legacy image methods.
74 // Sourceforge user 1707880 (hucste) for line-trough mode.
75 // Larry Stanbery for page groups.
76 // Martin Hall-May for transparency.
77 // Aaron C. Spike for Polycurve method.
78 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
79 // Moritz Wagner and Andreas Wurmser for graphic functions.
80 // Andrew Whitehead for core fonts support.
81 // Esteban Joël Marín for OpenType font conversion.
82 // Teus Hagen for several suggestions and fixes.
83 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
84 // Kosmas Papachristos for some CSS improvements.
85 // Anyone that has reported a bug or sent a suggestion.
86 //============================================================+
87
88 /**
89  * This is a PHP class for generating PDF documents without requiring external extensions.<br>
90  * 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>
91  * <h3>TCPDF main features are:</h3>
92  * <ul>
93  * <li>no external libraries are required for the basic functions;</li>
94  * <li>supports all ISO page formats;</li>
95  * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
96  * <li>supports document encryption;</li>
97  * <li>includes methods to publish some XHTML code;</li>
98  * <li>includes graphic (geometric) and transformation methods;</li>
99  * <li>includes bookmarks;</li>
100  * <li>includes Javascript and forms support;</li>
101  * <li>includes a method to print various barcode formats;</li>
102  * <li>supports TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
103  * <li>supports custom page formats, margins and units of measure;</li>
104  * <li>includes methods for page header and footer management;</li>
105  * <li>supports automatic page break;</li>
106  * <li>supports automatic page numbering and page groups;</li>
107  * <li>supports automatic line break and text justification;
108  * <li>supports JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;</li>
109  * <li>supports stroke and clipping mode for text;</li>
110  * <li>supports clipping masks;</li>
111  * <li>supports Grayscale, RGB and CMYK colors and transparency;</li>
112  * <li>supports links and annotations;</li>
113  * <li>supports page compression (requires zlib extension);</li>
114  * <li>supports PDF user's rights.</li>
115  * </ul>
116  * Tools to encode your unicode fonts are on fonts/utils directory.</p>
117  * @package com.tecnick.tcpdf
118  * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
119  * @author Nicola Asuni
120  * @copyright 2004-2008 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
121  * @link http://www.tcpdf.org
122  * @license http://www.gnu.org/copyleft/lesser.html LGPL
123  * @version 4.0.027_PHP4
124  */
125
126 /**
127  * main configuration file
128  */
129 /** -------------------------------FrontAccounting 2.0  ---------------------------
130   * following changes are done for FrontAccounting 2.0 - Joe Hunt 06.08.2008
131   * 1. /config/tcpdf_config.php is not included, commented out
132   * 2. Following 3 defines instead:
133   *    if (!defined("K_PATH_FONTS"))
134   *        define ("K_PATH_FONTS", '../reporting/fonts/');
135   *    define ("K_PATH_CACHE", '../reporting/fonts/');
136   *    define("K_CELL_HEIGHT_RATIO", 1.25);
137   * 3. ./unicode_data2.php only included if unicode is set. (in class constructor)
138   *    We only use a reduced variant of unicode_data.php (unicode_data.php).af wrap the
139   *    following defines
140   *    if (!defined("K_RE_PATTERN_RTL"))
141   *    and
142   *    if (!defined("K_RE_PATTERN_ARABIC"))
143   * 4. Parameter $unicode in constructor renamed to $uni.
144   * 4. Header function renamed to Header1 (due to conflict with FrontReport Header)
145   * -------------------------------------------------------------------------------
146   */
147 if (!defined("K_PATH_FONTS"))
148         define ("K_PATH_FONTS", '../reporting/fonts/');
149 define ("K_PATH_CACHE", '../reporting/fonts/');
150 define("K_CELL_HEIGHT_RATIO", 1.25);
151
152 //require_once(dirname(__FILE__).'/config/tcpdf_config.php');
153
154 // includes some support files
155
156 /**
157  * unicode data
158  */
159 // only included if unicode
160 //include_once(dirname(__FILE__)."/unicode_data2.php");
161
162 /**
163  * html colors table
164  */
165 require_once(dirname(__FILE__).'/htmlcolors.php');
166
167 /**
168  * barcode class
169  */
170 require_once(dirname(__FILE__)."/barcodes.php");
171
172 /**
173  * HTML entity decode functions
174  */
175 require_once(dirname(__FILE__)."/html_entity_decode_php4.php");
176
177 if (!class_exists('TCPDF')) {
178         /**
179          * define default PDF document producer
180          */
181         define('PDF_PRODUCER','TCPDF 4.0.027_PHP4 (http://www.tcpdf.org)');
182
183         /**
184         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
185         * 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>
186         * @name TCPDF
187         * @package com.tecnick.tcpdf
188         * @version 4.0.027_PHP4
189         * @author Nicola Asuni - info@tecnick.com
190         * @link http://www.tcpdf.org
191         * @license http://www.gnu.org/copyleft/lesser.html LGPL
192         */
193         class TCPDF {
194                 
195                 // protected or Protected properties
196
197                 /**
198                 * @var current page number
199                 * @access protected
200                 */
201                 var $page;
202
203                 /**
204                 * @var current object number
205                 * @access protected
206                 */
207                 var $n;
208
209                 /**
210                 * @var array of object offsets
211                 * @access protected
212                 */
213                 var $offsets;
214
215                 /**
216                 * @var buffer holding in-memory PDF
217                 * @access protected
218                 */
219                 var $buffer;
220
221                 /**
222                 * @var array containing pages
223                 * @access protected
224                 */
225                 var $pages = array();
226
227                 /**
228                 * @var current document state
229                 * @access protected
230                 */
231                 var $state;
232
233                 /**
234                 * @var compression flag
235                 * @access protected
236                 */
237                 var $compress;
238
239                 /**
240                 * @var current page orientation (P = Portrait, L = Landscape)
241                 * @access protected
242                 */
243                 var $CurOrientation;
244
245                 /**
246                 * @var array that stores page dimensions.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => 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></ul>
247                 * @access protected
248                 */
249                 var $pagedim = array();
250
251                 /**
252                 * @var scale factor (number of points in user unit)
253                 * @access protected
254                 */
255                 var $k;
256
257                 /**
258                 * @var width of page format in points
259                 * @access protected
260                 */
261                 var $fwPt;
262
263                 /**
264                 * @var height of page format in points
265                 * @access protected
266                 */
267                 var $fhPt;
268
269                 /**
270                 * @var current width of page in points
271                 * @access protected
272                 */
273                 var $wPt;
274
275                 /**
276                 * @var current height of page in points
277                 * @access protected
278                 */
279                 var $hPt;
280
281                 /**
282                 * @var current width of page in user unit
283                 * @access protected
284                 */
285                 var $w;
286
287                 /**
288                 * @var current height of page in user unit
289                 * @access protected
290                 */
291                 var $h;
292
293                 /**
294                 * @var left margin
295                 * @access protected
296                 */
297                 var $lMargin;
298
299                 /**
300                 * @var top margin
301                 * @access protected
302                 */
303                 var $tMargin;
304
305                 /**
306                 * @var right margin
307                 * @access protected
308                 */
309                 var $rMargin;
310
311                 /**
312                 * @var page break margin
313                 * @access protected
314                 */
315                 var $bMargin;
316
317                 /**
318                 * @var cell internal padding
319                 * @access protected
320                 */
321                 var $cMargin;
322
323                 /**
324                 * @var cell internal padding (previous value)
325                 * @access protected
326                 */
327                 var $oldcMargin;
328
329                 /**
330                 * @var current horizontal position in user unit for cell positioning
331                 * @access protected
332                 */
333                 var $x;
334
335                 /**
336                 * @var current vertical position in user unit for cell positioning
337                 * @access protected
338                 */
339                 var $y;
340
341                 /**
342                 * @var height of last cell printed
343                 * @access protected
344                 */
345                 var $lasth;
346
347                 /**
348                 * @var line width in user unit
349                 * @access protected
350                 */
351                 var $LineWidth;
352
353                 /**
354                 * @var array of standard font names
355                 * @access protected
356                 */
357                 var $CoreFonts;
358
359                 /**
360                 * @var array of used fonts
361                 * @access protected
362                 */
363                 var $fonts = array();
364
365                 /**
366                 * @var array of font files
367                 * @access protected
368                 */
369                 var $FontFiles = array();
370
371                 /**
372                 * @var array of encoding differences
373                 * @access protected
374                 */
375                 var $diffs = array();
376
377                 /**
378                 * @var array of used images
379                 * @access protected
380                 */
381                 var $images = array();
382
383                 /**
384                 * @var array of Annotations in pages
385                 * @access protected
386                 */
387                 var $PageAnnots = array();
388
389                 /**
390                 * @var array of internal links
391                 * @access protected
392                 */
393                 var $links = array();
394
395                 /**
396                 * @var current font family
397                 * @access protected
398                 */
399                 var $FontFamily;
400
401                 /**
402                 * @var current font style
403                 * @access protected
404                 */
405                 var $FontStyle;
406
407                 /**
408                 * @var current font ascent (distance between font top and baseline)
409                 * @access protected
410                 * @since 2.8.000 (2007-03-29)
411                 */
412                 var $FontAscent;
413
414                 /**
415                 * @var current font descent (distance between font bottom and baseline)
416                 * @access protected
417                 * @since 2.8.000 (2007-03-29)
418                 */
419                 var $FontDescent;
420
421                 /**
422                 * @var underlining flag
423                 * @access protected
424                 */
425                 var $underline;
426
427                 /**
428                 * @var current font info
429                 * @access protected
430                 */
431                 var $CurrentFont;
432
433                 /**
434                 * @var current font size in points
435                 * @access protected
436                 */
437                 var $FontSizePt;
438
439                 /**
440                 * @var current font size in user unit
441                 * @access protected
442                 */
443                 var $FontSize;
444
445                 /**
446                 * @var commands for drawing color
447                 * @access protected
448                 */
449                 var $DrawColor;
450
451                 /**
452                 * @var commands for filling color
453                 * @access protected
454                 */
455                 var $FillColor;
456
457                 /**
458                 * @var commands for text color
459                 * @access protected
460                 */
461                 var $TextColor;
462
463                 /**
464                 * @var indicates whether fill and text colors are different
465                 * @access protected
466                 */
467                 var $ColorFlag;
468
469                 /**
470                 * @var word spacing
471                 * @access protected
472                 */
473                 var $ws;
474
475                 /**
476                 * @var automatic page breaking
477                 * @access protected
478                 */
479                 var $AutoPageBreak;
480
481                 /**
482                 * @var threshold used to trigger page breaks
483                 * @access protected
484                 */
485                 var $PageBreakTrigger;
486
487                 /**
488                 * @var flag set when processing footer
489                 * @access protected
490                 */
491                 var $InFooter;
492
493                 /**
494                 * @var zoom display mode
495                 * @access protected
496                 */
497                 var $ZoomMode;
498
499                 /**
500                 * @var layout display mode
501                 * @access protected
502                 */
503                 var $LayoutMode;
504
505                 /**
506                 * @var title
507                 * @access protected
508                 */
509                 var $title;
510
511                 /**
512                 * @var subject
513                 * @access protected
514                 */
515                 var $subject;
516
517                 /**
518                 * @var author
519                 * @access protected
520                 */
521                 var $author;
522
523                 /**
524                 * @var keywords
525                 * @access protected
526                 */
527                 var $keywords;
528
529                 /**
530                 * @var creator
531                 * @access protected
532                 */
533                 var $creator;
534
535                 /**
536                 * @var alias for total number of pages
537                 * @access protected
538                 */
539                 var $AliasNbPages;
540
541                 /**
542                 * @var right-bottom corner X coordinate of inserted image
543                 * @since 2002-07-31
544                 * @author Nicola Asuni
545                 * @access protected
546                 */
547                 var $img_rb_x;
548
549                 /**
550                 * @var right-bottom corner Y coordinate of inserted image
551                 * @since 2002-07-31
552                 * @author Nicola Asuni
553                 * @access protected
554                 */
555                 var $img_rb_y;
556
557                 /**
558                 * @var image scale factor
559                 * @since 2004-06-14
560                 * @author Nicola Asuni
561                 * @access protected
562                 */
563                 var $imgscale = 1;
564
565                 /**
566                 * @var boolean set to true when the input text is unicode (require unicode fonts)
567                 * @since 2005-01-02
568                 * @author Nicola Asuni
569                 * @access protected
570                 */
571                 var $isunicode = false;
572
573                 /**
574                 * @var PDF version
575                 * @since 1.5.3
576                 * @access protected
577                 */
578                 var $PDFVersion = "1.7";
579
580
581                 // ----------------------
582
583                 /**
584                  * @var Minimum distance between header and top page margin.
585                  * @access protected
586                  */
587                 var $header_margin;
588
589                 /**
590                  * @var Minimum distance between footer and bottom page margin.
591                  * @access protected
592                  */
593                 var $footer_margin;
594
595                 /**
596                  * @var original left margin value
597                  * @access protected
598                  * @since 1.53.0.TC013
599                  */
600                 var $original_lMargin;
601
602                 /**
603                  * @var original right margin value
604                  * @access protected
605                  * @since 1.53.0.TC013
606                  */
607                 var $original_rMargin;
608
609                 /**
610                  * @var Header font.
611                  * @access protected
612                  */
613                 var $header_font;
614
615                 /**
616                  * @var Footer font.
617                  * @access protected
618                  */
619                 var $footer_font;
620
621                 /**
622                  * @var Language templates.
623                  * @access protected
624                  */
625                 var $l;
626
627                 /**
628                  * @var Barcode to print on page footer (only if set).
629                  * @access protected
630                  */
631                 var $barcode = false;
632
633                 /**
634                  * @var If true prints header
635                  * @access protected
636                  */
637                 var $print_header = true;
638
639                 /**
640                  * @var If true prints footer.
641                  * @access protected
642                  */
643                 var $print_footer = true;
644
645                 /**
646                  * @var Header image logo.
647                  * @access protected
648                  */
649                 var $header_logo = "";
650
651                 /**
652                  * @var Header image logo width in mm.
653                  * @access protected
654                  */
655                 var $header_logo_width = 30;
656
657                 /**
658                  * @var String to print as title on document header.
659                  * @access protected
660                  */
661                 var $header_title = "";
662
663                 /**
664                  * @var String to print on document header.
665                  * @access protected
666                  */
667                 var $header_string = "";
668
669                 /**
670                  * @var Default number of columns for html table.
671                  * @access protected
672                  */
673                 var $default_table_columns = 4;
674
675
676                 // variables for html parser
677
678                 /**
679                  * @var HTML PARSER: store current link.
680                  * @access protected
681                  */
682                 var $HREF;
683
684                 /**
685                  * @var store available fonts list.
686                  * @access protected
687                  */
688                 var $fontlist = array();
689
690                 /**
691                  * @var current foreground color
692                  * @access protected
693                  */
694                 var $fgcolor;
695
696                 /**
697                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
698                  * @access protected
699                  */
700                 var $listordered = array();
701
702                 /**
703                  * @var HTML PARSER: array count list items on nested lists.
704                  * @access protected
705                  */
706                 var $listcount = array();
707
708                 /**
709                  * @var HTML PARSER: current list nesting level.
710                  * @access protected
711                  */
712                 var $listnum = 0;
713
714                 /**
715                  * @var HTML PARSER: indent amount for lists.
716                  * @access protected
717                  */
718                 var $listindent;
719
720                 /**
721                  * @var current background color
722                  * @access protected
723                  */
724                 var $bgcolor;
725
726                 /**
727                  * @var Store temporary font size in points.
728                  * @access protected
729                  */
730                 var $tempfontsize = 10;
731
732                 /**
733                  * @var Bold font style status.
734                  * @access protected
735                  */
736                 var $b;
737
738                 /**
739                  * @var Underlined font style status.
740                  * @access protected
741                  */
742                 var $u;
743
744                 /**
745                  * @var Italic font style status.
746                  * @access protected
747                  */
748                 var $i;
749
750                 /**
751                  * @var Line through font style status.
752                  * @access protected
753                  * @since 2.8.000 (2008-03-19)
754                  */
755                 var $d;
756
757                 /**
758                  * @var spacer for LI tags.
759                  * @access protected
760                  */
761                 var $lispacer = "";
762
763                 /**
764                  * @var default encoding
765                  * @access protected
766                  * @since 1.53.0.TC010
767                  */
768                 var $encoding = "UTF-8";
769
770                 /**
771                  * @var PHP internal encoding
772                  * @access protected
773                  * @since 1.53.0.TC016
774                  */
775                 var $internal_encoding;
776
777                 /**
778                  * @var indicates if the document language is Right-To-Left
779                  * @access protected
780                  * @since 2.0.000
781                  */
782                 var $rtl = false;
783
784                 /**
785                  * @var used to force RTL or LTR string inversion
786                  * @access protected
787                  * @since 2.0.000
788                  */
789                 var $tmprtl = false;
790
791                 // --- Variables used for document encryption:
792
793                 /**
794                  * Indicates whether document is protected
795                  * @access protected
796                  * @since 2.0.000 (2008-01-02)
797                  */
798                 var $encrypted;
799
800                 /**
801                  * U entry in pdf document
802                  * @access protected
803                  * @since 2.0.000 (2008-01-02)
804                  */
805                 var $Uvalue;
806
807                 /**
808                  * O entry in pdf document
809                  * @access protected
810                  * @since 2.0.000 (2008-01-02)
811                  */
812                 var $Ovalue;
813
814                 /**
815                  * P entry in pdf document
816                  * @access protected
817                  * @since 2.0.000 (2008-01-02)
818                  */
819                 var $Pvalue;
820
821                 /**
822                  * encryption object id
823                  * @access protected
824                  * @since 2.0.000 (2008-01-02)
825                  */
826                 var $enc_obj_id;
827
828                 /**
829                  * last RC4 key encrypted (cached for optimisation)
830                  * @access protected
831                  * @since 2.0.000 (2008-01-02)
832                  */
833                 var $last_rc4_key;
834
835                 /**
836                  * last RC4 computed key
837                  * @access protected
838                  * @since 2.0.000 (2008-01-02)
839                  */
840                 var $last_rc4_key_c;
841
842                 // --- bookmark ---
843
844                 /**
845                  * Outlines for bookmark
846                  * @access protected
847                  * @since 2.1.002 (2008-02-12)
848                  */
849                 var $outlines = array();
850
851                 /**
852                  * Outline root for bookmark
853                  * @access protected
854                  * @since 2.1.002 (2008-02-12)
855                  */
856                 var $OutlineRoot;
857
858
859                 // --- javascript and form ---
860
861                 /**
862                  * javascript code
863                  * @access protected
864                  * @since 2.1.002 (2008-02-12)
865                  */
866                 var $javascript = "";
867
868                 /**
869                  * javascript counter
870                  * @access protected
871                  * @since 2.1.002 (2008-02-12)
872                  */
873                 var $n_js;
874
875                 /**
876                  * line trough state
877                  * @access protected
878                  * @since 2.8.000 (2008-03-19)
879                  */
880                 var $linethrough;
881
882                 // --- Variables used for User's Rights ---
883                 // See PDF reference chapter 8.7 Digital Signatures
884
885                 /**
886                  * If true enables user's rights on PDF reader
887                  * @access protected
888                  * @since 2.9.000 (2008-03-26)
889                  */
890                 var $ur;
891
892                 /**
893                  * Names specifying additional document-wide usage rights for the document.
894                  * @access protected
895                  * @since 2.9.000 (2008-03-26)
896                  */
897                 var $ur_document;
898
899                 /**
900                  * Names specifying additional annotation-related usage rights for the document.
901                  * @access protected
902                  * @since 2.9.000 (2008-03-26)
903                  */
904                 var $ur_annots;
905
906                 /**
907                  * Names specifying additional form-field-related usage rights for the document.
908                  * @access protected
909                  * @since 2.9.000 (2008-03-26)
910                  */
911                 var $ur_form;
912
913                 /**
914                  * Names specifying additional signature-related usage rights for the document.
915                  * @access protected
916                  * @since 2.9.000 (2008-03-26)
917                  */
918                 var $ur_signature;
919
920                 /**
921                  * Dot Per Inch Document Resolution (do not change)
922                  * @access protected
923                  * @since 3.0.000 (2008-03-27)
924                  */
925                 var $dpi = 72;
926
927                 /**
928                  * Indicates whether a new page group was requested
929                  * @access protected
930                  * @since 3.0.000 (2008-03-27)
931                  */
932                 var $newpagegroup;
933
934                 /**
935                  * Contains the number of pages of the groups
936                  * @access protected
937                  * @since 3.0.000 (2008-03-27)
938                  */
939                 var $pagegroups;
940
941                 /**
942                  * Contains the alias of the current page group
943                  * @access protected
944                  * @since 3.0.000 (2008-03-27)
945                  */
946                 var $currpagegroup;
947
948                 /**
949                  * Restrict the rendering of some elements to screen or printout.
950                  * @access protected
951                  * @since 3.0.000 (2008-03-27)
952                  */
953                 var $visibility="all";
954
955                 /**
956                  * Print visibility.
957                  * @access protected
958                  * @since 3.0.000 (2008-03-27)
959                  */
960                 var $n_ocg_print;
961
962                 /**
963                  * View visibility.
964                  * @access protected
965                  * @since 3.0.000 (2008-03-27)
966                  */
967                 var $n_ocg_view;
968
969                 /**
970                  * Array of transparency objects and parameters.
971                  * @access protected
972                  * @since 3.0.000 (2008-03-27)
973                  */
974                 var $extgstates;
975
976                 /**
977                  * Set the default JPEG compression quality (1-100)
978                  * @access protected
979                  * @since 3.0.000 (2008-03-27)
980                  */
981                 var $jpeg_quality;
982
983                 /**
984                  * Default cell height ratio.
985                  * @access protected
986                  * @since 3.0.014 (2008-05-23)
987                  */
988                 var $cell_height_ratio = K_CELL_HEIGHT_RATIO;
989
990                 /**
991                  * PDF viewer preferences.
992                  * @access protected
993                  * @since 3.1.000 (2008-06-09)
994                  */
995                 var $viewer_preferences;
996
997                 /**
998                  * A name object specifying how the document should be displayed when opened.
999                  * @access protected
1000                  * @since 3.1.000 (2008-06-09)
1001                  */
1002                 var $PageMode;
1003
1004                 /**
1005                  * Array for storing gradient information.
1006                  * @access protected
1007                  * @since 3.1.000 (2008-06-09)
1008                  */
1009                 var $gradients = array();
1010
1011                 /**
1012                  * Array used to store positions inside the pages buffer.
1013                  * keys are the page numbers
1014                  * @access protected
1015                  * @since 3.2.000 (2008-06-26)
1016                  */
1017                 var $intmrk = array();
1018
1019                 /**
1020                  * Array used to store footer positions of each page.
1021                  * @access protected
1022                  * @since 3.2.000 (2008-07-01)
1023                  */
1024                 var $footerpos = array();
1025
1026
1027                 /**
1028                  * Array used to store footer lenght of each page.
1029                  * @access protected
1030                  * @since 4.0.014 (2008-07-29)
1031                  */
1032                 var $footerlen = array();
1033
1034                 /**
1035                  * True if a newline is created.
1036                  * @access protected
1037                  * @since 3.2.000 (2008-07-01)
1038                  */
1039                 var $newline = true;
1040
1041                 /**
1042                  * End position of the latest inserted line
1043                  * @access protected
1044                  * @since 3.2.000 (2008-07-01)
1045                  */
1046                 var $endlinex = 0;
1047
1048                 /**
1049                  * PDF string for last line width
1050                  * @access protected
1051                  * @since 4.0.006 (2008-07-16)
1052                  */
1053                 var $linestyleWidth = "";
1054
1055                 /**
1056                  * PDF string for last line width
1057                  * @access protected
1058                  * @since 4.0.006 (2008-07-16)
1059                  */
1060                 var $linestyleCap = "0 J";
1061
1062                 /**
1063                  * PDF string for last line width
1064                  * @access protected
1065                  * @since 4.0.006 (2008-07-16)
1066                  */
1067                 var $linestyleJoin = "0 j";
1068
1069                 /**
1070                  * PDF string for last line width
1071                  * @access protected
1072                  * @since 4.0.006 (2008-07-16)
1073                  */
1074                 var $linestyleDash = "[] 0 d";
1075
1076                 /**
1077                  * True if marked-content sequence is open
1078                  * @access protected
1079                  * @since 4.0.013 (2008-07-28)
1080                  */
1081                 var $openMarkedContent = false;
1082
1083                 /**
1084                  * Count the latest inserted vertical spaces on HTML.
1085                  * @access protected
1086                  * @since 4.0.021 (2008-08-24)
1087                  */
1088                 var $htmlvspace = 0;
1089                 
1090                 /**
1091                  * Array of Spot colors
1092                  * @access protected
1093                  * @since 4.0.024 (2008-09-12)
1094                  */
1095                 var $spot_colors = array();
1096
1097                 //------------------------------------------------------------
1098                 // METHODS
1099                 //------------------------------------------------------------
1100
1101                 /**
1102                  * This is the class constructor.
1103                  * It allows to set up the page format, the orientation and
1104                  * the measure unit used in all the methods (except for the font sizes).
1105                  * @since 1.0
1106                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1107                  * @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.
1108                  * @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>
1109                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1110                  * @param String $encoding charset encoding; default is UTF-8
1111                  */
1112                 function TCPDF($orientation='P', $unit='mm', $format='A4', $uni=true, $encoding="UTF-8") {
1113                         if ($uni) // Fix for FrontAccounting
1114                         {
1115                                 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
1116                                 include_once(dirname(__FILE__)."/unicode_data2.php");
1117                         }
1118                         /* Set internal character encoding to ASCII */
1119                         if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) {
1120                                 $this->internal_encoding = mb_internal_encoding();
1121                                 mb_internal_encoding("ASCII");
1122                         }
1123                         // set language direction
1124                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1125                         $this->tmprtl = false;
1126                         //Some checks
1127                         $this->_dochecks();
1128                         //Initialization of properties
1129                         $this->isunicode = $uni;
1130                         $this->page = 0;
1131                         $this->pagedim = array();
1132                         $this->n = 2;
1133                         $this->buffer = '';
1134                         $this->pages = array();
1135                         $this->state = 0;
1136                         $this->fonts = array();
1137                         $this->FontFiles = array();
1138                         $this->diffs = array();
1139                         $this->images = array();
1140                         $this->links = array();
1141                         $this->gradients = array();
1142                         $this->InFooter = false;
1143                         $this->lasth = 0;
1144                         $this->FontFamily = 'helvetica';
1145                         $this->FontStyle = '';
1146                         $this->FontSizePt = 12;
1147                         $this->underline = false;
1148                         $this->linethrough = false;
1149                         $this->DrawColor = '0 G';
1150                         $this->FillColor = '0 g';
1151                         $this->TextColor = '0 g';
1152                         $this->ColorFlag = false;
1153                         $this->ws = 0;
1154                         // encryption values
1155                         $this->encrypted = false;
1156                         $this->last_rc4_key = '';
1157                         $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";
1158                         //Standard Unicode fonts
1159                         $this->CoreFonts = array(
1160                                 'courier'=>'Courier',
1161                                 'courierB'=>'Courier-Bold',
1162                                 'courierI'=>'Courier-Oblique',
1163                                 'courierBI'=>'Courier-BoldOblique',
1164                                 'helvetica'=>'Helvetica',
1165                                 'helveticaB'=>'Helvetica-Bold',
1166                                 'helveticaI'=>'Helvetica-Oblique',
1167                                 'helveticaBI'=>'Helvetica-BoldOblique',
1168                                 'times'=>'Times-Roman',
1169                                 'timesB'=>'Times-Bold',
1170                                 'timesI'=>'Times-Italic',
1171                                 'timesBI'=>'Times-BoldItalic',
1172                                 'symbol'=>'Symbol',
1173                                 'zapfdingbats'=>'ZapfDingbats'
1174                         );
1175                         //Set scale factor
1176                         $this->setPageUnit($unit);
1177                         // set page format and orientation
1178                         $this->setPageFormat($format, $orientation);
1179                         //Page margins (1 cm)
1180                         $margin = 28.35 / $this->k;
1181                         $this->SetMargins($margin,$margin);
1182                         //Interior cell margin (1 mm)
1183                         $this->cMargin = $margin / 10;
1184                         //Line width (0.2 mm)
1185                         $this->LineWidth = 0.57 / $this->k;
1186                         $this->linestyleWidth = sprintf('%.2f w', ($this->LineWidth * $this->k));
1187                         $this->linestyleCap = "0 J";
1188                         $this->linestyleJoin = "0 j";
1189                         $this->linestyleDash = "[] 0 d";
1190                         //Automatic page break
1191                         $this->SetAutoPageBreak(true, 2*$margin);
1192                         //Full width display mode
1193                         $this->SetDisplayMode('fullwidth');
1194                         //Compression
1195                         $this->SetCompression(true);
1196                         //Set default PDF version number
1197                         $this->PDFVersion = "1.7";
1198                         $this->encoding = $encoding;
1199                         $this->HREF = '';
1200                         $this->getFontsList();
1201                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1202                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1203                         $this->extgstates = array();
1204                         // user's rights
1205                         $this->ur = false;
1206                         $this->ur_document = "/FullSave";
1207                         $this->ur_annots = "/Create/Delete/Modify/Copy/Import/Export";
1208                         $this->ur_form = "/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate";
1209                         $this->ur_signature = "/Modify";
1210                         // set default JPEG quality
1211                         $this->jpeg_quality = 75;
1212                         // initialize some settings
1213                         $this->utf8Bidi(array(""));
1214                 }
1215
1216                 /**
1217                  * Default destructor.
1218                  * @since 1.53.0.TC016
1219                  */
1220                 function TCPDFDestruct() {
1221                         // restore internal encoding
1222                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1223                                 mb_internal_encoding($this->internal_encoding);
1224                         }
1225                 }
1226
1227                 /**
1228                 * Set the units of measure for the document.
1229                 * @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.
1230                 * @since 3.0.015 (2008-06-06)
1231                 */
1232                 function setPageUnit($unit) {
1233                 //Set scale factor
1234                         switch (strtolower($unit)) {
1235                                 // points
1236                                 case 'pt': {
1237                                         $this->k = 1;
1238                                         break;
1239                                 }
1240                                 // millimeters
1241                                 case 'mm': {
1242                                         $this->k = $this->dpi / 25.4;
1243                                         break;
1244                                 }
1245                                 // centimeters
1246                                 case 'cm': {
1247                                         $this->k = $this->dpi / 2.54;
1248                                         break;
1249                                 }
1250                                 // inches
1251                                 case 'in': {
1252                                         $this->k = $this->dpi;
1253                                         break;
1254                                 }
1255                                 // unsupported unit
1256                                 default : {
1257                                         $this->Error('Incorrect unit: '.$unit);
1258                                         break;
1259                                 }
1260                         }
1261                         if (isset($this->CurOrientation)) {
1262                                         $this->setPageOrientation($this->CurOrientation);
1263                         }
1264                 }
1265
1266                 /**
1267                 * Set the page format
1268                 * @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>
1269                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1270                 * @since 3.0.015 (2008-06-06)
1271                 */
1272                 function setPageFormat($format, $orientation="P") {
1273                         //Page format
1274                         if (is_string($format)) {
1275                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1276                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1277                                 switch (strtoupper($format)){
1278                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1279                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1280                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1281                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1282                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1283                                         case 'A3': {$format = array(841.89,1190.55); break;}
1284                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1285                                         case 'A5': {$format = array(419.53,595.28); break;}
1286                                         case 'A6': {$format = array(297.64,419.53); break;}
1287                                         case 'A7': {$format = array(209.76,297.64); break;}
1288                                         case 'A8': {$format = array(147.40,209.76); break;}
1289                                         case 'A9': {$format = array(104.88,147.40); break;}
1290                                         case 'A10': {$format = array(73.70,104.88); break;}
1291                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1292                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1293                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1294                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1295                                         case 'B4': {$format = array(708.66,1000.63); break;}
1296                                         case 'B5': {$format = array(498.90,708.66); break;}
1297                                         case 'B6': {$format = array(354.33,498.90); break;}
1298                                         case 'B7': {$format = array(249.45,354.33); break;}
1299                                         case 'B8': {$format = array(175.75,249.45); break;}
1300                                         case 'B9': {$format = array(124.72,175.75); break;}
1301                                         case 'B10': {$format = array(87.87,124.72); break;}
1302                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1303                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1304                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1305                                         case 'C3': {$format = array(918.43,1298.27); break;}
1306                                         case 'C4': {$format = array(649.13,918.43); break;}
1307                                         case 'C5': {$format = array(459.21,649.13); break;}
1308                                         case 'C6': {$format = array(323.15,459.21); break;}
1309                                         case 'C7': {$format = array(229.61,323.15); break;}
1310                                         case 'C8': {$format = array(161.57,229.61); break;}
1311                                         case 'C9': {$format = array(113.39,161.57); break;}
1312                                         case 'C10': {$format = array(79.37,113.39); break;}
1313                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1314                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1315                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1316                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1317                                         case 'RA4': {$format = array(609.45,864.57); break;}
1318                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1319                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1320                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1321                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1322                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1323                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1324                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1325                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1326                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1327                                 }
1328                                 $this->fwPt = $format[0];
1329                                 $this->fhPt = $format[1];
1330                         }
1331                         else {
1332                                 $this->fwPt = $format[0] * $this->k;
1333                                 $this->fhPt = $format[1] * $this->k;
1334                         }
1335                         $this->setPageOrientation($orientation);
1336                 }
1337
1338
1339                 /**
1340                 * Set page orientation.
1341                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1342                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1343                 * @param float $bottommargin bottom margin of the page.
1344                 * @since 3.0.015 (2008-06-06)
1345                 */
1346                 function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1347                         $orientation = strtoupper($orientation);
1348                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1349                                 $this->CurOrientation = 'P';
1350                                 $this->wPt = $this->fwPt;
1351                                 $this->hPt = $this->fhPt;
1352                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1353                                 $this->CurOrientation = 'L';
1354                                 $this->wPt = $this->fhPt;
1355                                 $this->hPt = $this->fwPt;
1356                         }
1357                         else {
1358                                 $this->Error('Incorrect orientation: '.$orientation);
1359                         }
1360                         $this->w = $this->wPt / $this->k;
1361                         $this->h = $this->hPt / $this->k;
1362                         if (empty($autopagebreak)) {
1363                                 if (isset($this->AutoPageBreak)) {
1364                                         $autopagebreak = $this->AutoPageBreak;
1365                                 } else {
1366                                         $autopagebreak = true;
1367                                 }
1368                         }
1369                         if (empty($bottommargin)) {
1370                                 if (isset($this->bMargin)) {
1371                                         $bottommargin = $this->bMargin;
1372                                 } else {
1373                                         // default value = 2 cm
1374                                         $bottommargin = 2 * 28.35 / $this->k;
1375                                 }
1376                         }
1377                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1378                         // store page dimensions
1379                         $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation);
1380                 }
1381
1382                 /**
1383                  * Enable or disable Right-To-Left language mode
1384                  * @param Boolean $enable if true enable Right-To-Left language mode.
1385                  * @since 2.0.000 (2008-01-03)
1386                  */
1387                 function setRTL($enable) {
1388                         $this->rtl = $enable ? true : false;
1389                         $this->tmprtl = false;
1390                 }
1391
1392                 /**
1393                  * Return the RTL status
1394                  * @return boolean
1395                  * @since 4.0.012 (2008-07-24)
1396                  */
1397                 function getRTL() {
1398                         return $this->rtl;
1399                 }
1400
1401                 /**
1402                 * Force temporary RTL language direction
1403                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1404                 * @since 2.1.000 (2008-01-09)
1405                 */
1406                 function setTempRTL($mode) {
1407                         switch ($mode) {
1408                                 case false:
1409                                 case 'L':
1410                                 case 'R': {
1411                                         $this->tmprtl = $mode;
1412                                 }
1413                         }
1414                 }
1415
1416                 /**
1417                 * Set the last cell height.
1418                 * @param float $h cell height.
1419                 * @author Nicola Asuni
1420                 * @since 1.53.0.TC034
1421                 */
1422                 function setLastH($h) {
1423                         $this->lasth = $h;
1424                 }
1425
1426                 /**
1427                 * Get the last cell height.
1428                 * @return last cell height
1429                 * @since 4.0.017 (2008-08-05)
1430                 */
1431                 function getLastH() {
1432                         return $this->lasth;
1433                 }
1434
1435                 /**
1436                 * Set the image scale.
1437                 * @param float $scale image scale.
1438                 * @author Nicola Asuni
1439                 * @since 1.5.2
1440                 */
1441                 function setImageScale($scale) {
1442                         $this->imgscale = $scale;
1443                 }
1444
1445                 /**
1446                 * Returns the image scale.
1447                 * @return float image scale.
1448                 * @author Nicola Asuni
1449                 * @since 1.5.2
1450                 */
1451                 function getImageScale() {
1452                         return $this->imgscale;
1453                 }
1454
1455                 /**
1456                 * Returns the page width in units.
1457                 * @return int page width.
1458                 * @author Nicola Asuni
1459                 * @since 1.5.2
1460                 */
1461                 function getPageWidth() {
1462                         return $this->w;
1463                 }
1464
1465                 /**
1466                 * Returns the page height in units.
1467                 * @return int page height.
1468                 * @author Nicola Asuni
1469                 * @since 1.5.2
1470                 */
1471                 function getPageHeight() {
1472                         return $this->h;
1473                 }
1474
1475                 /**
1476                 * Returns the page break margin.
1477                 * @return int page break margin.
1478                 * @author Nicola Asuni
1479                 * @since 1.5.2
1480                 */
1481                 function getBreakMargin() {
1482                         return $this->bMargin;
1483                 }
1484
1485                 /**
1486                 * Returns the scale factor (number of points in user unit).
1487                 * @return int scale factor.
1488                 * @author Nicola Asuni
1489                 * @since 1.5.2
1490                 */
1491                 function getScaleFactor() {
1492                         return $this->k;
1493                 }
1494
1495                 /**
1496                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1497                 * @param float $left Left margin.
1498                 * @param float $top Top margin.
1499                 * @param float $right Right margin. Default value is the left one.
1500                 * @since 1.0
1501                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1502                 */
1503                 function SetMargins($left, $top, $right=-1) {
1504                         //Set left, top and right margins
1505                         $this->lMargin = $left;
1506                         $this->tMargin = $top;
1507                         if ($right == -1) {
1508                                 $right = $left;
1509                         }
1510                         $this->rMargin = $right;
1511                 }
1512
1513                 /**
1514                 * 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.
1515                 * @param float $margin The margin.
1516                 * @since 1.4
1517                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1518                 */
1519                 function SetLeftMargin($margin) {
1520                         //Set left margin
1521                         $this->lMargin=$margin;
1522                         if (($this->page > 0) AND ($this->x < $margin)) {
1523                                 $this->x = $margin;
1524                         }
1525                 }
1526
1527                 /**
1528                 * Defines the top margin. The method can be called before creating the first page.
1529                 * @param float $margin The margin.
1530                 * @since 1.5
1531                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1532                 */
1533                 function SetTopMargin($margin) {
1534                         //Set top margin
1535                         $this->tMargin=$margin;
1536                         if (($this->page > 0) AND ($this->y < $margin)) {
1537                                 $this->y = $margin;
1538                         }
1539                 }
1540
1541                 /**
1542                 * Defines the right margin. The method can be called before creating the first page.
1543                 * @param float $margin The margin.
1544                 * @since 1.5
1545                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1546                 */
1547                 function SetRightMargin($margin) {
1548                         $this->rMargin=$margin;
1549                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1550                                 $this->x = $this->w - $margin;
1551                         }
1552                 }
1553
1554                 /**
1555                 * Set the internal Cell padding.
1556                 * @param float $pad internal padding.
1557                 * @since 2.1.000 (2008-01-09)
1558                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1559                 */
1560                 function SetCellPadding($pad) {
1561                         $this->cMargin = $pad;
1562                 }
1563
1564                 /**
1565                 * 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.
1566                 * @param boolean $auto Boolean indicating if mode should be on or off.
1567                 * @param float $margin Distance from the bottom of the page.
1568                 * @since 1.0
1569                 * @see Cell(), MultiCell(), AcceptPageBreak()
1570                 */
1571                 function SetAutoPageBreak($auto, $margin=0) {
1572                         //Set auto page break mode and triggering margin
1573                         $this->AutoPageBreak = $auto;
1574                         $this->bMargin = $margin;
1575                         $this->PageBreakTrigger = $this->h - $margin;
1576                 }
1577
1578                 /**
1579                 * Defines the way the document is to be displayed by the viewer.
1580                 * @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>
1581                 * @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>
1582                 * @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>
1583                 * @since 1.2
1584                 */
1585                 function SetDisplayMode($zoom, $layout='SinglePage', $mode="UseNone") {
1586                         //Set display mode in viewer
1587                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1588                                 $this->ZoomMode = $zoom;
1589                         } else {
1590                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1591                         }
1592                         switch ($layout) {
1593                                 case "default":
1594                                 case "single":
1595                                 case "SinglePage": {
1596                                         $this->LayoutMode = "SinglePage";
1597                                         break;
1598                                 }
1599                                 case "continuous":
1600                                 case "OneColumn": {
1601                                         $this->LayoutMode = "OneColumn";
1602                                         break;
1603                                 }
1604                                 case "two":
1605                                 case "TwoColumnLeft": {
1606                                         $this->LayoutMode = "TwoColumnLeft";
1607                                         break;
1608                                 }
1609                                 case "TwoColumnRight": {
1610                                         $this->LayoutMode = "TwoColumnRight";
1611                                         break;
1612                                 }
1613                                 case "TwoPageLeft": {
1614                                         $this->LayoutMode = "TwoPageLeft";
1615                                         break;
1616                                 }
1617                                 case "TwoPageRight": {
1618                                         $this->LayoutMode = "TwoPageRight";
1619                                         break;
1620                                 }
1621                                 default: {
1622                                         $this->LayoutMode = "SinglePage";
1623                                 }
1624                         }
1625                         switch ($mode) {
1626                                 case "UseNone": {
1627                                         $this->PageMode = "UseNone";
1628                                         break;
1629                                 }
1630                                 case "UseOutlines": {
1631                                         $this->PageMode = "UseOutlines";
1632                                         break;
1633                                 }
1634                                 case "UseThumbs": {
1635                                         $this->PageMode = "UseThumbs";
1636                                         break;
1637                                 }
1638                                 case "FullScreen": {
1639                                         $this->PageMode = "FullScreen";
1640                                         break;
1641                                 }
1642                                 case "UseOC": {
1643                                         $this->PageMode = "UseOC";
1644                                         break;
1645                                 }
1646                                 case "": {
1647                                         $this->PageMode = "UseAttachments";
1648                                         break;
1649                                 }
1650                                 default: {
1651                                         $this->PageMode = "UseNone";
1652                                 }
1653                         }
1654                 }
1655
1656                 /**
1657                 * 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.
1658                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1659                 * @param boolean $compress Boolean indicating if compression must be enabled.
1660                 * @since 1.4
1661                 */
1662                 function SetCompression($compress) {
1663                         //Set page compression
1664                         if (function_exists('gzcompress')) {
1665                                 $this->compress = $compress;
1666                         } else {
1667                                 $this->compress = false;
1668                         }
1669                 }
1670
1671                 /**
1672                 * Defines the title of the document.
1673                 * @param string $title The title.
1674                 * @since 1.2
1675                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1676                 */
1677                 function SetTitle($title) {
1678                         //Title of document
1679                         $this->title = $title;
1680                 }
1681
1682                 /**
1683                 * Defines the subject of the document.
1684                 * @param string $subject The subject.
1685                 * @since 1.2
1686                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1687                 */
1688                 function SetSubject($subject) {
1689                         //Subject of document
1690                         $this->subject = $subject;
1691                 }
1692
1693                 /**
1694                 * Defines the author of the document.
1695                 * @param string $author The name of the author.
1696                 * @since 1.2
1697                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
1698                 */
1699                 function SetAuthor($author) {
1700                         //Author of document
1701                         $this->author = $author;
1702                 }
1703
1704                 /**
1705                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
1706                 * @param string $keywords The list of keywords.
1707                 * @since 1.2
1708                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
1709                 */
1710                 function SetKeywords($keywords) {
1711                         //Keywords of document
1712                         $this->keywords = $keywords;
1713                 }
1714
1715                 /**
1716                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
1717                 * @param string $creator The name of the creator.
1718                 * @since 1.2
1719                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
1720                 */
1721                 function SetCreator($creator) {
1722                         //Creator of document
1723                         $this->creator = $creator;
1724                 }
1725
1726                 /**
1727                 * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
1728                 * @param string $alias The alias. Default value: {nb}.
1729                 * @since 1.4
1730                 * @see getAliasNbPages(), PageNo(), Footer()
1731                 */
1732                 function AliasNbPages($alias='{nb}') {
1733                         //Define an alias for total number of pages
1734                         $this->AliasNbPages = $alias;
1735                 }
1736                 
1737                 /**
1738                  * Returns the string alias used for the total number of pages.
1739          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
1740                  * @return string
1741                  * @since 4.0.018 (2008-08-08)
1742                  * @see AliasNbPages(), PageNo(), Footer()
1743                 */
1744                 function getAliasNbPages() {
1745                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
1746                                 return "{".$this->AliasNbPages."}";
1747             }
1748                         return $this->AliasNbPages;
1749                 }
1750
1751                 /**
1752                 * 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.
1753                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
1754                 * @param string $msg The error message
1755                 * @since 1.0
1756                 */
1757                 function Error($msg) {
1758                         //Fatal error
1759                         display_error('<strong>TCPDF error: </strong>'.$msg);
1760                         exit;
1761                 }
1762
1763                 /**
1764                 * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
1765                 * Note: no page is created by this method
1766                 * @since 1.0
1767                 * @see AddPage(), Close()
1768                 */
1769                 function Open() {
1770                         //Begin document
1771                         $this->state = 1;
1772                 }
1773
1774                 /**
1775                 * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document.
1776                 * @since 1.0
1777                 * @see Open(), Output()
1778                 */
1779                 function Close() {
1780                         //Terminate document
1781                         if ($this->state == 3) {
1782                                 return;
1783                         }
1784                         if ($this->page == 0) {
1785                                 $this->AddPage();
1786                         }
1787                         //Page footer
1788                         $this->setFooter();
1789                         //Close page
1790                         $this->_endpage();
1791                         //Close document
1792                         $this->_enddoc();
1793                 }
1794
1795                 /**
1796                 * Move pointer at the specified document page and update page dimensions.
1797                 * @param int $pnum page number
1798                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
1799                 * @since 2.1.000 (2008-01-07)
1800                 * @see getPage(), lastpage(), getNumPages()
1801                 */
1802                 function setPage($pnum, $resetmargins=false) {
1803                         if (($pnum > 0) AND ($pnum <= count($this->pages))) {
1804                                 $this->page = $pnum;
1805                                 $this->wPt = $this->pagedim[$this->page]['w'];
1806                                 $this->hPt = $this->pagedim[$this->page]['h'];
1807                                 $this->w = $this->wPt / $this->k;
1808                                 $this->h = $this->hPt / $this->k;
1809                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
1810                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
1811                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
1812                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
1813                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
1814                                 if ($resetmargins) {
1815                                         $this->lMargin = $this->pagedim[$this->page]['lm'];
1816                                         $this->rMargin = $this->pagedim[$this->page]['rm'];
1817                                         $this->SetY($this->tMargin);
1818                                 }
1819                         } else {
1820                                 $this->Error('Wrong page number on setPage() function.');
1821                         }
1822                 }
1823
1824                 /**
1825                 * Reset pointer to the last document page.
1826                 * @since 2.0.000 (2008-01-04)
1827                 * @see setPage(), getPage(), getNumPages()
1828                 */
1829                 function lastPage() {
1830                         $this->setPage($this->getNumPages());
1831                 }
1832
1833                 /**
1834                 * Get current document page number.
1835                 * @return int page number
1836                 * @since 2.1.000 (2008-01-07)
1837                 * @see setPage(), lastpage(), getNumPages()
1838                 */
1839                 function getPage() {
1840                         return $this->page;
1841                 }
1842
1843
1844                 /**
1845                 * Get the total number of insered pages.
1846                 * @return int number of pages
1847                 * @since 2.1.000 (2008-01-07)
1848                 * @see setPage(), getPage(), lastpage()
1849                 */
1850                 function getNumPages() {
1851                         return count($this->pages);
1852                 }
1853
1854                 /**
1855                 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header.
1856                 * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width.
1857                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
1858                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1859                 * @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>
1860                 * @since 1.0
1861                 * @see TCPDF(), Header(), Footer(), SetMargins()
1862                 */
1863                 function AddPage($orientation='', $format='') {
1864                         if (!isset($this->original_lMargin)) {
1865                                 $this->original_lMargin = $this->lMargin;
1866                         }
1867                         if (!isset($this->original_rMargin)) {
1868                                 $this->original_rMargin = $this->rMargin;
1869                         }
1870                         if (count($this->pages) > $this->page) {
1871                                 // this page has been already added
1872                                 $this->setPage(($this->page + 1));
1873                                 $this->SetY($this->tMargin);
1874                                 return;
1875                         }
1876                         //Start a new page
1877                         if ($this->state == 0) {
1878                                 $this->Open();
1879                         }
1880                         // save current settings
1881                         $font_family = $this->FontFamily;
1882                         $font_style = $this->FontStyle.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
1883                         $font_size = $this->FontSizePt;
1884                         $prev_rMargin = $this->rMargin;
1885                         $prev_lMargin = $this->lMargin;
1886                         $prev_cMargin = $this->cMargin;
1887                         $prev_linestyleWidth = $this->linestyleWidth;
1888                         $prev_linestyleCap = $this->linestyleCap;
1889                         $prev_linestyleJoin = $this->linestyleJoin;
1890                         $prev_linestyleDash = $this->linestyleDash;
1891                         $prev_DrawColor = $this->DrawColor;
1892                         $prev_FillColor = $this->FillColor;
1893                         $prev_TextColor = $this->TextColor;
1894                         $prev_ColorFlag = $this->ColorFlag;
1895                         if ($this->page > 0) {
1896                                 //Page footer
1897                                 $this->setFooter();
1898                                 //Close page
1899                                 $this->_endpage();
1900                         }
1901                         //Start new page
1902                         $this->_beginpage($orientation, $format);
1903                         // restore graphic styles
1904                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1905                         if (!empty($font_family)) {
1906                                 $this->SetFont($font_family, $font_style, $font_size);
1907                         }
1908                         //Page header
1909                         $this->setHeader();
1910                         // restore graphic styles
1911                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1912                         if (!empty($font_family)) {
1913                                 $this->SetFont($font_family, $font_style, $font_size);
1914                         }
1915                         // restore settings
1916                         $this->FontFamily = $font_family;
1917                         $this->FontStyle = $font_style;
1918                         $this->FontSizePt = $font_size;
1919                         $this->rMargin = $prev_rMargin;
1920                         $this->lMargin = $prev_lMargin;
1921                         $this->cMargin = $prev_cMargin;
1922                         $this->linestyleWidth = $prev_linestyleWidth;
1923                         $this->linestyleCap = $prev_linestyleCap;
1924                         $this->linestyleJoin = $prev_linestyleJoin;
1925                         $this->linestyleDash = $prev_linestyleDash;
1926                         $this->DrawColor = $prev_DrawColor;
1927                         $this->FillColor = $prev_FillColor;
1928                         $this->TextColor = $prev_TextColor;
1929                         $this->ColorFlag = $prev_ColorFlag;
1930                         // mark this point
1931                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1932                 }
1933
1934                 /**
1935                  * Set start-writing mark on current page for multicell borders and fills.
1936                  * This function must be called after calling Image() function for a background image.
1937                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
1938                  * @access public
1939                  * @since 4.0.016 (2008-07-30)
1940                  */
1941                 function setPageMark() {
1942                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1943                 }
1944
1945                 /**
1946                  * Set header data.
1947                  * @param string $ln header image logo
1948                  * @param string $lw header image logo width in mm
1949                  * @param string $ht string to print as title on document header
1950                  * @param string $hs string to print on document header
1951                 */
1952                 function setHeaderData($ln="", $lw=0, $ht="", $hs="") {
1953                         $this->header_logo = $ln;
1954                         $this->header_logo_width = $lw;
1955                         $this->header_title = $ht;
1956                         $this->header_string = $hs;
1957                 }
1958
1959                 /**
1960                  * Returns header data:
1961                  * <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>
1962                  * @return array()
1963                  * @since 4.0.012 (2008-07-24)
1964                  */
1965                 function getHeaderData() {
1966                         $ret = array();
1967                         $ret['logo'] = $this->header_logo;
1968                         $ret['logo_width'] = $this->header_logo_width;
1969                         $ret['title'] = $this->header_title;
1970                         $ret['string'] = $this->header_string;
1971                         return $ret;
1972                 }
1973
1974                 /**
1975                  * Set header margin.
1976                  * (minimum distance between header and top page margin)
1977                  * @param int $hm distance in user units
1978                 */
1979                 function setHeaderMargin($hm=10) {
1980                         $this->header_margin = $hm;
1981                 }
1982
1983                 /**
1984                  * Returns header margin in user units.
1985                  * @return float
1986                  * @since 4.0.012 (2008-07-24)
1987                 */
1988                 function getHeaderMargin() {
1989                         return $this->header_margin;
1990                 }
1991
1992                 /**
1993                  * Set footer margin.
1994                  * (minimum distance between footer and bottom page margin)
1995                  * @param int $fm distance in user units
1996                 */
1997                 function setFooterMargin($fm=10) {
1998                         $this->footer_margin = $fm;
1999                 }
2000
2001                 /**
2002                  * Returns footer margin in user units.
2003                  * @return float
2004                  * @since 4.0.012 (2008-07-24)
2005                 */
2006                 function getFooterMargin() {
2007                         return $this->footer_margin;
2008                 }
2009                 /**
2010                  * Set a flag to print page header.
2011                  * @param boolean $val set to true to print the page header (default), false otherwise.
2012                  */
2013                 function setPrintHeader($val=true) {
2014                         $this->print_header = $val;
2015                 }
2016
2017                 /**
2018                  * Set a flag to print page footer.
2019                  * @param boolean $value set to true to print the page footer (default), false otherwise.
2020                  */
2021                 function setPrintFooter($val=true) {
2022                         $this->print_footer = $val;
2023                 }
2024
2025                 /**
2026                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2027                  * @return float
2028                  */
2029                 function getImageRBX() {
2030                         return $this->img_rb_x;
2031                 }
2032
2033                 /**
2034                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2035                  * @return float
2036                  */
2037                 function getImageRBY() {
2038                         return $this->img_rb_y;
2039                 }
2040
2041                 /**
2042                  * This method is used to render the page header.
2043                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2044                  */
2045                 function Header1() {
2046                         $ormargins = $this->getOriginalMargins();
2047                         $headerfont = $this->getHeaderFont();
2048                         $headerdata = $this->getHeaderData();
2049                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2050                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2051                                 $imgy = $this->getImageRBY();
2052                         } else {
2053                                 $imgy = $this->GetY();
2054                         }
2055                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2056                         // set starting margin for text data cell
2057                         if ($this->getRTL()) {
2058                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2059                         } else {
2060                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2061                         }
2062                         $this->SetTextColor(0, 0, 0);
2063                         // header title
2064                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2065                         $this->SetX($header_x);
2066                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '');
2067                         // header string
2068                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2069                         $this->SetX($header_x);
2070                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, 0, 0, true, 0);
2071                         // print an ending header line
2072                         $this->SetLineStyle(array("width" => 0.85 / $this->getScaleFactor(), "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2073                         $this->SetY(1 + max($imgy, $this->GetY()));
2074                         if ($this->getRTL()) {
2075                                 $this->SetX($ormargins['right']);
2076                         } else {
2077                                 $this->SetX($ormargins['left']);
2078                         }
2079                         $this->Cell(0, 0, '', 'T', 0, 'C');
2080                 }
2081
2082                 /**
2083                  * This method is used to render the page footer.
2084                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2085                  */
2086                 function Footer() {
2087                         $cur_y = $this->GetY();
2088                         $ormargins = $this->getOriginalMargins();
2089                         $this->SetTextColor(0, 0, 0);
2090                         //set style for cell border
2091                         $line_width = 0.85 / $this->getScaleFactor();
2092                         $this->SetLineStyle(array("width" => $line_width, "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2093                         //print document barcode
2094                         $barcode = $this->getBarcode();
2095                         if (!empty($barcode)) {
2096                                 $this->Ln();
2097                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2098                                 $this->write1DBarcode($barcode, "C128B", $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2099                         }
2100                         $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / '.$this->getAliasNbPages();
2101                         $this->SetY($cur_y);
2102                         //Print page number
2103                         if ($this->getRTL()) {
2104                                 $this->SetX($ormargins['right']);
2105                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2106                         } else {
2107                                 $this->SetX($ormargins['left']);
2108                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2109                         }
2110                 }
2111
2112                 /**
2113                  * This method is used to render the page header.
2114                  * @access protected
2115                  * @since 4.0.012 (2008-07-24)
2116                  */
2117                 function setHeader() {
2118                         if ($this->print_header) {
2119                                 $lasth = $this->lasth;
2120                                 $this->_out("q");
2121                                 $this->rMargin = $this->original_rMargin;
2122                                 $this->lMargin = $this->original_lMargin;
2123                                 //set current position
2124                                 if ($this->rtl) {
2125                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2126                                 } else {
2127                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2128                                 }
2129                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2130                                 $this->Header1();
2131                                 //restore position
2132                                 if ($this->rtl) {
2133                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2134                                 } else {
2135                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2136                                 }
2137                                 $this->_out("Q");
2138                                 $this->lasth = $lasth;
2139                         }
2140                 }
2141
2142                 /**
2143                  * This method is used to render the page footer.
2144                  * @access protected
2145                  * @since 4.0.012 (2008-07-24)
2146                  */
2147                 function setFooter() {
2148                         //Page footer
2149                         $this->InFooter = true;
2150                         // mark this point
2151                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
2152                         if ($this->print_footer) {
2153                                 $lasth = $this->lasth;
2154                                 $this->_out("q");
2155                                 $this->rMargin = $this->original_rMargin;
2156                                 $this->lMargin = $this->original_lMargin;
2157                                 //set current position
2158                                 $footer_y = $this->h - $this->footer_margin;
2159                                 if ($this->rtl) {
2160                                         $this->SetXY($this->original_rMargin, $footer_y);
2161                                 } else {
2162                                         $this->SetXY($this->original_lMargin, $footer_y);
2163                                 }
2164                                 $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]);
2165                                 $this->Footer();
2166                                 //restore position
2167                                 if ($this->rtl) {
2168                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2169                                 } else {
2170                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2171                                 }
2172                                 $this->_out("Q");
2173                                 $this->lasth = $lasth;
2174                         }
2175                         $this->footerlen[$this->page] = strlen($this->pages[$this->page]) - $this->footerpos[$this->page];
2176                         $this->InFooter = false;
2177                 }
2178
2179                 /**
2180                 * Returns the current page number.
2181                 * @return int page number
2182                 * @since 1.0
2183                 * @see AliasNbPages(), getAliasNbPages()
2184                 */
2185                 function PageNo() {
2186                         return $this->page;
2187                 }
2188
2189                 /**
2190                 * Defines a new spot color. 
2191                 * It can be expressed in RGB components or gray scale. 
2192                 * The method can be called before the first page is created and the value is retained from page to page.
2193                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2194                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2195                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2196                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2197                 * @since 4.0.024 (2008-09-12)
2198                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2199                 */
2200                 function AddSpotColor($name, $c, $m, $y, $k) {
2201                         if (!isset($this->spot_colors[$name])) {
2202                                 $i = 1 + count($this->spot_colors);
2203                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2204                         }
2205                 }
2206
2207                 /**
2208                 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
2209                 * It can be expressed in RGB components or gray scale.
2210                 * The method can be called before the first page is created and the value is retained from page to page.
2211                 * @param array $color array of colors
2212                 * @since 3.1.000 (2008-06-11)
2213                 * @see SetDrawColor()
2214                 */
2215                 function SetDrawColorArray($color) {
2216                         if (isset($color)) {
2217                                 $color = array_values($color);
2218                                 $r = isset($color[0]) ? $color[0] : -1;
2219                                 $g = isset($color[1]) ? $color[1] : -1;
2220                                 $b = isset($color[2]) ? $color[2] : -1;
2221                                 $k = isset($color[3]) ? $color[3] : -1;
2222                                 if ($r >= 0) {
2223                                         $this->SetDrawColor($r, $g, $b, $k);
2224                                 }
2225                         }
2226                 }
2227
2228                 /**
2229                 * 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.
2230                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2231                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2232                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2233                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2234                 * @since 1.3
2235                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2236                 */
2237                 function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2238                         // set default values
2239                         if (!is_numeric($col1)) {
2240                                 $col1 = 0;
2241                         }
2242                         if (!is_numeric($col2)) {
2243                                 $col2 = -1;
2244                         }
2245                         if (!is_numeric($col3)) {
2246                                 $col3 = -1;
2247                         }
2248                         if (!is_numeric($col4)) {
2249                                 $col4 = -1;
2250                         }
2251                         //Set color for all stroking operations
2252                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2253                                 // Grey scale
2254                                 $this->DrawColor = sprintf('%.3f G', $col1/255);
2255                         } elseif ($col4 == -1) {
2256                                 // RGB
2257                                 $this->DrawColor = sprintf('%.3f %.3f %.3f RG', $col1/255, $col2/255, $col3/255);
2258                         } else {
2259                                 // CMYK
2260                                 $this->DrawColor = sprintf('%.3f %.3f %.3f %.3f K', $col1/100, $col2/100, $col3/100, $col4/100);
2261                         }
2262                         if ($this->page > 0) {
2263                                 $this->_out($this->DrawColor);
2264                         }
2265                 }
2266                 
2267                 /**
2268                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2269                 * @param string $name name of the spot color
2270                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2271                 * @since 4.0.024 (2008-09-12)
2272                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2273                 */
2274                 function SetDrawSpotColor($name, $tint=100) {
2275                         if (!isset($this->spot_colors[$name])) {
2276                                 $this->Error('Undefined spot color: '.$name);
2277                         }
2278                         $this->DrawColor = sprintf('/CS%d CS %.3f SCN', $this->spot_colors[$name]['i'], $tint/100);
2279                         if ($this->page > 0) {
2280                                 $this->_out($this->DrawColor);
2281                         }
2282                 }
2283
2284                 /**
2285                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
2286                 * It can be expressed in RGB components or gray scale.
2287                 * The method can be called before the first page is created and the value is retained from page to page.
2288                 * @param array $color array of colors
2289                 * @since 3.1.000 (2008-6-11)
2290                 * @see SetFillColor()
2291                 */
2292                 function SetFillColorArray($color) {
2293                         if (isset($color)) {
2294                                 $color = array_values($color);
2295                                 $r = isset($color[0]) ? $color[0] : -1;
2296                                 $g = isset($color[1]) ? $color[1] : -1;
2297                                 $b = isset($color[2]) ? $color[2] : -1;
2298                                 $k = isset($color[3]) ? $color[3] : -1;
2299                                 if ($r >= 0) {
2300                                         $this->SetFillColor($r, $g, $b, $k);
2301                                 }
2302                         }
2303                 }
2304
2305                 /**
2306                 * 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.
2307                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2308                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2309                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2310                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2311                 * @since 1.3
2312                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2313                 */
2314                 function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2315                         // set default values
2316                         if (!is_numeric($col1)) {
2317                                 $col1 = 0;
2318                         }
2319                         if (!is_numeric($col2)) {
2320                                 $col2 = -1;
2321                         }
2322                         if (!is_numeric($col3)) {
2323                                 $col3 = -1;
2324                         }
2325                         if (!is_numeric($col4)) {
2326                                 $col4 = -1;
2327                         }
2328                         //Set color for all filling operations
2329                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2330                                 // Grey scale
2331                                 $this->FillColor = sprintf('%.3f g', $col1/255);
2332                                 $this->bgcolor = array('G' => $col1);
2333                         } elseif ($col4 == -1) {
2334                                 // RGB
2335                                 $this->FillColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2336                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2337                         } else {
2338                                 // CMYK
2339                                 $this->FillColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2340                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2341                         }
2342                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2343                         if ($this->page > 0) {
2344                                 $this->_out($this->FillColor);
2345                         }
2346                 }
2347                 
2348                 /**
2349                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2350                 * @param string $name name of the spot color
2351                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2352                 * @since 4.0.024 (2008-09-12)
2353                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2354                 */
2355                 function SetFillSpotColor($name, $tint=100) {
2356                         if (!isset($this->spot_colors[$name])) {
2357                                 $this->Error('Undefined spot color: '.$name);
2358                         }
2359                         $this->FillColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2360                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2361                         if ($this->page > 0) {
2362                                 $this->_out($this->FillColor);
2363                         }
2364                 }
2365
2366                 /**
2367                 * Defines the color used for text. It can be expressed in RGB components or gray scale.
2368                 * The method can be called before the first page is created and the value is retained from page to page.
2369                 * @param array $color array of colors
2370                 * @since 3.1.000 (2008-6-11)
2371                 * @see SetFillColor()
2372                 */
2373                 function SetTextColorArray($color) {
2374                         if (isset($color)) {
2375                                 $color = array_values($color);
2376                                 $r = isset($color[0]) ? $color[0] : -1;
2377                                 $g = isset($color[1]) ? $color[1] : -1;
2378                                 $b = isset($color[2]) ? $color[2] : -1;
2379                                 $k = isset($color[3]) ? $color[3] : -1;
2380                                 if ($r >= 0) {
2381                                         $this->SetTextColor($r, $g, $b, $k);
2382                                 }
2383                         }
2384                 }
2385
2386                 /**
2387                 * 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.
2388                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2389                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2390                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2391                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2392                 * @since 1.3
2393                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2394                 */
2395                 function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2396                         // set default values
2397                         if (!is_numeric($col1)) {
2398                                 $col1 = 0;
2399                         }
2400                         if (!is_numeric($col2)) {
2401                                 $col2 = -1;
2402                         }
2403                         if (!is_numeric($col3)) {
2404                                 $col3 = -1;
2405                         }
2406                         if (!is_numeric($col4)) {
2407                                 $col4 = -1;
2408                         }
2409                         //Set color for text
2410                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2411                                 // Grey scale
2412                                 $this->TextColor = sprintf('%.3f g', $col1/255);
2413                                 $this->fgcolor = array('G' => $col1);
2414                         } elseif ($col4 == -1) {
2415                                 // RGB
2416                                 $this->TextColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2417                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2418                         } else {
2419                                 // CMYK
2420                                 $this->TextColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2421                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2422                         }
2423                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2424                 }
2425
2426                 /**
2427                 * Defines the spot color used for text.
2428                 * @param string $name name of the spot color
2429                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2430                 * @since 4.0.024 (2008-09-12)
2431                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2432                 */
2433                 function SetTextSpotColor($name, $tint=100) {
2434                         if (!isset($this->spot_colors[$name])) {
2435                                 $this->Error('Undefined spot color: '.$name);
2436                         }
2437                         $this->TextColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2438                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2439                         if ($this->page > 0) {
2440                                 $this->_out($this->TextColor);
2441                         }
2442                 }
2443
2444                 /**
2445                 * Returns the length of a string in user unit. A font must be selected.<br>
2446                 * @param string $s The string whose length is to be computed
2447                 * @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.
2448                 * @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.
2449                 * @param float $fontsize Font size in points. The default value is the current size.
2450                 * @return int string length
2451                 * @author Nicola Asuni
2452                 * @since 1.2
2453                 */
2454                 function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2455                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $this->tmprtl), $fontname, $fontstyle, $fontsize);
2456                 }
2457
2458                 /**
2459                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2460                 * @param string $arr The array of chars whose total length is to be computed
2461                 * @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.
2462                 * @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.
2463                 * @param float $fontsize Font size in points. The default value is the current size.
2464                 * @return int string length
2465                 * @author Nicola Asuni
2466                 * @since 2.4.000 (2008-03-06)
2467                 */
2468                 function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2469                         // store current values
2470                         if (!empty($fontname)) {
2471                                 $prev_FontFamily = $this->FontFamily;
2472                                 $prev_FontStyle = $this->FontStyle;
2473                                 $prev_FontSizePt = $this->FontSizePt;
2474                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2475                         }
2476                         $w = 0;
2477                         foreach($sa as $char) {
2478                                 $w += $this->GetCharWidth($char);
2479                         }
2480                         // restore previous values
2481                         if (!empty($fontname)) {
2482                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2483                         }
2484                         return $w;
2485                 }
2486
2487                 /**
2488                 * Returns the length of the char in user unit for the current font.<br>
2489                 * @param int $char The char code whose length is to be returned
2490                 * @return int char width
2491                 * @author Nicola Asuni
2492                 * @since 2.4.000 (2008-03-06)
2493                 */
2494                 function GetCharWidth($char) {
2495                         $cw = &$this->CurrentFont['cw'];
2496                         if (isset($cw[$char])) {
2497                                 $w = $cw[$char];
2498                                 /*
2499                         } elseif (isset($cw[ord($char)])) {
2500                                 $w = $cw[ord($char)];
2501                         } elseif (isset($cw[chr($char)])) {
2502                                 $w = $cw[chr($char)];
2503                                 */
2504                         } elseif (isset($this->CurrentFont['dw'])) {
2505                                 $w = $this->CurrentFont['dw'];
2506                         } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
2507                                 $w = $this->CurrentFont['desc']['MissingWidth']; // set default size
2508                         } else {
2509                                 $w = 500; // default width
2510                         }
2511                         return ($w * $this->FontSize / 1000);
2512                 }
2513
2514                 /**
2515                 * Returns the numbero of characters in a string.
2516                 * @param string $s The input string.
2517                 * @return int number of characters
2518                 * @since 2.0.0001 (2008-01-07)
2519                 */
2520                 function GetNumChars($s) {
2521                         if ($this->isunicode) {
2522                                 return count($this->UTF8StringToArray($s));
2523                         }
2524                         return strlen($s);
2525                 }
2526
2527                 /**
2528                 * Fill the list of available fonts ($this->fontlist).
2529                 * @access protected
2530                 * @since 4.0.013 (2008-07-28)
2531                 */
2532                 function getFontsList() {
2533                         $fontsdir = opendir($this->_getfontpath());
2534                         while (($file = readdir($fontsdir)) !== false) {
2535                                 if (substr($file, -4) == ".php") {
2536                                                 array_push($this->fontlist, strtolower(basename($file, ".php")));
2537                                 }
2538                         }
2539                         closedir($fontsdir);
2540                 }
2541
2542                 /**
2543                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2544                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
2545                 * 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.
2546                 * Changed to support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
2547                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2548                 * @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>
2549                 * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space.
2550                 * @return array containing the font data, or false in case of error.
2551                 * @since 1.5
2552                 * @see SetFont()
2553                 */
2554                 function AddFont($family, $style='', $file='') {
2555                         if (empty($family)) {
2556                                 if (!empty($this->FontFamily)) {
2557                                         $family = $this->FontFamily;
2558                                 } else {
2559                                         $this->Error('Empty font family');
2560                                 }
2561                         }
2562                         $family = strtolower($family);
2563                         if ((!$this->isunicode) AND ($family == 'arial')) {
2564                                 $family = 'helvetica';
2565                         }
2566                         if (($family == "symbol") OR ($family == "zapfdingbats")) {
2567                                 $style = '';
2568                         }
2569                         $style = strtoupper($style);
2570                         // underline
2571                         if (strpos($style,'U') !== false) {
2572                                 $this->underline = true;
2573                                 $style = str_replace('U', '', $style);
2574                         } else {
2575                                 $this->underline = false;
2576                         }
2577                         //line through (deleted)
2578                         if (strpos($style,'D') !== false) {
2579                                 $this->linethrough = true;
2580                                 $style = str_replace('D', '', $style);
2581                         } else {
2582                                 $this->linethrough = false;
2583                         }
2584                         if ($style == 'IB') {
2585                                 $style = 'BI';
2586                         }
2587                         $fontkey = $family.$style;
2588                         $fontdata = array("fontkey" => $fontkey, "family" => $family, "style" => $style);
2589                         // check if the font has been already added
2590                         if (isset($this->fonts[$fontkey])) {
2591                                 return $fontdata;
2592                         }
2593                         if ($file == '') {
2594                                 $file = str_replace(' ', '', $family).strtolower($style).'.php';
2595                         }
2596                         if (!file_exists($this->_getfontpath().$file)) {
2597                                 // try to load the basic file without styles
2598                                 $file = str_replace(' ', '', $family).'.php';
2599                         }
2600                         if (isset($type)) {
2601                                 unset($type);
2602                         }
2603                         if (isset($cw)) {
2604                                 unset($cw);
2605                         }
2606                         @include($this->_getfontpath().$file);
2607                         if ((!isset($type)) OR (!isset($cw))) {
2608                                 $this->Error("Could not include font definition file: ".$file);
2609                         }
2610                         $i = count($this->fonts) + 1;
2611                         // register CID font (all styles at once)
2612                         if ($type == 'cidfont0') {
2613                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
2614                                 foreach ($styles as $skey => $qual) {
2615                                         $sname = $name.$qual;
2616                                         $sfontkey = $family.$skey;
2617                                         $this->fonts[$sfontkey] = array('i' => $i, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc);
2618                                         $i = count($this->fonts) + 1;
2619                                 }
2620                                 $file = '';
2621                         } elseif ($type == 'core') {
2622                                 $def_width = $cw[ord('?')];
2623                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $def_width);
2624                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
2625                                 if (!isset($file)) {
2626                                         $file = '';
2627                                 }
2628                                 if (!isset($enc)) {
2629                                         $enc = '';
2630                                 }
2631                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'file' => $file, 'enc' => $enc, 'desc' => $desc);
2632                         } elseif ($type == 'TrueTypeUnicode') {
2633                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg);
2634                         } else {
2635                                 $this->Error('Unknow font type');
2636                         }
2637                         if (isset($diff) AND (!empty($diff))) {
2638                                 //Search existing encodings
2639                                 $d = 0;
2640                                 $nb = count($this->diffs);
2641                                 for($i=1; $i <= $nb; $i++) {
2642                                         if ($this->diffs[$i] == $diff) {
2643                                                 $d = $i;
2644                                                 break;
2645                                         }
2646                                 }
2647                                 if ($d == 0) {
2648                                         $d = $nb + 1;
2649                                         $this->diffs[$d] = $diff;
2650                                 }
2651                                 $this->fonts[$fontkey]['diff'] = $d;
2652                         }
2653                         if (!empty($file)) {
2654                                 if ((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) {
2655                                         $this->FontFiles[$file] = array('length1' => $originalsize);
2656                                 } elseif ($type != 'core') {
2657                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2);
2658                                 }
2659                         }
2660                         return $fontdata;
2661                 }
2662
2663                 /**
2664                 * Sets the font used to print character strings.
2665                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
2666                 * The method can be called before the first page is created and the font is retained from page to page.
2667                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
2668                 * 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 />
2669                 * @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.
2670                 * @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.
2671                 * @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
2672                 * @since 1.0
2673                 * @see AddFont(), SetFontSize()
2674                 */
2675                 function SetFont($family, $style='', $size=0) {
2676                         //Select a font; size given in points
2677                         if ($size == 0) {
2678                                 $size = $this->FontSizePt;
2679                         }
2680                         // try to add font (if not already added)
2681                         $fontdata =  $this->AddFont($family, $style);
2682                         $this->FontFamily = $fontdata['family'];
2683                         $this->FontStyle = $fontdata['style'];
2684                         $this->CurrentFont = &$this->fonts[$fontdata['fontkey']];
2685                         $this->SetFontSize($size);
2686                 }
2687
2688                 /**
2689                 * Defines the size of the current font.
2690                 * @param float $size The size (in points)
2691                 * @since 1.0
2692                 * @see SetFont()
2693                 */
2694                 function SetFontSize($size) {
2695                         //Set font size in points
2696                         $this->FontSizePt = $size;
2697                         $this->FontSize = $size / $this->k;
2698                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
2699                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
2700                         } else {
2701                                 $this->FontAscent = 0.8 * $this->FontSize;
2702                         }
2703                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
2704                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
2705                         } else {
2706                                 $this->FontDescent = 0.2 * $this->FontSize;
2707                         }
2708                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
2709                                 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2710                         }
2711                 }
2712
2713                 /**
2714                 * 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 />
2715                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
2716                 * @since 1.5
2717                 * @see Cell(), Write(), Image(), Link(), SetLink()
2718                 */
2719                 function AddLink() {
2720                         //Create a new internal link
2721                         $n = count($this->links) + 1;
2722                         $this->links[$n] = array(0, 0);
2723                         return $n;
2724                 }
2725
2726                 /**
2727                 * Defines the page and position a link points to.
2728                 * @param int $link The link identifier returned by AddLink()
2729                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
2730                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
2731                 * @since 1.5
2732                 * @see AddLink()
2733                 */
2734                 function SetLink($link, $y=0, $page=-1) {
2735                         if ($y == -1) {
2736                                 $y = $this->y;
2737                         }
2738                         if ($page == -1) {
2739                                 $page = $this->page;
2740                         }
2741                         $this->links[$link] = array($page, $y);
2742                 }
2743
2744                 /**
2745                 * Puts a link on a rectangular area of the page.
2746                 * 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.
2747                 * @param float $x Abscissa of the upper-left corner of the rectangle
2748                 * @param float $y Ordinate of the upper-left corner of the rectangle
2749                 * @param float $w Width of the rectangle
2750                 * @param float $h Height of the rectangle
2751                 * @param mixed $link URL or identifier returned by AddLink()
2752                 * @since 1.5
2753                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
2754                 */
2755                 function Link($x, $y, $w, $h, $link) {
2756                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'));
2757                 }
2758                 
2759                 /**
2760                 * Puts a text annotation on a rectangular area of the page.
2761                 * !!!! THIS FUNCTION IS NOT YET FULLY IMPLEMENTED !!!!
2762                 * @param float $x Abscissa of the upper-left corner of the rectangle
2763                 * @param float $y Ordinate of the upper-left corner of the rectangle
2764                 * @param float $w Width of the rectangle
2765                 * @param float $h Height of the rectangle
2766                 * @param string $text annotation text
2767                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
2768                 * @since 4.0.018 (2008-08-06)
2769                 */
2770                 function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text')) {
2771                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt);
2772                 }
2773
2774                 /**
2775                 * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text.
2776                 * @param float $x Abscissa of the origin
2777                 * @param float $y Ordinate of the origin
2778                 * @param string $txt String to print
2779                 * @param int $stroke outline size in points (0 = disable)
2780                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
2781                 * @since 1.0
2782                 * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
2783                 */
2784                 function Text($x, $y, $txt, $stroke=0, $clip=false) {
2785                         //Output a string
2786                         if ($this->rtl) {
2787                                 // bidirectional algorithm (some chars may be changed affecting the line length)
2788                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $this->tmprtl);
2789                                 $l = $this->GetArrStringWidth($s);
2790                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
2791                         } else {
2792                                 $xr = $x;
2793                         }
2794                         $opt = "";
2795                         if (($stroke > 0) AND (!$clip)) {
2796                                 $opt .= "1 Tr ".intval($stroke)." w ";
2797                         } elseif (($stroke > 0) AND $clip) {
2798                                 $opt .= "5 Tr ".intval($stroke)." w ";
2799                         } elseif ($clip) {
2800                                 $opt .= "7 Tr ";
2801                         }
2802                         $s = sprintf('BT %.2f %.2f Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
2803                         if ($this->underline AND ($txt!='')) {
2804                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
2805                         }
2806                         if ($this->linethrough AND ($txt!='')) {
2807                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt);
2808                         }
2809                         if ($this->ColorFlag AND (!$clip)) {
2810                                 $s='q '.$this->TextColor.' '.$s.' Q';
2811                         }
2812                         $this->_out($s);
2813                 }
2814
2815                 /**
2816                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
2817                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
2818                 * This method is called automatically and should not be called directly by the application.
2819                 * @return boolean
2820                 * @since 1.4
2821                 * @see SetAutoPageBreak()
2822                 */
2823                 function AcceptPageBreak() {
2824                         return $this->AutoPageBreak;
2825                 }
2826
2827                 /**
2828                 * Add page if needed.
2829                 * @param float $h Cell height. Default value: 0.
2830                 * @since 3.2.000 (2008-07-01)
2831                 * @access protected
2832                 */
2833                 function checkPageBreak($h) {
2834                         if ((($this->y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
2835                                 $rs = "";
2836                                 //Automatic page break
2837                                 $x = $this->x;
2838                                 $ws = $this->ws;
2839                                 if ($ws > 0) {
2840                                         $this->ws = 0;
2841                                         $rs .= '0 Tw';
2842                                 }
2843                                 $this->AddPage($this->CurOrientation);
2844                                 if ($ws > 0) {
2845                                         $this->ws = $ws;
2846                                         $rs .= sprintf('%.3f Tw', $ws * $k);
2847                                 }
2848                                 $this->_out($rs);
2849                                 $this->y = $this->tMargin;
2850                                 $this->x = $x;
2851                         }
2852                 }
2853
2854                 /**
2855                 * 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 />
2856                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2857                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2858                 * @param float $h Cell height. Default value: 0.
2859                 * @param string $txt String to print. Default value: empty string.
2860                 * @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>
2861                 * @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>
2862                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2863                 * @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>
2864                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2865                 * @param mixed $link URL or identifier returned by AddLink().
2866                 * @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>
2867                 * @since 1.0
2868                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
2869                 */
2870                 function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2871                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
2872                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2873                         if ($h < $min_cell_height) {
2874                                 $h = $min_cell_height;
2875                         }
2876                         $this->checkPageBreak($h);
2877                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch));
2878                 }
2879
2880                 /**
2881                 * 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 />
2882                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2883                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2884                 * @param float $h Cell height. Default value: 0.
2885                 * @param string $txt String to print. Default value: empty string.
2886                 * @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>
2887                 * @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.
2888                 * @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>
2889                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2890                 * @param mixed $link URL or identifier returned by AddLink().
2891                 * @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>
2892                 * @since 1.0
2893                 * @see Cell()
2894                 */
2895                 function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2896                         $rs = ""; //string to be returned
2897                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2898                         if ($h < $min_cell_height) {
2899                                 $h = $min_cell_height;
2900                         }
2901                         $k = $this->k;
2902                         if (empty($w) OR ($w <= 0)) {
2903                                 if ($this->rtl) {
2904                                         $w = $this->x - $this->lMargin;
2905                                 } else {
2906                                         $w = $this->w - $this->rMargin - $this->x;
2907                                 }
2908                         }
2909                         $s = '';
2910                         if (($fill == 1) OR ($border == 1)) {
2911                                 if ($fill == 1) {
2912                                         $op = ($border == 1) ? 'B' : 'f';
2913                                 } else {
2914                                         $op = 'S';
2915                                 }
2916                                 if ($this->rtl) {
2917                                         $xk = (($this->x  - $w) * $k);
2918                                 } else {
2919                                         $xk = ($this->x * $k);
2920                                 }
2921                                 $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
2922                         }
2923
2924                         if (is_string($border)) {
2925                                 $x = $this->x;
2926                                 $y = $this->y;
2927                                 if (strpos($border,'L') !== false) {
2928                                         if ($this->rtl) {
2929                                                 $xk = ($x - $w) * $k;
2930                                         } else {
2931                                                 $xk = $x * $k;
2932                                         }
2933                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h)) * $k));
2934                                 }
2935                                 if (strpos($border,'T') !== false) {
2936                                         if ($this->rtl) {
2937                                                 $xk = ($x - $w) * $k;
2938                                                 $xwk = $x * $k;
2939                                         } else {
2940                                                 $xk = $x * $k;
2941                                                 $xwk = ($x + $w) * $k;
2942                                         }
2943                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
2944                                 }
2945                                 if (strpos($border,'R') !== false) {
2946                                         if ($this->rtl) {
2947                                                 $xk = $x * $k;
2948                                         } else {
2949                                                 $xk = ($x + $w) * $k;
2950                                         }
2951                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h))* $k));
2952                                 }
2953                                 if (strpos($border,'B') !== false) {
2954                                         if ($this->rtl) {
2955                                                 $xk = ($x - $w) * $k;
2956                                                 $xwk = $x * $k;
2957                                         } else {
2958                                                 $xk = $x * $k;
2959                                                 $xwk = ($x + $w) * $k;
2960                                         }
2961                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
2962                                 }
2963                         }
2964                         if ($txt != '') {
2965                                 // text lenght
2966                                 $width = $this->GetStringWidth($txt);
2967                                 // ratio between cell lenght and text lenght
2968                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
2969
2970                                 // stretch text if required
2971                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
2972                                         if ($stretch > 2) {
2973                                                 // spacing
2974                                                 //Calculate character spacing in points
2975                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
2976                                                 //Set character spacing
2977                                                 $rs .= sprintf('BT %.2f Tc ET ', $char_space);
2978                                         } else {
2979                                                 // scaling
2980                                                 //Calculate horizontal scaling
2981                                                 $horiz_scale = $ratio * 100.0;
2982                                                 //Set horizontal scaling
2983                                                 $rs .= sprintf('BT %.2f Tz ET ', $horiz_scale);
2984                                         }
2985                                         $align = '';
2986                                         $width = $w - (2 * $this->cMargin);
2987                                 } else {
2988                                         $stretch == 0;
2989                                 }
2990                                 if ($align == 'L') {
2991                                         if ($this->rtl) {
2992                                                 $dx = $w - $width - $this->cMargin;
2993                                         } else {
2994                                                 $dx = $this->cMargin;
2995                                         }
2996                                 } elseif ($align == 'R') {
2997                                         if ($this->rtl) {
2998                                                 $dx = $this->cMargin;
2999                                         } else {
3000                                                 $dx = $w - $width - $this->cMargin;
3001                                         }
3002                                 } elseif ($align == 'C') {
3003                                         $dx = ($w - $width) / 2;
3004                                 } elseif ($align == 'J') {
3005                                         if ($this->rtl) {
3006                                                 $dx = $w - $width - $this->cMargin;
3007                                         } else {
3008                                                 $dx = $this->cMargin;
3009                                         }
3010                                 } else {
3011                                         $dx = $this->cMargin;
3012                                 }
3013                                 if ($this->ColorFlag) {
3014                                         $s .= 'q '.$this->TextColor.' ';
3015                                 }
3016                                 $txt2 = $this->_escapetext($txt);
3017                                 if ($this->rtl) {
3018                                         $xdk = ($this->x - $dx - $width) * $k;
3019                                 } else {
3020                                         $xdk = ($this->x + $dx) * $k;
3021                                 }
3022                                 // Justification
3023                                 if ($align == 'J') {
3024                                         // count number of spaces
3025                                         $ns = substr_count($txt, ' ');
3026                                         //if ($this->isunicode) {
3027                                         if (($this->CurrentFont['type'] == "TrueTypeUnicode") OR ($this->CurrentFont['type'] == "cidfont0")) {
3028                                                 // get string width without spaces
3029                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3030                                                 // calculate average space width
3031                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3032                                                 // set word position to be used with TJ operator
3033                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3034                                         } else {
3035                                                 // get string width
3036                                                 $width = $this->GetStringWidth($txt);
3037                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3038                                                 $rs .= sprintf('BT %.3f Tw ET ', $spacewidth);
3039                                         }
3040                                 }
3041                                 // calculate approximate position of the font base line
3042                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3043                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3044                                 // print text
3045                                 $s .= sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3046                                 if ($this->rtl) {
3047                                         $xdx = $this->x - $dx - $width;
3048                                 } else {
3049                                         $xdx = $this->x + $dx;
3050                                 }
3051                                 if ($this->underline)  {
3052                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3053                                 }
3054                                 if ($this->linethrough) {
3055                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3056                                 }
3057                                 if ($this->ColorFlag) {
3058                                         $s .= ' Q';
3059                                 }
3060                                 if ($link) {
3061                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link);
3062                                 }
3063                         }
3064                         // output cell
3065                         if ($s) {
3066                                 // output cell
3067                                 $rs .= $s;
3068                                 // reset text stretching
3069                                 if ($stretch > 2) {
3070                                         //Reset character horizontal spacing
3071                                         $rs .= ' BT 0 Tc ET';
3072                                 } elseif ($stretch > 0) {
3073                                         //Reset character horizontal scaling
3074                                         $rs .= ' BT 100 Tz ET';
3075                                 }
3076                         }
3077                         // reset word spacing
3078                         if ((!$this->isunicode) AND ($align == 'J')) {
3079                                 $rs .= ' BT 0 Tw ET';
3080                         }
3081                         $this->lasth = $h;
3082                         if ($ln > 0) {
3083                                 //Go to the beginning of the next line
3084                                 $this->y += $h;
3085                                 if ($ln == 1) {
3086                                         if ($this->rtl) {
3087                                                 $this->x = $this->w - $this->rMargin;
3088                                         } else {
3089                                                 $this->x = $this->lMargin;
3090                                         }
3091                                 }
3092                         } else {
3093                                 // go left or right by case
3094                                 if ($this->rtl) {
3095                                         $this->x -= $w;
3096                                 } else {
3097                                         $this->x += $w;
3098                                 }
3099                         }
3100                         $gstyles = $this->linestyleWidth." ".$this->linestyleCap." ".$this->linestyleJoin." ".$this->linestyleDash." ".$this->DrawColor." ".$this->FillColor."\n";
3101                         $rs = $gstyles.$rs;
3102                         return $rs;
3103                 }
3104
3105                 /**
3106                 * This method allows printing text with line breaks.
3107                 * 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 />
3108                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3109                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3110                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3111                 * @param string $txt String to print
3112                 * @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>
3113                 * @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>
3114                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3115                 * @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>
3116                 * @param int $x x position in user units
3117                 * @param int $y y position in user units
3118                 * @param boolean $reseth if true reset the last cell height (default true).
3119                 * @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>
3120                 * @param boolean $ishtml se to true if $txt is HTML content (default = false).
3121                 * @return int Return the number of cells or 1 for html mode.
3122                 * @since 1.3
3123                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3124                 */
3125                 function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false) {
3126                         if ((empty($this->lasth))OR ($reseth)) {
3127                                 //set row height
3128                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3129                         }
3130                         if (!empty($y)) {
3131                                 $this->SetY($y);
3132                         } else {
3133                                 $y = $this->GetY();
3134                         }
3135                         // check for page break
3136                         $this->checkPageBreak($h);
3137                         $y = $this->GetY();
3138                         // get current page number
3139                         $startpage = $this->page;
3140                         if (!empty($x)) {
3141                                 $this->SetX($x);
3142                         } else {
3143                                 $x = $this->GetX();
3144                         }
3145                         if (empty($w) OR ($w <= 0)) {
3146                                 if ($this->rtl) {
3147                                         $w = $this->x - $this->lMargin;
3148                                 } else {
3149                                         $w = $this->w - $this->rMargin - $this->x;
3150                                 }
3151                         }
3152                         // store original margin values
3153                         $lMargin = $this->lMargin;
3154                         $rMargin = $this->rMargin;
3155                         if ($this->rtl) {
3156                                 $this->SetRightMargin($this->w - $this->x);
3157                                 $this->SetLeftMargin($this->x - $w);
3158                         } else {
3159                                 $this->SetLeftMargin($this->x);
3160                                 $this->SetRightMargin($this->w - $this->x - $w);
3161                         }
3162                         // calculate remaining vertical space on first page ($startpage)
3163                         $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
3164                         // Adjust internal padding
3165                         if ($this->cMargin < ($this->LineWidth / 2)) {
3166                                 $this->cMargin = ($this->LineWidth / 2);
3167                         }
3168                         // Add top space if needed
3169                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3170                                 $this->y += $this->LineWidth / 2;
3171                         }
3172                         // add top padding
3173                         $this->y += $this->cMargin;
3174                         if ($ishtml) {
3175                                 // Write HTML text
3176                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3177                                 $nl = 1;
3178                         } else {
3179                                 // Write text
3180                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false);
3181                         }
3182                         // add bottom padding
3183                         $this->y += $this->cMargin;
3184                         // Add bottom space if needed
3185                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3186                                 $this->y += $this->LineWidth / 2;
3187                         }
3188                         // Get end-of-text Y position
3189                         $currentY = $this->GetY();
3190                         // get latest page number
3191                         $endpage = $this->page;
3192                         // check if a new page has been created
3193                         if ($endpage > $startpage) {
3194                                 // design borders around HTML cells.
3195                                 for ($page=$startpage; $page <= $endpage; $page++) {
3196                                         $this->setPage($page);
3197                                         if ($page == $startpage) {
3198                                                 $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
3199                                                 $h = $restspace;
3200                                         } elseif ($page == $endpage) {
3201                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3202                                                 $h = $currentY - $this->tMargin;
3203                                         } else {
3204                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3205                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3206                                         }
3207                                         $this->SetX($x);
3208                                         $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3209                                         if ($border OR $fill) {
3210                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3211                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3212                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3213                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3214                                         }
3215                                 }
3216                         } else {
3217                                 $h = max($h, ($currentY - $y));
3218                                 // put cursor at the beginning of text
3219                                 $this->SetY($y);
3220                                 $this->SetX($x);
3221                                 $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3222                                 if ($border OR $fill) {
3223                                         // design a cell around the text
3224                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3225                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3226                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3227                                         $this->intmrk[$this->page] += strlen($ccode."\n");
3228                                 }
3229                         }
3230                         // Get end-of-cell Y position
3231                         $currentY = $this->GetY();
3232                         // restore original margin values
3233                         $this->SetLeftMargin($lMargin);
3234                         $this->SetRightMargin($rMargin);
3235                         if ($ln > 0) {
3236                                 //Go to the beginning of the next line
3237                                 $this->SetY($currentY);
3238                                 if ($ln == 2) {
3239                                         $this->SetX($x + $w);
3240                                 }
3241                         } else {
3242                                 // go left or right by case
3243                                 $this->setPage($startpage);
3244                                 $this->y = $y;
3245                                 $this->SetX($x + $w);
3246                         }
3247                         return $nl;
3248                 }
3249
3250                 /**
3251                 * This method prints text from the current position.<br />
3252                 * @param float $h Line height
3253                 * @param string $txt String to print
3254                 * @param mixed $link URL or identifier returned by AddLink()
3255                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3256                 * @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>
3257                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3258                 * @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>
3259                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3260                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3261                 * @since 1.5
3262                 */
3263                 function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false) {
3264                         // remove carriage returns
3265                         $s = str_replace("\r", '', $txt);
3266                         // check if string contains arabic text
3267                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3268                                 $arabic = true;
3269                         } else {
3270                                 $arabic = false;
3271                         }
3272                         // get array of chars
3273                         $chars = $this->UTF8StringToArray($s);
3274                         // get the number of characters
3275                         $nb = count($chars);
3276                         // handle single space character
3277                         if (($nb == 1) AND preg_match("/[\s]/u", $s)) {
3278                                 if ($this->rtl) {
3279                                         $this->x -= $this->GetStringWidth($s);
3280                                 } else {
3281                                         $this->x += $this->GetStringWidth($s);
3282                                 }
3283                                 return;
3284                         }
3285                         // store current position
3286                         $prevx = $this->x;
3287                         $prevy = $this->y;
3288                         // calculate remaining line width ($w)
3289                         if ($this->rtl) {
3290                                 $w = $this->x - $this->lMargin;
3291                         } else {
3292                                 $w = $this->w - $this->rMargin - $this->x;
3293                         }
3294                         // max column width
3295                         $wmax = $w - (2 * $this->cMargin);
3296                         $i = 0; // character position
3297                         $j = 0; // current starting position
3298                         $sep = -1; // position of the last blank space
3299                         $l = 0; // current string lenght
3300                         $nl = 0; //number of lines
3301                         $linebreak = false;
3302                         // for each character
3303                         while ($i < $nb) {
3304                                 //Get the current character
3305                                 $c = $chars[$i];
3306                                 if ($c == 10) { // 10 = "\n" = new line
3307                                         //Explicit line break
3308                                         if ($align == "J") {
3309                                                 if ($this->rtl) {
3310                                                         $talign = "R";
3311                                                 } else {
3312                                                         $talign = "L";
3313                                                 }
3314                                         } else {
3315                                                 $talign = $align;
3316                                         }
3317                                         if ($firstline) {
3318                                                 $startx = $this->x;
3319                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3320                                                 if ($this->rtl) {
3321                                                         $this->endlinex = $startx - $linew;
3322                                                 } else {
3323                                                         $this->endlinex = $startx + $linew;
3324                                                 }
3325                                                 $w = $linew;
3326                                                 $tmpcmargin = $this->cMargin;
3327                                                 $this->cMargin = 0;
3328                                         }
3329                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $talign, $fill, $link, $stretch);
3330                                         if ($firstline) {
3331                                                 $this->cMargin = $tmpcmargin;
3332                                                 return ($this->UTF8ArrSubString($chars, $i));
3333                                         }
3334                                         $nl++;
3335                                         $j = $i + 1;
3336                                         $l = 0;
3337                                         $sep = -1;
3338                                         $w = $this->getRemainingWidth();
3339                                         $wmax = $w - (2 * $this->cMargin);
3340                                 } else {
3341                                         if (preg_match("/[\s]/u", $this->unichr($c))) {
3342                                                 // update last blank space position
3343                                                 $sep = $i;
3344                                         }
3345                                         // update string length
3346                                         if (($this->isunicode) AND ($arabic)) {
3347                                                 // with bidirectional algorithm some chars may be changed affecting the line length
3348                                                 // *** very slow ***
3349                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), $this->tmprtl));
3350                                         } else {
3351                                                 $l += $this->GetCharWidth($c);
3352                                         }
3353                                         if ($l > $wmax) {
3354                                                 // we have reached the end of column
3355                                                 if ($sep == -1) {
3356                                                         // check if the line was already started
3357                                                         if (($this->rtl AND ($this->x < ($this->w - $this->rMargin)))
3358                                                                 OR ((!$this->rtl) AND ($this->x > $this->lMargin))) {
3359                                                                 // print a void cell and go to next line
3360                                                                 $this->Cell($w, $h, "", 0, 1);
3361                                                                 $linebreak = true;
3362                                                                 if ($firstline) {
3363                                                                         return ($this->UTF8ArrSubString($chars, $j));
3364                                                                 }
3365                                                         } else {
3366                                                                 // truncate the word because do not fit on column
3367                                                                 if ($firstline) {
3368                                                                         $startx = $this->x;
3369                                                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3370                                                                         if ($this->rtl) {
3371                                                                                 $this->endlinex = $startx - $linew;
3372                                                                         } else {
3373                                                                                 $this->endlinex = $startx + $linew;
3374                                                                         }
3375                                                                         $w = $linew;
3376                                                                         $tmpcmargin = $this->cMargin;
3377                                                                         $this->cMargin = 0;
3378                                                                 }
3379                                                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $align, $fill, $link, $stretch);
3380                                                                 if ($firstline) {
3381                                                                         $this->cMargin = $tmpcmargin;
3382                                                                         return ($this->UTF8ArrSubString($chars, $i));
3383                                                                 }
3384                                                                 $j = $i;
3385                                                                 $i--;
3386                                                         }
3387                                                 } else {
3388                                                         // word wrapping
3389                                                         if ($firstline) {
3390                                                                 $startx = $this->x;
3391                                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $sep), $this->tmprtl));
3392                                                                 if ($this->rtl) {
3393                                                                         $this->endlinex = $startx - $linew;
3394                                                                 } else {
3395                                                                         $this->endlinex = $startx + $linew;
3396                                                                 }
3397                                                                 $w = $linew;
3398                                                                 $tmpcmargin = $this->cMargin;
3399                                                                 $this->cMargin = 0;
3400                                                         }
3401                                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $sep), 0, 1, $align, $fill, $link, $stretch);
3402                                                         if ($firstline) {
3403                                                                 $this->cMargin = $tmpcmargin;
3404                                                                 return ($this->UTF8ArrSubString($chars, $sep));
3405                                                         }
3406                                                         $i = $sep;
3407                                                         $sep = -1;
3408                                                         $j = ($i+1);
3409                                                 }
3410                                                 $w = $this->getRemainingWidth();
3411                                                 $wmax = $w - (2 * $this->cMargin);
3412                                                 if ($linebreak) {
3413                                                         $linebreak = false;
3414                                                 } else {
3415                                                         $nl++;
3416                                                         $l = 0;
3417                                                 }
3418                                         }
3419                                 }
3420                                 $i++;
3421                         } // end while i < nb
3422                         // print last substring (if any)
3423                         if ($l > 0) {
3424                                 switch ($align) {
3425                                         case "J":
3426                                         case "C": {
3427                                                 $w = $w;
3428                                                 break;
3429                                         }
3430                                         case "L": {
3431                                                 if ($this->rtl) {
3432                                                         $w = $w;
3433                                                 } else {
3434                                                         $w = $l;
3435                                                 }
3436                                                 break;
3437                                         }
3438                                         case "R": {
3439                                                 if ($this->rtl) {
3440                                                         $w = $l;
3441                                                 } else {
3442                                                         $w = $w;
3443                                                 }
3444                                                 break;
3445                                         }
3446                                         default: {
3447                                                 $w = $l;
3448                                                 break;
3449                                         }
3450                                 }
3451                                 if ($firstline) {
3452                                         $startx = $this->x;
3453                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $nb), $this->tmprtl));
3454                                         if ($this->rtl) {
3455                                                 $this->endlinex = $startx - $linew;
3456                                         } else {
3457                                                 $this->endlinex = $startx + $linew;
3458                                         }
3459                                         $w = $linew;
3460                                         $tmpcmargin = $this->cMargin;
3461                                         $this->cMargin = 0;
3462                                 }
3463                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $nb), 0, $ln, $align, $fill, $link, $stretch);
3464                                 if ($firstline) {
3465                                         $this->cMargin = $tmpcmargin;
3466                                         return ($this->UTF8ArrSubString($chars, $nb));
3467                                 }
3468                                 $nl++;
3469                         }
3470                         if ($firstline) {
3471                                 return "";
3472                         }
3473                         return $nl;
3474                 }
3475
3476                 /**
3477                 * Returns the remaining width between the current position and margins.
3478                 * @return int Return the remaining width
3479                 * @access protected
3480                 */
3481                 function getRemainingWidth() {
3482                         if ($this->rtl) {
3483                                 return ($this->x - $this->lMargin);
3484                         } else {
3485                                 return ($this->w - $this->rMargin - $this->x);
3486                         }
3487                 }
3488
3489          /**
3490                 * Extract a slice of the $strarr array and return it as string.
3491                 * @param string $strarr The input array of characters.
3492                 * @param int $start the starting element of $strarr.
3493                 * @param int $end first element that will not be returned.
3494                 * @return Return part of a string
3495                 */
3496                 function UTF8ArrSubString($strarr, $start='', $end='') {
3497                         if (strlen($start) == 0) {
3498                                 $start = 0;
3499                         }
3500                         if (strlen($end) == 0) {
3501                                 $end = count($strarr);
3502                         }
3503                         $string = "";
3504                         for ($i=$start; $i < $end; $i++) {
3505                                 $string .= $this->unichr($strarr[$i]);
3506                         }
3507                         return $string;
3508                 }
3509
3510                 /**
3511                 * Returns the unicode caracter specified by UTF-8 code
3512                 * @param int $c UTF-8 code
3513                 * @return Returns the specified character.
3514                 * @author Miguel Perez, Nicola Asuni
3515                 * @since 2.3.000 (2008-03-05)
3516                 */
3517                 function unichr($c) {
3518                         if (!$this->isunicode) {
3519                                 return chr($c);
3520                         } elseif ($c <= 0x7F) {
3521                                 // one byte
3522                                 return chr($c);
3523                         } elseif ($c <= 0x7FF) {
3524                                 // two bytes
3525                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
3526                         } elseif ($c <= 0xFFFF) {
3527                                 // three bytes
3528                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3529                         } elseif ($c <= 0x10FFFF) {
3530                                 // four bytes
3531                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3532                         } else {
3533                                 return "";
3534                         }
3535                 }
3536
3537                 /**
3538                 * Puts an image in the page.
3539                 * The upper-left corner must be given.
3540                 * The dimensions can be specified in different ways:<ul>
3541                 * <li>explicit width and height (expressed in user unit)</li>
3542                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
3543                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
3544                 * 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;
3545                 * The format can be specified explicitly or inferred from the file extension.<br />
3546                 * It is possible to put a link on the image.<br />
3547                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
3548                 * @param string $file Name of the file containing the image.
3549                 * @param float $x Abscissa of the upper-left corner.
3550                 * @param float $y Ordinate of the upper-left corner.
3551                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
3552                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
3553                 * @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.
3554                 * @param mixed $link URL or identifier returned by AddLink().
3555                 * @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>
3556                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
3557                 * @param int $dpi dot-per-inch resolution used on resize
3558                 * @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>
3559                 * @since 1.1
3560                 * @see AddLink()
3561                 */
3562                 function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
3563                         // get image size
3564                         list($pixw, $pixh) = getimagesize($file);
3565                         // calculate image width and height on document
3566                         if (($w <= 0) AND ($h <= 0)) {
3567                                 // convert image size to document unit
3568                                 $w = $pixw / ($this->imgscale * $this->k);
3569                                 $h = $pixh / ($this->imgscale * $this->k);
3570                         } elseif ($w <= 0) {
3571                                 $w = $h * $pixw / $pixh;
3572                         } elseif ($h <= 0) {
3573                                 $h = $w * $pixh / $pixw;
3574                         }
3575                         // calculate new minimum dimensions in pixels
3576                         $neww = round($w * $this->k * $dpi / $this->dpi);
3577                         $newh = round($h * $this->k * $dpi / $this->dpi);
3578                         // check if resize is necessary (resize is used only to reduce the image)
3579                         if (($neww * $newh) >= ($pixw * $pixh)) {
3580                                 $resize = false;
3581                         }
3582                         // check if image has been already added on document
3583                         if (!isset($this->images[$file])) {
3584                                 //First use of image, get info
3585                                 if ($type == '') {
3586                                         $fileinfo = pathinfo($file);
3587                                         if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
3588                                                 $type = $fileinfo['extension'];
3589                                         } else {
3590                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
3591                                         }
3592                                 }
3593                                 $type = strtolower($type);
3594                                 if ($type == "jpg") {
3595                                         $type = "jpeg";
3596                                 }
3597                                 $mqr = get_magic_quotes_runtime();
3598                                 set_magic_quotes_runtime(0);
3599                                 // Specific image handlers
3600                                 $mtd = '_parse'.$type;
3601                                 // GD image handler function
3602                                 $gdfunction = "imagecreatefrom".$type;
3603                                 $info = false;
3604                                 if ((method_exists($this,$mtd)) AND (!($resize AND function_exists($gdfunction)))) {
3605                                         $info = $this->$mtd($file);
3606                                 }
3607                                 if (!$info) {
3608                                         if (function_exists($gdfunction)) {
3609                                                 $img = $gdfunction($file);
3610                                                 if ($resize) {
3611                                                         $imgr = imagecreatetruecolor($neww, $newh);
3612                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
3613                                                         $info = $this->_toJPEG($imgr);
3614                                                 } else {
3615                                                         $info = $this->_toJPEG($img);
3616                                                 }
3617                                         } else {
3618                                                 return;
3619                                         }
3620                                 }
3621                                 if ($info === false) {
3622                                         //If false, we cannot process image
3623                                         return;
3624                                 }
3625                                 set_magic_quotes_runtime($mqr);
3626                                 $info['i'] = count($this->images) + 1;
3627                                 // add image to document
3628                                 $this->images[$file] = $info;
3629                         } else {
3630                                 $info = $this->images[$file];
3631                         }
3632                         // Check whether we need a new page first as this does not fit
3633                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
3634                                 // Automatic page break
3635                                 $this->AddPage($this->CurOrientation);
3636                                 // Reset coordinates to top fo next page
3637                                 //$x = $this->GetX();
3638                                 $y = $this->GetY() + $this->cMargin;
3639                         }
3640                         // 2007-10-19 Warren Sherliker: End Edit
3641                         // set bottomcoordinates
3642                         $this->img_rb_y = $y + $h;
3643                         // set alignment
3644                         if ($this->rtl) {
3645                                 if ($palign == 'L') {
3646                                         $ximg = $this->lMargin;
3647                                         // set right side coordinate
3648                                         $this->img_rb_x = $ximg + $w;
3649                                 } elseif ($palign == 'C') {
3650                                         $ximg = ($this->w - $x - $w) / 2;
3651                                         // set right side coordinate
3652                                         $this->img_rb_x = $ximg + $w;
3653                                 } else {
3654                                         $ximg = $this->w - $x - $w;
3655                                         // set left side coordinate
3656                                         $this->img_rb_x = $ximg;
3657                                 }
3658                         } else {
3659                                 if ($palign == 'R') {
3660                                         $ximg = $this->w - $this->rMargin - $w;
3661                                         // set left side coordinate
3662                                         $this->img_rb_x = $ximg;
3663                                 } elseif ($palign == 'C') {
3664                                         $ximg = ($this->w - $x - $w) / 2;
3665                                         // set right side coordinate
3666                                         $this->img_rb_x = $ximg + $w;
3667                                 } else {
3668                                         $ximg = $x;
3669                                         // set right side coordinate
3670                                         $this->img_rb_x = $ximg + $w;
3671                                 }
3672                         }
3673                         $xkimg = $ximg * $this->k;
3674                         $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']));
3675                         if ($link) {
3676                                 $this->Link($ximg, $y, $w, $h, $link);
3677                         }
3678                         // set pointer to align the successive text/objects
3679                         switch($align) {
3680                                 case 'T': {
3681                                         $this->y = $y;
3682                                         $this->x = $this->img_rb_x;
3683                                         break;
3684                                 }
3685                                 case 'M': {
3686                                         $this->y = $y + round($h/2);
3687                                         $this->x = $this->img_rb_x;
3688                                         break;
3689                                 }
3690                                 case 'B': {
3691                                         $this->y = $this->img_rb_y;
3692                                         $this->x = $this->img_rb_x;
3693                                         break;
3694                                 }
3695                                 case 'N': {
3696                                         $this->SetY($this->img_rb_y);
3697                                         break;
3698                                 }
3699                                 default:{
3700                                         break;
3701                                 }
3702                         }
3703                         $this->endlinex = $this->img_rb_x;
3704                 }
3705
3706                 /**
3707                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
3708                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
3709                 * @param string $file Image file name.
3710                 * @param image $image Image object.
3711                 * return image JPEG image object.
3712                 * @access protected
3713                 */
3714                 function _toJPEG($image) {
3715                         $tempname = tempnam(K_PATH_CACHE,'jpg');
3716                         imagejpeg($image, $tempname, $this->jpeg_quality);
3717                         imagedestroy($image);
3718                         $retvars = $this->_parsejpeg($tempname);
3719                         // tidy up by removing temporary image
3720                         unlink($tempname);
3721                         return $retvars;
3722                 }
3723
3724                 /**
3725                 * Extract info from a JPEG file without using the GD library.
3726                 * @param string $file image file to parse
3727                 * @return array structure containing the image data
3728                 * @access protected
3729                 */
3730                 function _parsejpeg($file) {
3731                         $a = getimagesize($file);
3732                         if (empty($a)) {
3733                                 $this->Error('Missing or incorrect image file: '.$file);
3734                         }
3735                         if ($a[2] != 2) {
3736                                 $this->Error('Not a JPEG file: '.$file);
3737                         }
3738                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
3739                                 $colspace = 'DeviceRGB';
3740                         } elseif ($a['channels'] == 4) {
3741                                 $colspace = 'DeviceCMYK';
3742                         }       else {
3743                                 $colspace = 'DeviceGray';
3744                         }
3745                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
3746                         $data = file_get_contents($file);
3747                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
3748                 }
3749
3750                 /**
3751                 * Extract info from a PNG file without using the GD library.
3752                 * @param string $file image file to parse
3753                 * @return array structure containing the image data
3754                 * @access protected
3755                 */
3756                 function _parsepng($file) {
3757                         $f = fopen($file,'rb');
3758                         if (empty($f)) {
3759                                 $this->Error('Can\'t open image file: '.$file);
3760                         }
3761                         //Check signature
3762                         if (fread($f,8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3763                                 $this->Error('Not a PNG file: '.$file);
3764                         }
3765                         //Read header chunk
3766                         fread($f,4);
3767                         if (fread($f,4) != 'IHDR') {
3768                                 $this->Error('Incorrect PNG file: '.$file);
3769                         }
3770                         $w = $this->_freadint($f);
3771                         $h = $this->_freadint($f);
3772                         $bpc = ord(fread($f,1));
3773                         if ($bpc > 8) {
3774                                 //$this->Error('16-bit depth not supported: '.$file);
3775                                 return false;
3776                         }
3777                         $ct = ord(fread($f,1));
3778                         if ($ct == 0) {
3779                                 $colspace = 'DeviceGray';
3780                         } elseif ($ct == 2) {
3781                                 $colspace = 'DeviceRGB';
3782                         } elseif ($ct == 3) {
3783                                 $colspace = 'Indexed';
3784                         } else {
3785                                 //$this->Error('Alpha channel not supported: '.$file);
3786                                 return false;
3787                         }
3788                         if (ord(fread($f,1)) != 0) {
3789                                 //$this->Error('Unknown compression method: '.$file);
3790                                 return false;
3791                         }
3792                         if (ord(fread($f,1)) != 0) {
3793                                 //$this->Error('Unknown filter method: '.$file);
3794                                 return false;
3795                         }
3796                         if (ord(fread($f,1)) != 0) {
3797                                 //$this->Error('Interlacing not supported: '.$file);
3798                                 return false;
3799                         }
3800                         fread($f,4);
3801                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3802                         //Scan chunks looking for palette, transparency and image data
3803                         $pal = '';
3804                         $trns = '';
3805                         $data = '';
3806                         do {
3807                                 $n = $this->_freadint($f);
3808                                 $type = fread($f,4);
3809                                 if ($type == 'PLTE') {
3810                                         //Read palette
3811                                         $pal = fread($f,$n);
3812                                         fread($f,4);
3813                                 } elseif ($type == 'tRNS') {
3814                                         //Read transparency info
3815                                         $t = fread($f,$n);
3816                                         if ($ct == 0) {
3817                                                 $trns = array(ord(substr($t,1,1)));
3818                                         }
3819                                         elseif ($ct == 2) {
3820                                                 $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
3821                                         } else {
3822                                                 $pos = strpos($t,chr(0));
3823                                                 if ($pos !== false) {
3824                                                         $trns = array($pos);
3825                                                 }
3826                                         }
3827                                         fread($f, 4);
3828                                 } elseif ($type == 'IDAT') {
3829                                         //Read image data block
3830                                         $data .= fread($f,$n);
3831                                         fread($f, 4);
3832                                 } elseif ($type == 'IEND') {
3833                                         break;
3834                                 } else {
3835                                         fread($f, $n+4);
3836                                 }
3837                         }
3838                         while ($n);
3839                         if (($colspace == 'Indexed') AND (empty($pal))) {
3840                                 //$this->Error('Missing palette in '.$file);
3841                                 return false;
3842                         }
3843                         fclose($f);
3844                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
3845                 }
3846
3847                 /**
3848                 * Performs a line break.
3849                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
3850                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
3851                 * @param boolean $cell if true add a cMargin to the x coordinate
3852                 * @since 1.0
3853                 * @see Cell()
3854                 */
3855                 function Ln($h='', $cell=false) {
3856                         //Line feed; default value is last cell height
3857                         if ($cell) {
3858                                 $cellmargin = $this->cMargin;
3859                         } else {
3860                                 $cellmargin = 0;
3861                         }
3862                         if ($this->rtl) {
3863                                 $this->x = $this->w - $this->rMargin - $cellmargin;
3864                         } else {
3865                                 $this->x = $this->lMargin + $cellmargin;
3866                         }
3867                         if (is_string($h)) {
3868                                 $this->y += $this->lasth;
3869                         } else {
3870                                 $this->y += $h;
3871                         }
3872                         $this->newline = true;
3873                 }
3874
3875                 /**
3876                 * Returns the relative X value of current position.
3877                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
3878                 * @return float
3879                 * @since 1.2
3880                 * @see SetX(), GetY(), SetY()
3881                 */
3882                 function GetX() {
3883                         //Get x position
3884                         if ($this->rtl) {
3885                                 return ($this->w - $this->x);
3886                         } else {
3887                                 return $this->x;
3888                         }
3889                 }
3890
3891                 /**
3892                 * Returns the absolute X value of current position.
3893                 * @return float
3894                 * @since 1.2
3895                 * @see SetX(), GetY(), SetY()
3896                 */
3897                 function GetAbsX() {
3898                         return $this->x;
3899                 }
3900
3901                 /**
3902                 * Returns the ordinate of the current position.
3903                 * @return float
3904                 * @since 1.0
3905                 * @see SetY(), GetX(), SetX()
3906                 */
3907                 function GetY() {
3908                         //Get y position
3909                         return $this->y;
3910                 }
3911
3912                 /**
3913                 * Defines the abscissa of the current position.
3914                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
3915                 * @param float $x The value of the abscissa.
3916                 * @since 1.2
3917                 * @see GetX(), GetY(), SetY(), SetXY()
3918                 */
3919                 function SetX($x) {
3920                         //Set x position
3921                         if ($this->rtl) {
3922                                 if ($x >= 0) {
3923                                         $this->x = $this->w - $x;
3924                                 } else {
3925                                         $this->x = abs($x);
3926                                 }
3927                         } else {
3928                                 if ($x >= 0) {
3929                                         $this->x = $x;
3930                                 } else {
3931                                         $this->x = $this->w + $x;
3932                                 }
3933                         }
3934                 }
3935
3936                 /**
3937                 * Moves the current abscissa back to the left margin and sets the ordinate.
3938                 * If the passed value is negative, it is relative to the bottom of the page.
3939                 * @param float $y The value of the ordinate.
3940                 * @since 1.0
3941                 * @see GetX(), GetY(), SetY(), SetXY()
3942                 */
3943                 function SetY($y) {
3944                         //Set y position and reset x
3945                         if ($this->rtl) {
3946                                 $this->x = $this->w - $this->rMargin;
3947                         } else {
3948                                 $this->x = $this->lMargin;
3949                         }
3950                         if ($y >= 0) {
3951                                 $this->y = $y;
3952                         } else {
3953                                 $this->y = $this->h + $y;
3954                         }
3955                 }
3956
3957                 /**
3958                 * Defines the abscissa and ordinate of the current position.
3959                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
3960                 * @param float $x The value of the abscissa
3961                 * @param float $y The value of the ordinate
3962                 * @since 1.2
3963                 * @see SetX(), SetY()
3964                 */
3965                 function SetXY($x, $y) {
3966                         //Set x and y positions
3967                         $this->SetY($y);
3968                         $this->SetX($x);
3969                 }
3970
3971                 /**
3972                 * Send the document to a given destination: string, local file or browser.
3973                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
3974                 * The method first calls Close() if necessary to terminate the document.
3975                 * @param string $name The name of the file when saved.
3976                 * @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>
3977                 * @since 1.0
3978                 * @see Close()
3979                 */
3980                 function Output($name='doc.pdf', $dest='I') {
3981                         //Output PDF to some destination
3982                         //Finish document if necessary
3983                         if ($this->state < 3) {
3984                                 $this->Close();
3985                         }
3986                         //Normalize parameters
3987                         if (is_bool($dest)) {
3988                                 $dest = $dest ? 'D' : 'F';
3989                         }
3990                         $dest = strtoupper($dest);
3991                         if ($dest != 'F') {
3992                                 $name = str_replace("+", "%20", urlencode($name));
3993                                 $name = preg_replace('/[\r\n]+\s*/', '' , $name);
3994                         }
3995                         switch($dest) {
3996                                 case 'I': {
3997                                         //Send to standard output
3998                                         if (ob_get_contents()) {
3999                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4000                                         }
4001                                         if (php_sapi_name() != 'cli') {
4002                                                 //We send to a browser
4003                                                 header('Content-Type: application/pdf');
4004                                                 if (headers_sent()) {
4005                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
4006                                                 }
4007                                                 header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4008                                                 header("Pragma: public");
4009                                                 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4010                                                 header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");      
4011                                                 header('Content-Length: '.strlen($this->buffer));
4012                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
4013                                         }
4014                                         echo $this->buffer;
4015                                         break;
4016                                 }
4017                                 case 'D': {
4018                                         //Download file
4019                                         if (ob_get_contents()) {
4020                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4021                                         }
4022                                         header('Content-Description: File Transfer');
4023                                         if (headers_sent()) {
4024                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
4025                                         }
4026                                         header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4027                                         header("Pragma: public");
4028                                         header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4029                                         header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
4030                                         // force download dialog
4031                                         header("Content-Type: application/force-download");
4032                                         header("Content-Type: application/octet-stream", false);
4033                                         header("Content-Type: application/download", false);
4034                                         // use the Content-Disposition header to supply a recommended filename
4035                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
4036                                         header("Content-Transfer-Encoding: binary");
4037                                         header("Content-Length: ".strlen($this->buffer));
4038                                         echo $this->buffer;
4039                                         break;
4040                                 }
4041                                 case 'F': {
4042                                         //Save to local file
4043                                         $f = fopen($name, 'wb');
4044                                         if (!$f) {
4045                                                 $this->Error('Unable to create output file: '.$name);
4046                                         }
4047                                         fwrite($f, $this->buffer,strlen($this->buffer));
4048                                         fclose($f);
4049                                         break;
4050                                 }
4051                                 case 'S': {
4052                                         //Return as a string
4053                                         return $this->buffer;
4054                                 }
4055                                 default: {
4056                                         $this->Error('Incorrect output destination: '.$dest);
4057                                 }
4058                         }
4059                         return '';
4060                 }
4061
4062                 /**
4063                 * Check for locale-related bug
4064                 * @access protected
4065                 */
4066                 function _dochecks() {
4067                         //Check for locale-related bug
4068                         if (1.1 == 1) {
4069                                 $this->Error('Don\'t alter the locale before including class file');
4070                         }
4071                         //Check for decimal separator
4072                         if (sprintf('%.1f', 1.0) != '1.0') {
4073                                 setlocale(LC_NUMERIC, 'C');
4074                         }
4075                 }
4076
4077                 /**
4078                 * Return fonts path
4079                 * @return string
4080                 * @access protected
4081                 */
4082                 function _getfontpath() {
4083                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
4084                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
4085                         }
4086                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
4087                 }
4088
4089                 /**
4090                 * Output pages.
4091                 * @access protected
4092                 */
4093                 function _putpages() {
4094                         $nb = count($this->pages);
4095                         if (!empty($this->pagegroups)) {
4096                                 // do page number replacement
4097                                 foreach ($this->pagegroups as $k => $v) {
4098                                         $vu = $this->UTF8ToUTF16BE($v, false);
4099                                         $alias_a = $this->_escape($k);
4100                                         $alias_au = $this->_escape("{".$k."}");
4101                                         if ($this->isunicode) {
4102                                                 $alias_b = $this->_escape($this->UTF8ToLatin1($k));
4103                                                 $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$k."}"));
4104                                                 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
4105                                                 $alias_cu = $this->_escape($this->utf8StrRev("{".$k."}", false, $this->tmprtl));
4106                                         }
4107                                         for ($n = 1; $n <= $nb; $n++) {
4108                                                 $this->pages[$n] = str_replace($alias_au, $vu, $this->pages[$n]);
4109                                                 if ($this->isunicode) {
4110                                                         $this->pages[$n] = str_replace($alias_bu, $vu, $this->pages[$n]);
4111                                                         $this->pages[$n] = str_replace($alias_cu, $vu, $this->pages[$n]);
4112                                                         $this->pages[$n] = str_replace($alias_b, $v, $this->pages[$n]);
4113                                                         $this->pages[$n] = str_replace($alias_c, $v, $this->pages[$n]);
4114                                                 }
4115                                                 $this->pages[$n] = str_replace($alias_a, $v, $this->pages[$n]);
4116                                         }
4117                                 }
4118                         }
4119                         if (!empty($this->AliasNbPages)) {
4120                                 $nbu = $this->UTF8ToUTF16BE($nb, false); // replacement for unicode font
4121                                 $alias_a = $this->_escape($this->AliasNbPages);
4122                                 $alias_au = $this->_escape("{".$this->AliasNbPages."}");
4123                                 if ($this->isunicode) {
4124                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
4125                                         $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$this->AliasNbPages."}"));
4126                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
4127                                         $alias_cu = $this->_escape($this->utf8StrRev("{".$this->AliasNbPages."}", false, $this->tmprtl));
4128                                 }
4129                                 //Replace number of pages
4130                                 for($n = 1; $n <= $nb; $n++) {
4131                                         $this->pages[$n] = str_replace($alias_au, $nbu, $this->pages[$n]);
4132                                         if ($this->isunicode) {
4133                                                 $this->pages[$n] = str_replace($alias_bu, $nbu, $this->pages[$n]);
4134                                                 $this->pages[$n] = str_replace($alias_cu, $nbu, $this->pages[$n]);
4135                                                 $this->pages[$n] = str_replace($alias_b, $nb, $this->pages[$n]);
4136                                                 $this->pages[$n] = str_replace($alias_c, $nb, $this->pages[$n]);
4137                                         }
4138                                         $this->pages[$n] = str_replace($alias_a, $nb, $this->pages[$n]);
4139                                 }
4140                         }
4141                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4142                         for($n=1; $n <= $nb; $n++) {
4143                                 //Page
4144                                 $this->_newobj();
4145                                 $this->_out('<</Type /Page');
4146                                 $this->_out('/Parent 1 0 R');
4147                                 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
4148                                 $this->_out('/Resources 2 0 R');
4149                                 $this->_putannots($n);
4150                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
4151                                 $this->_out('endobj');
4152                                 //Page content
4153                                 $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
4154                                 $this->_newobj();
4155                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
4156                                 $this->_putstream($p);
4157                                 $this->_out('endobj');
4158                         }
4159                         //Pages root
4160                         $this->offsets[1] = strlen($this->buffer);
4161                         $this->_out('1 0 obj');
4162                         $this->_out('<</Type /Pages');
4163                         $kids='/Kids [';
4164                         for($i=0; $i < $nb; $i++) {
4165                                 $kids .= (3+2*$i).' 0 R ';
4166                         }
4167                         $this->_out($kids.']');
4168                         $this->_out('/Count '.$nb);
4169                         //$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
4170                         $this->_out('>>');
4171                         $this->_out('endobj');
4172                 }
4173
4174                 /**
4175                 * Output Page Annotations.
4176                 * See section 8.4 of PDF reference.
4177                 * @param int $n page number
4178                 * @access protected
4179                 * @author Nicola Asuni
4180                 * @since 4.0.018 (2008-08-06)
4181                 */
4182                 function _putannots($n) {
4183                         if (isset($this->PageAnnots[$n])) {
4184                                 $annots = '/Annots [';
4185                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
4186                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
4187                                         $a = $pl['x'] * $this->k;
4188                                         $b = $this->hPt - $pl['y'] * $this->k;
4189                                         $c = $pl['w'] * $this->k;
4190                                         $d = $pl['h'] * $this->k;
4191                                         $rect = sprintf('%.2f %.2f %.2f %.2f', $a, $b, $a+$c, $b-$d);
4192                                         $annots .= '<</Type /Annot';
4193                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
4194                                         $annots .= ' /Rect ['.$rect.']';
4195                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
4196                                         //$annots .= ' /P ';
4197                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
4198                                         $annots .= ' /M '.$this->_datestring('D:'.date('YmdHis'));
4199                                         if (isset($pl['opt']['f'])) {
4200                                                 $val = 0;
4201                                                 if (is_array($pl['opt']['f'])) {
4202                                                         foreach ($pl['opt']['f'] as $f) {
4203                                                                 switch (strtolower($f)) {
4204                                                                         case 'invisible': {
4205                                                                                 $val += 1 << 0;
4206                                                                                 break;
4207                                                                         }
4208                                                                         case 'hidden': {
4209                                                                                 $val += 1 << 1;
4210                                                                                 break;
4211                                                                         }
4212                                                                         case 'print': {
4213                                                                                 $val += 1 << 2;
4214                                                                                 break;
4215                                                                         }
4216                                                                         case 'nozoom': {
4217                                                                                 $val += 1 << 3;
4218                                                                                 break;
4219                                                                         }
4220                                                                         case 'norotate': {
4221                                                                                 $val += 1 << 4;
4222                                                                                 break;
4223                                                                         }
4224                                                                         case 'noview': {
4225                                                                                 $val += 1 << 5;
4226                                                                                 break;
4227                                                                         }
4228                                                                         case 'readonly': {
4229                                                                                 $val += 1 << 6;
4230                                                                                 break;
4231                                                                         }
4232                                                                         case 'locked': {
4233                                                                                 $val += 1 << 8;
4234                                                                                 break;
4235                                                                         }
4236                                                                         case 'togglenoview': {
4237                                                                                 $val += 1 << 9;
4238                                                                                 break;
4239                                                                         }
4240                                                                         case 'lockedcontents': {
4241                                                                                 $val += 1 << 10;
4242                                                                                 break;
4243                                                                         }
4244                                                                         default: {
4245                                                                                 break;
4246                                                                         }
4247                                                                 }
4248                                                         }
4249                                                 }
4250                                                 $annots .= ' /F '.intval($val);
4251                                         }
4252                                         //$annots .= ' /AP ';
4253                                         //$annots .= ' /AS ';
4254                                         $annots .= ' /Border [';
4255                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
4256                                                 $annots .= intval($pl['opt']['border'][0]).' ';
4257                                                 $annots .= intval($pl['opt']['border'][1]).' ';
4258                                                 $annots .= intval($pl['opt']['border'][2]);
4259                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
4260                                                         $annots .= ' [';
4261                                                         foreach ($pl['opt']['border'][3] as $dash) {
4262                                                                 $annots .= intval($dash).' ';
4263                                                         }
4264                                                         $annots .= ']';
4265                                                 }
4266                                         } else {
4267                                                 $annots .= '0 0 0';
4268                                         }
4269                                         $annots .= ']';
4270                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
4271                                                 $annots .= ' /BS <<Type /Border';
4272                                                 if (isset($pl['opt']['bs']['w'])) {
4273                                                         $annots .= ' /W '.sprintf("%.4f", floatval($pl['opt']['bs']['w']));
4274                                                 }
4275                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
4276                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $markups)) {
4277                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4278                                                 }
4279                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
4280                                                         $annots .= ' /D [';
4281                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
4282                                                                 $cord = floatval($cord);
4283                                                                 $annots .= sprintf(" %.4f", $cord);
4284                                                         }
4285                                                         $annots .= ']';
4286                                                 }
4287                                                 $annots .= '>>';
4288                                         }
4289                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
4290                                                 $annots .= ' /BE <<';
4291                                                 $bstyles = array('S', 'C');
4292                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
4293                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4294                                                 } else {
4295                                                         $annots .= ' /S /S';
4296                                                 }
4297                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
4298                                                         $annots .= ' /I '.sprintf(" %.4f", $pl['opt']['be']['i']);
4299                                                 }
4300                                                 $annots .= '>>';
4301                                         }
4302                                         $annots .= ' /C [';
4303                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
4304                                                 foreach ($pl['opt']['c'] as $col) {
4305                                                         $col = intval($col);
4306                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
4307                                                         $annots .= sprintf(" %.4f", $color);
4308                                                 }
4309                                         }
4310                                         $annots .= ']';
4311                                         //$annots .= ' /StructParent ';
4312                                         //$annots .= ' /OC ';
4313                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
4314                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
4315                                                 // this is a markup type
4316                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
4317                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
4318                                                 }
4319                                                 //$annots .= ' /Popup ';
4320                                                 if (isset($pl['opt']['ca'])) {
4321                                                         $annots .= ' /CA '.sprintf("%.4f", floatval($pl['opt']['ca']));
4322                                                 }
4323                                                 if (isset($pl['opt']['rc'])) {
4324                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4325                                                 }
4326                                                 $annots .= ' /CreationDate '.$this->_datestring('D:'.date('YmdHis'));
4327                                                 //$annots .= ' /IRT ';
4328                                                 if (isset($pl['opt']['subj'])) {
4329                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
4330                                                 }
4331                                                 //$annots .= ' /RT ';
4332                                                 //$annots .= ' /IT ';
4333                                                 //$annots .= ' /ExData ';
4334                                         }
4335                                         switch (strtolower($pl['opt']['subtype'])) {
4336                                                 case 'text': {
4337                                                         if (isset($pl['opt']['open'])) {
4338                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
4339                                                         }
4340                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
4341                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
4342                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
4343                                                         } else {
4344                                                                 $annots .= ' /Name /Note';
4345                                                         }
4346                                                         $statemodels = array('Marked', 'Review');
4347                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
4348                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4349                                                         } else {
4350                                                                 $pl['opt']['statemodel'] = 'Marked';
4351                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4352                                                         }
4353                                                         if ($pl['opt']['statemodel'] == 'Marked') {
4354                                                                 $states = array('Accepted', 'Unmarked');
4355                                                         } else {
4356                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
4357                                                         }
4358                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
4359                                                                 $annots .= ' /State /'.$pl['opt']['state'];
4360                                                         } else {
4361                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
4362                                                                         $annots .= ' /State /Unmarked';
4363                                                                 } else {
4364                                                                         $annots .= ' /State /None';
4365                                                                 }
4366                                                         }
4367                                                         break;
4368                                                 }
4369                                                 case 'link': {
4370                                                         $annots .= ' /A <</S /URI /URI '.$this->_uristring($pl['txt']).'>>';
4371                                                         $hmodes = array('N', 'I', 'O', 'P');
4372                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
4373                                                                 $annots .= ' /H /'.$pl['opt']['h'];
4374                                                         } else {
4375                                                                 $annots .= ' /H /I';
4376                                                         }
4377                                                         //$annots .= ' /Dest ';
4378                                                         //$annots .= ' /PA ';
4379                                                         //$annots .= ' /Quadpoints ';
4380                                                         break;
4381                                                 }
4382                                                 case 'freetext': {
4383                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
4384                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
4385                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
4386                                                         }
4387                                                         if (isset($pl['opt']['rc'])) {
4388                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4389                                                         }
4390                                                         if (isset($pl['opt']['ds'])) {
4391                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
4392                                                         }
4393                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
4394                                                                 $annots .= ' /CL [';
4395                                                                 foreach ($pl['opt']['cl'] as $cl) {
4396                                                                         $annots .= sprintf("%.4f ", $cl * $this->k);
4397                                                                 }
4398                                                                 $annots .= ']';
4399                                                         }
4400                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
4401                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
4402                                                                 $annots .= ' /IT '.$pl['opt']['it'];
4403                                                         }
4404                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
4405                                                                 $l = $pl['opt']['rd'][0] * $this->k;
4406                                                                 $r = $pl['opt']['rd'][1] * $this->k;
4407                                                                 $t = $pl['opt']['rd'][2] * $this->k;
4408                                                                 $b = $pl['opt']['rd'][3] * $this->k;
4409                                                                 $annots .= ' /RD ['.sprintf('%.2f %.2f %.2f %.2f', $l, $r, $t, $b).']';
4410                                                         }
4411                                                         //$annots .= ' /LE ';
4412                                                         break;
4413                                                 }
4414                                                 // ... to be completed ...
4415                                                 case 'line': {
4416                                                         break;
4417                                                 }
4418                                                 case 'square': {
4419                                                         break;
4420                                                 }
4421                                                 case 'circle': {
4422                                                         break;
4423                                                 }
4424                                                 case 'polygon': {
4425                                                         break;
4426                                                 }
4427                                                 case 'polyline': {
4428                                                         break;
4429                                                 }
4430                                                 case 'highlight': {
4431                                                         break;
4432                                                 }
4433                                                 case 'underline': {
4434                                                         break;
4435                                                 }
4436                                                 case 'squiggly': {
4437                                                         break;
4438                                                 }
4439                                                 case 'strikeout': {
4440                                                         break;
4441                                                 }
4442                                                 case 'stamp': {
4443                                                         break;
4444                                                 }
4445                                                 case 'caret': {
4446                                                         break;
4447                                                 }
4448                                                 case 'ink': {
4449                                                         break;
4450                                                 }
4451                                                 case 'popup': {
4452                                                         break;
4453                                                 }
4454                                                 case 'fileattachment': {
4455                                                         break;
4456                                                 }
4457                                                 case 'sound': {
4458                                                         break;
4459                                                 }
4460                                                 case 'movie': {
4461                                                         break;
4462                                                 }
4463                                                 case 'widget': {
4464                                                         break;
4465                                                 }
4466                                                 case 'screen': {
4467                                                         break;
4468                                                 }
4469                                                 case 'printermark': {
4470                                                         break;
4471                                                 }
4472                                                 case 'trapnet': {
4473                                                         break;
4474                                                 }
4475                                                 case 'watermark': {
4476                                                         break;
4477                                                 }
4478                                                 case '3d': {
4479                                                         break;
4480                                                 }
4481                                                 default: {
4482                                                         break;
4483                                                 }
4484                                         }
4485                                         
4486                                 $annots .= '>>';
4487                                 }
4488                                 $this->_out($annots.']');
4489                         }
4490                 }
4491
4492                 /**
4493                 * Output fonts.
4494                 * _putfonts
4495                 * @access protected
4496                 */
4497                 function _putfonts() {
4498                         $nf = $this->n;
4499                         foreach($this->diffs as $diff) {
4500                                 //Encodings
4501                                 $this->_newobj();
4502                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
4503                                 $this->_out('endobj');
4504                         }
4505                         $mqr = get_magic_quotes_runtime();
4506                         set_magic_quotes_runtime(0);
4507                         foreach($this->FontFiles as $file => $info) {
4508                                 //Font file embedding
4509                                 $this->_newobj();
4510                                 $this->FontFiles[$file]['n'] = $this->n;
4511                                 $font = file_get_contents($this->_getfontpath().strtolower($file));
4512                                 $compressed = (substr($file,-2)=='.z');
4513                                 if ((!$compressed) AND (isset($info['length2']))) {
4514                                         $header = (ord($font{0}) == 128);
4515                                         if ($header) {
4516                                                 //Strip first binary header
4517                                                 $font = substr($font,6);
4518                                         }
4519                                         if ($header AND (ord($font{$info['length1']}) == 128)) {
4520                                                 //Strip second binary header
4521                                                 $font = substr($font, 0, $info['length1']).substr($font, $info['length1']+6);
4522                                         }
4523                                 }
4524                                 $this->_out('<</Length '.strlen($font));
4525                                 if ($compressed) {
4526                                         $this->_out('/Filter /FlateDecode');
4527                                 }
4528                                 $this->_out('/Length1 '.$info['length1']);
4529                                 if (isset($info['length2'])) {
4530                                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
4531                                 }
4532                                 $this->_out('>>');
4533                                 $this->_putstream($font);
4534                                 $this->_out('endobj');
4535                         }
4536                         set_magic_quotes_runtime($mqr);
4537                         foreach($this->fonts as $k => $font) {
4538                                 //Font objects
4539                                 $this->fonts[$k]['n'] = $this->n + 1;
4540                                 $type = $font['type'];
4541                                 $name = $font['name'];
4542                                 if ($type == 'core') {
4543                                         //Standard font
4544                                         $this->_newobj();
4545                                         $this->_out('<</Type /Font');
4546                                         $this->_out('/BaseFont /'.$name);
4547                                         $this->_out('/Subtype /Type1');
4548                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
4549                                                 $this->_out('/Encoding /WinAnsiEncoding');
4550                                         }
4551                                         $this->_out('>>');
4552                                         $this->_out('endobj');
4553                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
4554                                         //Additional Type1 or TrueType font
4555                                         $this->_newobj();
4556                                         $this->_out('<</Type /Font');
4557                                         $this->_out('/BaseFont /'.$name);
4558                                         $this->_out('/Subtype /'.$type);
4559                                         $this->_out('/FirstChar 32 /LastChar 255');
4560                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
4561                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
4562                                         if ($font['enc']) {
4563                                                 if (isset($font['diff'])) {
4564                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
4565                                                 } else {
4566                                                         $this->_out('/Encoding /WinAnsiEncoding');
4567                                                 }
4568                                         }
4569                                         $this->_out('>>');
4570                                         $this->_out('endobj');
4571                                         //Widths
4572                                         $this->_newobj();
4573                                         $cw = &$font['cw'];
4574                                         $s = '[';
4575                                         for($i=32; $i <= 255; $i++) {
4576                                                 //$s .= $cw[chr($i)].' ';
4577                                                 $s .= $cw[$i].' ';
4578                                         }
4579                                         $this->_out($s.']');
4580                                         $this->_out('endobj');
4581                                         //Descriptor
4582                                         $this->_newobj();
4583                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4584                                         foreach($font['desc'] as $k => $v) {
4585                                                 $s .= ' /'.$k.' '.$v;
4586                                         }
4587                                         $file = $font['file'];
4588                                         if ($file) {
4589                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
4590                                         }
4591                                         $this->_out($s.'>>');
4592                                         $this->_out('endobj');
4593                                 } else {
4594                                         //Allow for additional types
4595                                         $mtd = '_put'.strtolower($type);
4596                                         if (!method_exists($this, $mtd)) {
4597                                                 $this->Error('Unsupported font type: '.$type);
4598                                         }
4599                                         $this->$mtd($font);
4600                                 }
4601                         }
4602                 }
4603
4604                 /**
4605                  * Output CID-0 fonts.
4606                  * @param array $font font data
4607                  * @access protected
4608                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
4609                  * @since 3.2.000 (2008-06-23)
4610                  */
4611                 function _putcidfont0($font) {
4612                         if (isset($font['cidinfo']['uni2cid'])) {
4613                                 // convert unicode to cid.
4614                                 $uni2cid = $font['cidinfo']['uni2cid'];
4615                                 $cw = array();
4616                                 foreach ($font['cw'] as $uni => $width) {
4617                                         if (isset($uni2cid[$uni])) {
4618                                                 $cw[($uni2cid[$uni] + 31)] = $width;
4619                                         } elseif ($uni <= 255) {
4620                                                 $cw[$uni] = $width;
4621                                         } // else unknown character
4622                                 }
4623                                 ksort($cw);
4624                                 $font = array_merge($font, array('cw'=>$cw));
4625                         }
4626                         $longname = $name = $font['name'];
4627                         $enc = $font['enc'];
4628                         if ($enc) {
4629                                 $longname .= "-$enc";
4630                         }
4631                         $this->_newobj();
4632                         $this->_out('<</Type /Font');
4633                         $this->_out('/BaseFont /'.$longname);
4634                         $this->_out('/Subtype /Type0');
4635                         if ($enc) {
4636                                 $this->_out('/Encoding /'.$enc);
4637                         }
4638                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4639                         $this->_out('>>');
4640                         $this->_out('endobj');
4641                         $this->_newobj();
4642                         $this->_out('<</Type /Font');
4643                         $this->_out('/BaseFont /'.$name);
4644                         $this->_out('/Subtype /CIDFontType0');
4645                         $cidinfo = '/Registry ('.$font['cidinfo']['Registry'].') ';
4646                         $cidinfo .= '/Ordering ('.$font['cidinfo']['Ordering'].') ';
4647                         $cidinfo .= '/Supplement '.$font['cidinfo']['Supplement'];
4648                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
4649                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
4650                         $codes = array_keys($font['cw']);
4651                         $first = current($codes);
4652                         $last = end($codes);
4653                         $this->_out('/DW '.$font['dw']);
4654                         $w = '/W [';
4655                         $ranges = array();
4656                         $currange = 0;
4657                         for($i = $first; $i <= $last; $i++) {
4658                                 if (isset($font['cw'][$i]) AND (!$currange)) {
4659                                         $currange = $i - 31;
4660                                 } elseif (!isset($font['cw'][$i])) {
4661                                         $currange = 0;
4662                                 }
4663                                 if ($currange) {
4664                                         $ranges[$currange][] = $font['cw'][$i];
4665                                 }
4666                         }
4667                         foreach($ranges as $k => $ws) {
4668                                 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
4669                         }
4670                         $w .= ' ]';
4671                         $this->_out($w);
4672                         $this->_out('>>');
4673                         $this->_out('endobj');
4674                         $this->_newobj();
4675                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4676                         foreach($font['desc'] as $k => $v) {
4677                                 $s .= ' /'.$k.' '.$v;
4678                         }
4679                         $this->_out($s.'>>');
4680                         $this->_out('endobj');
4681                 }
4682
4683                 /**
4684                  * Output images.
4685                  * @access protected
4686                  */
4687                 function _putimages() {
4688                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4689                         reset($this->images);
4690                         while (list($file, $info) = each($this->images)) {
4691                                 $this->_newobj();
4692                                 $this->images[$file]['n'] = $this->n;
4693                                 $this->_out('<</Type /XObject');
4694                                 $this->_out('/Subtype /Image');
4695                                 $this->_out('/Width '.$info['w']);
4696                                 $this->_out('/Height '.$info['h']);
4697                                 if (isset($info["masked"])) {
4698                                         $this->_out('/SMask '.($this->n-1).' 0 R');
4699                                 }
4700                                 if ($info['cs'] == 'Indexed') {
4701                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]');
4702                                 } else {
4703                                         $this->_out('/ColorSpace /'.$info['cs']);
4704                                         if ($info['cs'] == 'DeviceCMYK') {
4705                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
4706                                         }
4707                                 }
4708                                 $this->_out('/BitsPerComponent '.$info['bpc']);
4709                                 if (isset($info['f'])) {
4710                                         $this->_out('/Filter /'.$info['f']);
4711                                 }
4712                                 if (isset($info['parms'])) {
4713                                         $this->_out($info['parms']);
4714                                 }
4715                                 if (isset($info['trns']) and is_array($info['trns'])) {
4716                                         $trns='';
4717                                         for($i=0; $i < count($info['trns']); $i++) {
4718                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
4719                                         }
4720                                         $this->_out('/Mask ['.$trns.']');
4721                                 }
4722                                 $this->_out('/Length '.strlen($info['data']).'>>');
4723                                 $this->_putstream($info['data']);
4724                                 unset($this->images[$file]['data']);
4725                                 $this->_out('endobj');
4726                                 //Palette
4727                                 if ($info['cs'] == 'Indexed') {
4728                                         $this->_newobj();
4729                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
4730                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
4731                                         $this->_putstream($pal);
4732                                         $this->_out('endobj');
4733                                 }
4734                         }
4735                 }
4736
4737                 /**
4738                 * Output Spot Colors Resources.
4739                 * @access protected
4740                 * @since 4.0.024 (2008-09-12)
4741                 */
4742                 function _putspotcolors() {
4743                         foreach ($this->spot_colors as $name => $color) {
4744                                 $this->_newobj();
4745                                 $this->spot_colors[$name]['n'] = $this->n;
4746                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
4747                                 $this->_out('/DeviceCMYK <<');
4748                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
4749                                 $this->_out(sprintf('/C1 [%.4f %.4f %.4f %.4f] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
4750                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
4751                                 $this->_out('endobj');
4752                         }
4753                 }
4754
4755                 /**
4756                 * Output object dictionary for images.
4757                 * @access protected
4758                 */
4759                 function _putxobjectdict() {
4760                         foreach($this->images as $image) {
4761                                 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
4762                         }
4763                 }
4764
4765                 /**
4766                 * Output Resources Dictionary.
4767                 * @access protected
4768                 */
4769                 function _putresourcedict(){
4770                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
4771                         $this->_out('/Font <<');
4772                         foreach($this->fonts as $font) {
4773                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
4774                         }
4775                         $this->_out('>>');
4776                         $this->_out('/XObject <<');
4777                         $this->_putxobjectdict();
4778                         $this->_out('>>');
4779                         // visibility
4780                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
4781                         // transparency
4782                         $this->_out('/ExtGState <<');
4783                         foreach($this->extgstates as $k => $extgstate) {
4784                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
4785                         }
4786                         $this->_out('>>');
4787                         // gradients
4788                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
4789                                 $this->_out('/Shading <<');
4790                                 foreach($this->gradients as $id => $grad) {
4791                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
4792                                 }
4793                                 $this->_out('>>');
4794                         }
4795                         // spot colors
4796                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
4797                                 $this->_out('/ColorSpace <<');
4798                                 foreach ($this->spot_colors as $color) {
4799                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
4800                                 }
4801                                 $this->_out('>>');
4802                         }
4803                 }
4804
4805                 /**
4806                 * Output Resources.
4807                 * @access protected
4808                 */
4809                 function _putresources() {
4810                         $this->_putextgstates();
4811                         $this->_putocg();
4812                         $this->_putfonts();
4813                         $this->_putimages();
4814                         $this->_putspotcolors();
4815                         $this->_putshaders();
4816                         //Resource dictionary
4817                         $this->offsets[2] = strlen($this->buffer);
4818                         $this->_out('2 0 obj');
4819                         $this->_out('<<');
4820                         $this->_putresourcedict();
4821                         $this->_out('>>');
4822                         $this->_out('endobj');
4823                         $this->_putjavascript();
4824                         $this->_putbookmarks();
4825                         // encryption
4826                         if ($this->encrypted) {
4827                                 $this->_newobj();
4828                                 $this->enc_obj_id = $this->n;
4829                                 $this->_out('<<');
4830                                 $this->_putencryption();
4831                                 $this->_out('>>');
4832                                 $this->_out('endobj');
4833                         }
4834                 }
4835
4836                 /**
4837                 * Adds some Metadata information
4838                 * (see Chapter 10.2 of PDF Reference)
4839                 * @access protected
4840                 */
4841                 function _putinfo() {
4842                         if (!empty($this->title)) {
4843                                 $this->_out('/Title '.$this->_textstring($this->title));
4844                         }
4845                         if (!empty($this->author)) {
4846                                 $this->_out('/Author '.$this->_textstring($this->author));
4847                         }
4848                         if (!empty($this->subject)) {
4849                                 $this->_out('/Subject '.$this->_textstring($this->subject));
4850                         }
4851                         if (!empty($this->keywords)) {
4852                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
4853                         }
4854                         if (!empty($this->creator)) {
4855                                 $this->_out('/Creator '.$this->_textstring($this->creator));
4856                         }
4857                         if (defined('PDF_PRODUCER')) {
4858                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
4859                         }
4860                         $this->_out('/CreationDate '.$this->_datestring('D:'.date('YmdHis')));
4861                         $this->_out('/ModDate '.$this->_datestring('D:'.date('YmdHis')));
4862                 }
4863
4864                 /**
4865                 * Format a date string for meta information
4866                 * @param string $s date string to escape.
4867                 * @return string escaped string.
4868                 * @access protected
4869                 */
4870                 function _datestring($s) {
4871                         if ($this->encrypted) {
4872                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4873                         }
4874                         return '('. $this->_escape($s).')';
4875                 }
4876
4877                 /**
4878                 * Output Catalog.
4879                 * @access protected
4880                 */
4881                 function _putcatalog() {
4882                         $this->_out('/Type /Catalog');
4883                         $this->_out('/Pages 1 0 R');
4884
4885                         if ($this->ZoomMode == 'fullpage') {
4886                                 $this->_out('/OpenAction [3 0 R /Fit]');
4887                         } elseif ($this->ZoomMode == 'fullwidth') {
4888                                 $this->_out('/OpenAction [3 0 R /FitH null]');
4889                         } elseif ($this->ZoomMode == 'real') {
4890                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
4891                         } elseif (!is_string($this->ZoomMode)) {
4892                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
4893                         }
4894                         if (isset($this->LayoutMode) AND (!empty($this->LayoutMode))) {
4895                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
4896                         }
4897                         if (isset($this->PageMode) AND (!empty($this->PageMode))) {
4898                                 $this->_out('/PageMode /'.$this->PageMode);
4899                         }
4900                         if (isset($this->l['a_meta_language'])) {
4901                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
4902                         }
4903                         if (!empty($this->javascript)) {
4904                                 $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
4905                         }
4906                         if (count($this->outlines) > 0) {
4907                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
4908                                 $this->_out('/PageMode /UseOutlines');
4909                         }
4910                         $this->_putviewerpreferences();
4911                         $p = $this->n_ocg_print.' 0 R';
4912                         $v = $this->n_ocg_view.' 0 R';
4913                         $as = "<</Event /Print /OCGs [".$p." ".$v."] /Category [/Print]>> <</Event /View /OCGs [".$p." ".$v."] /Category [/View]>>";
4914                         $this->_out("/OCProperties <</OCGs [".$p." ".$v."] /D <</ON [".$p."] /OFF [".$v."] /AS [".$as."]>>>>");
4915                         $this->_putuserrights();
4916                 }
4917
4918                 /**
4919                 * Output viewer preferences.
4920                 * @author Nicola asuni
4921                 * @since 3.1.000 (2008-06-09)
4922                 * @access protected
4923                 */
4924                 function _putviewerpreferences() {
4925                         $this->_out('/ViewerPreferences<<');
4926                         if ($this->rtl) {
4927                                 $this->_out('/Direction /R2L');
4928                         } else {
4929                                 $this->_out('/Direction /L2R');
4930                         }
4931                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
4932                                 $this->_out('/HideToolbar true');
4933                         }
4934                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
4935                                 $this->_out('/HideMenubar true');
4936                         }
4937                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
4938                                 $this->_out('/HideWindowUI true');
4939                         }
4940                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
4941                                 $this->_out('/FitWindow true');
4942                         }
4943                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
4944                                 $this->_out('/CenterWindow true');
4945                         }
4946                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
4947                                 $this->_out('/DisplayDocTitle true');
4948                         }
4949                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
4950                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
4951                         }
4952                         if (isset($this->viewer_preferences['ViewArea'])) {
4953                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
4954                         }
4955                         if (isset($this->viewer_preferences['ViewClip'])) {
4956                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
4957                         }
4958                         if (isset($this->viewer_preferences['PrintArea'])) {
4959                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
4960                         }
4961                         if (isset($this->viewer_preferences['PrintClip'])) {
4962                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
4963                         }
4964                         if (isset($this->viewer_preferences['PrintScaling'])) {
4965                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
4966                         }
4967                         if (isset($this->viewer_preferences['Duplex']) AND (!empty($this->viewer_preferences['Duplex']))) {
4968                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
4969                         }
4970                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
4971                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
4972                                         $this->_out('/PickTrayByPDFSize true');
4973                                 } else {
4974                                         $this->_out('/PickTrayByPDFSize false');
4975                                 }
4976                         }
4977                         if (isset($this->viewer_preferences['PrintPageRange'])) {
4978                                 $PrintPageRangeNum = "";
4979                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
4980                                         $PrintPageRangeNum .= " ".($v-1)."";
4981                                 }
4982                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
4983                         }
4984                         if (isset($this->viewer_preferences['NumCopies'])) {
4985                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
4986                         }
4987                         $this->_out('>>');
4988                 }
4989
4990                 /**
4991                 * Output trailer.
4992                 * @access protected
4993                 */
4994                 function _puttrailer() {
4995                         $this->_out('/Size '.($this->n + 1));
4996                         $this->_out('/Root '.$this->n.' 0 R');
4997                         $this->_out('/Info '.($this->n - 1).' 0 R');
4998                         if ($this->encrypted) {
4999                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
5000                                 $this->_out('/ID [()()]');
5001                         }
5002                 }
5003
5004                 /**
5005                 * Output PDF header.
5006                 * @access protected
5007                 */
5008                 function _putheader() {
5009                         $this->_out('%PDF-'.$this->PDFVersion);
5010                 }
5011
5012                 /**
5013                 * Output end of document (EOF).
5014                 * @access protected
5015                 */
5016                 function _enddoc() {
5017                         $this->_putheader();
5018                         $this->_putpages();
5019                         $this->_putresources();
5020                         //Info
5021                         $this->_newobj();
5022                         $this->_out('<<');
5023                         $this->_putinfo();
5024                         $this->_out('>>');
5025                         $this->_out('endobj');
5026                         //Catalog
5027                         $this->_newobj();
5028                         $this->_out('<<');
5029                         $this->_putcatalog();
5030                         $this->_out('>>');
5031                         $this->_out('endobj');
5032                         //Cross-ref
5033                         $o = strlen($this->buffer);
5034                         $this->_out('xref');
5035                         $this->_out('0 '.($this->n + 1));
5036                         $this->_out('0000000000 65535 f ');
5037                         for($i=1; $i <= $this->n; $i++) {
5038                                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
5039                         }
5040                         //Trailer
5041                         $this->_out('trailer');
5042                         $this->_out('<<');
5043                         $this->_puttrailer();
5044                         $this->_out('>>');
5045                         $this->_out('startxref');
5046                         $this->_out($o);
5047                         $this->_out('%%EOF');
5048                         $this->state = 3;
5049                 }
5050
5051                 /**
5052                 * Initialize a new page.
5053                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
5054                 * @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>
5055                 * @access protected
5056                 */
5057                 function _beginpage($orientation='', $format='') {
5058                         $this->page++;
5059                         $this->pages[$this->page] = ""; // this mark should be removed before output
5060                         $this->state = 2;
5061                         if (empty($orientation)) {
5062                                 if (isset($this->CurOrientation)) {
5063                                         $orientation = $this->CurOrientation;
5064                                 } else {
5065                                         $orientation = 'P';
5066                                 }
5067                         }
5068                         if (!empty($format)) {
5069                                 $this->setPageFormat($format, $orientation);
5070                         } else {
5071                                 $this->setPageOrientation($orientation);
5072                         }
5073                         if ($this->rtl) {
5074                                 $this->x = $this->w - $this->rMargin;
5075                         } else {
5076                                 $this->x = $this->lMargin;
5077                         }
5078                         $this->y = $this->tMargin;
5079                         if ($this->newpagegroup){
5080                                 // start a new group
5081                                 $n = sizeof($this->pagegroups) + 1;
5082                                 $alias = "{nb".$n."}";
5083                                 $this->pagegroups[$alias] = 1;
5084                                 $this->currpagegroup = $alias;
5085                                 $this->newpagegroup = false;
5086                         } elseif ($this->currpagegroup) {
5087                                 $this->pagegroups[$this->currpagegroup]++;
5088                         }
5089                 }
5090
5091                 /**
5092                 * Mark end of page.
5093                 * @access protected
5094                 */
5095                 function _endpage() {
5096                         $this->setVisibility("all");
5097                         $this->state = 1;
5098                 }
5099
5100                 /**
5101                 * Begin a new object.
5102                 * @access protected
5103                 */
5104                 function _newobj() {
5105                         $this->n++;
5106                         $this->offsets[$this->n] = strlen($this->buffer);
5107                         $this->_out($this->n.' 0 obj');
5108                 }
5109
5110                 /**
5111                 * Underline text.
5112                 * @param int $x X coordinate
5113                 * @param int $y Y coordinate
5114                 * @param string $txt text to underline
5115                 * @access protected
5116                 */
5117                 function _dounderline($x, $y, $txt) {
5118                         $up = $this->CurrentFont['up'];
5119                         $ut = $this->CurrentFont['ut'];
5120                         $w = $this->GetStringWidth($txt);
5121                         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);
5122                 }
5123
5124                 /**
5125                 * Line through text.
5126                 * @param int $x X coordinate
5127                 * @param int $y Y coordinate
5128                 * @param string $txt text to underline
5129                 * @access protected
5130                 */
5131                 function _dolinethrough($x, $y, $txt) {
5132                         $up = $this->CurrentFont['up'];
5133                         $ut = $this->CurrentFont['ut'];
5134                         $w = $this->GetStringWidth($txt);
5135                         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);
5136                 }
5137
5138                 /**
5139                 * Read a 4-byte integer from file.
5140                 * @param string $f file name.
5141                 * @return 4-byte integer
5142                 * @access protected
5143                 */
5144                 function _freadint($f) {
5145                         $a = unpack('Ni', fread($f,4));
5146                         return $a['i'];
5147                 }
5148
5149                 /**
5150                 * Format a text string for meta information
5151                 * @param string $s string to escape.
5152                 * @return string escaped string.
5153                 * @access protected
5154                 */
5155                 function _textstring($s) {
5156                         if ($this->isunicode) {
5157                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5158                                         $s = $this->UTF8ToLatin1($s);
5159                                 } else {
5160                                         //Convert string to UTF-16BE
5161                                         $s = $this->UTF8ToUTF16BE($s, true);
5162                                 }
5163                         }
5164                         if ($this->encrypted) {
5165                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5166                         }
5167                         return '('. $this->_escape($s).')';
5168                 }
5169
5170                 /**
5171                 * Format an URI string
5172                 * @param string $s string to escape.
5173                 * @return string escaped string.
5174                 * @access protected
5175                 */
5176                 function _uristring($s) {
5177                         if ($this->encrypted) {
5178                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5179                         }
5180                         return '('.$this->_escape($s).')';
5181                 }
5182
5183                 /**
5184                 * Format a text string
5185                 * @param string $s string to escape.
5186                 * @return string escaped string.
5187                 * @access protected
5188                 */
5189                 function _escapetext($s) {
5190                         if ($this->isunicode) {
5191                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5192                                         $s = $this->UTF8ToLatin1($s);
5193                                 } else {
5194                                         //Convert string to UTF-16BE and reverse RTL language
5195                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
5196                                 }
5197                         }
5198                         return $this->_escape($s);
5199                 }
5200
5201                 /**
5202                 * Add "\" before "\", "(" and ")"
5203                 * @param string $s string to escape.
5204                 * @return string escaped string.
5205                 * @access protected
5206                 */
5207                 function _escape($s) {
5208                         // the chr(13) substitution fixes the Bugs item #1421290.
5209                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
5210                 }
5211
5212                 /**
5213                 * Output a stream.
5214                 * @param string $s string to output.
5215                 * @access protected
5216                 */
5217                 function _putstream($s) {
5218                         if ($this->encrypted) {
5219                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5220                         }
5221                         $this->_out('stream');
5222                         $this->_out($s);
5223                         $this->_out('endstream');
5224                 }
5225
5226                 /**
5227                 * Output a string to the document.
5228                 * @param string $s string to output.
5229                 * @access protected
5230                 */
5231                 function _out($s) {
5232                         if ($this->state == 2) {
5233                                 if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
5234                                         // puts data before page footer
5235                                         $page = substr($this->pages[$this->page], 0, -$this->footerlen[$this->page]);
5236                                         $footer = substr($this->pages[$this->page], -$this->footerlen[$this->page]);
5237                                         $this->pages[$this->page] = $page." ".$s."\n".$footer;
5238                                 } else {
5239                                         $this->pages[$this->page] .= $s."\n";
5240                                 }
5241                         } else {
5242                                 $this->buffer .= $s."\n";
5243                         }
5244                 }
5245
5246                 /**
5247                 * Adds unicode fonts.<br>
5248                 * Based on PDF Reference 1.3 (section 5)
5249                 * @access protected
5250                 * @author Nicola Asuni
5251                 * @since 1.52.0.TC005 (2005-01-05)
5252                 */
5253                 function _puttruetypeunicode($font) {
5254                         // Type0 Font
5255                         // A composite font composed of other fonts, organized hierarchically
5256                         $this->_newobj();
5257                         $this->_out('<</Type /Font');
5258                         $this->_out('/Subtype /Type0');
5259                         $this->_out('/BaseFont /'.$font['name'].'');
5260                         $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.
5261                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5262                         $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
5263                         $this->_out('>>');
5264                         $this->_out('endobj');
5265                         // CIDFontType2
5266                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5267                         $this->_newobj();
5268                         $this->_out('<</Type /Font');
5269                         $this->_out('/Subtype /CIDFontType2');
5270                         $this->_out('/BaseFont /'.$font['name'].'');
5271                         $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
5272                         $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
5273                         if (isset($font['desc']['MissingWidth'])){
5274                                 $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth
5275                         }
5276                         $w = "";
5277                         foreach ($font['cw'] as $cid => $width) {
5278                                 $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID
5279                         }
5280                         $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont
5281                         $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
5282                         $this->_out('>>');
5283                         $this->_out('endobj');
5284                         // ToUnicode
5285                         // is a stream object that contains the definition of the CMap
5286                         // (PDF Reference 1.3 chap. 5.9)
5287                         $this->_newobj();
5288                         $this->_out('<</Length 345>>');
5289                         $this->_out('stream');
5290                         $this->_out('/CIDInit /ProcSet findresource begin');
5291                         $this->_out('12 dict begin');
5292                         $this->_out('begincmap');
5293                         $this->_out('/CIDSystemInfo');
5294                         $this->_out('<</Registry (Adobe)');
5295                         $this->_out('/Ordering (UCS)');
5296                         $this->_out('/Supplement 0');
5297                         $this->_out('>> def');
5298                         $this->_out('/CMapName /Adobe-Identity-UCS def');
5299                         $this->_out('/CMapType 2 def');
5300                         $this->_out('1 begincodespacerange');
5301                         $this->_out('<0000> <FFFF>');
5302                         $this->_out('endcodespacerange');
5303                         $this->_out('1 beginbfrange');
5304                         $this->_out('<0000> <FFFF> <0000>');
5305                         $this->_out('endbfrange');
5306                         $this->_out('endcmap');
5307                         $this->_out('CMapName currentdict /CMap defineresource pop');
5308                         $this->_out('end');
5309                         $this->_out('end');
5310                         $this->_out('endstream');
5311                         $this->_out('endobj');
5312                         // CIDSystemInfo dictionary
5313                         // A dictionary containing entries that define the character collection of the CIDFont.
5314                         $this->_newobj();
5315                         $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections
5316                         $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry
5317                         $this->_out('/Supplement 0'); // The supplement number of the character collection.
5318                         $this->_out('>>');
5319                         $this->_out('endobj');
5320                         // Font descriptor
5321                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
5322                         $this->_newobj();
5323                         $this->_out('<</Type /FontDescriptor');
5324                         $this->_out('/FontName /'.$font['name']);
5325                         foreach ($font['desc'] as $key => $value) {
5326                                 $this->_out('/'.$key.' '.$value);
5327                         }
5328                         if ($font['file']) {
5329                                 // A stream containing a TrueType font program
5330                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
5331                         }
5332                         $this->_out('>>');
5333                         $this->_out('endobj');
5334                         // Embed CIDToGIDMap
5335                         // A specification of the mapping from CIDs to glyph indices
5336                         $this->_newobj();
5337                         $ctgfile = $this->_getfontpath().strtolower($font['ctg']);
5338                         if (!file_exists($ctgfile)) {
5339                                 $this->Error('Font file not found: '.$ctgfile);
5340                         }
5341                         $size = filesize($ctgfile);
5342                         $this->_out('<</Length '.$size.'');
5343                         if (substr($ctgfile, -2) == '.z') { // check file extension
5344                                 /* Decompresses data encoded using the public-domain
5345                                 zlib/deflate compression method, reproducing the
5346                                 original text or binary data */
5347                                 $this->_out('/Filter /FlateDecode');
5348                         }
5349                         $this->_out('>>');
5350                         $this->_putstream(file_get_contents($ctgfile));
5351                         $this->_out('endobj');
5352                 }
5353
5354                  /**
5355                  * Converts UTF-8 strings to codepoints array.<br>
5356                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
5357                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
5358                  * <pre>
5359                  *        Char. number range  |        UTF-8 octet sequence
5360                  *       (hexadecimal)    |              (binary)
5361                  *    --------------------+-----------------------------------------------
5362                  *    0000 0000-0000 007F | 0xxxxxxx
5363                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
5364                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
5365                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5366                  *    ---------------------------------------------------------------------
5367                  *
5368                  *   ABFN notation:
5369                  *   ---------------------------------------------------------------------
5370                  *   UTF8-octets = *( UTF8-char )
5371                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
5372                  *   UTF8-1      = %x00-7F
5373                  *   UTF8-2      = %xC2-DF UTF8-tail
5374                  *
5375                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
5376                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
5377                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
5378                  *                 %xF4 %x80-8F 2( UTF8-tail )
5379                  *   UTF8-tail   = %x80-BF
5380                  *   ---------------------------------------------------------------------
5381                  * </pre>
5382                  * @param string $str string to process.
5383                  * @return array containing codepoints (UTF-8 characters values)
5384                  * @access protected
5385                  * @author Nicola Asuni
5386                  * @since 1.53.0.TC005 (2005-01-05)
5387                  */
5388                 function UTF8StringToArray($str) {
5389                         if (!$this->isunicode) {
5390                                 // split string into array of equivalent codes
5391                                 $strarr = array();
5392                                 $strlen = strlen($str);
5393                                 for($i=0; $i < $strlen; $i++) {
5394                                         $strarr[] = ord($str{$i});
5395                                 }
5396                                 return $strarr;
5397                         }
5398                         $unicode = array(); // array containing unicode values
5399                         $bytes  = array(); // array containing single character byte sequences
5400                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
5401                         $str .= ""; // force $str to be a string
5402                         $length = strlen($str);
5403                         for($i = 0; $i < $length; $i++) {
5404                                 $char = ord($str{$i}); // get one string character at time
5405                                 if (count($bytes) == 0) { // get starting octect
5406                                         if ($char <= 0x7F) {
5407                                                 $unicode[] = $char; // use the character "as is" because is ASCII
5408                                                 $numbytes = 1;
5409                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
5410                                                 $bytes[] = ($char - 0xC0) << 0x06;
5411                                                 $numbytes = 2;
5412                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
5413                                                 $bytes[] = ($char - 0xE0) << 0x0C;
5414                                                 $numbytes = 3;
5415                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
5416                                                 $bytes[] = ($char - 0xF0) << 0x12;
5417                                                 $numbytes = 4;
5418                                         } else {
5419                                                 // use replacement character for other invalid sequences
5420                                                 $unicode[] = 0xFFFD;
5421                                                 $bytes = array();
5422                                                 $numbytes = 1;
5423                                         }
5424                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
5425                                         $bytes[] = $char - 0x80;
5426                                         if (count($bytes) == $numbytes) {
5427                                                 // compose UTF-8 bytes to a single unicode value
5428                                                 $char = $bytes[0];
5429                                                 for($j = 1; $j < $numbytes; $j++) {
5430                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
5431                                                 }
5432                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
5433                                                         /* The definition of UTF-8 prohibits encoding character numbers between
5434                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
5435                                                         encoding form (as surrogate pairs) and do not directly represent
5436                                                         characters. */
5437                                                         $unicode[] = 0xFFFD; // use replacement character
5438                                                 } else {
5439                                                         $unicode[] = $char; // add char to array
5440                                                 }
5441                                                 // reset data for next char
5442                                                 $bytes = array();
5443                                                 $numbytes = 1;
5444                                         }
5445                                 } else {
5446                                         // use replacement character for other invalid sequences
5447                                         $unicode[] = 0xFFFD;
5448                                         $bytes = array();
5449                                         $numbytes = 1;
5450                                 }
5451                         }
5452                         return $unicode;
5453                 }
5454
5455                 /**
5456                  * Converts UTF-8 strings to UTF16-BE.<br>
5457                  * @param string $str string to process.
5458                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5459                  * @return string
5460                  * @access protected
5461                  * @author Nicola Asuni
5462                  * @since 1.53.0.TC005 (2005-01-05)
5463                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
5464                  */
5465                 function UTF8ToUTF16BE($str, $setbom=true) {
5466                         if (!$this->isunicode) {
5467                                 return $str; // string is not in unicode
5468                         }
5469                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5470                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
5471                 }
5472
5473                 /**
5474                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
5475                  * @param string $str string to process.
5476                  * @return string
5477                  * @author Andrew Whitehead, Nicola Asuni
5478                  * @access protected
5479                  * @since 3.2.000 (2008-06-23)
5480                  */
5481                 function UTF8ToLatin1($str) {
5482                         if (!$this->isunicode) {
5483                                 return $str; // string is not in unicode
5484                         }
5485                         $outstr = ""; // string to be returned
5486                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5487                         foreach ($unicode as $char) {
5488                                 if ($char == 0xFFFD) {
5489                                         // skip
5490                                 } elseif ($char == 0x2022) {
5491                                         // fix for middot
5492                                         $outstr .= chr(183);
5493                                 } elseif ($char < 256) {
5494                                         $outstr .= chr($char);
5495                                 } else {
5496                                         $outstr .= '?';
5497                                 }
5498                         }
5499                         return $outstr;
5500                 }
5501
5502                 /**
5503                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
5504                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
5505                  * <pre>
5506                  *   Encoding UTF-16:
5507                  *
5508                  *   Encoding of a single character from an ISO 10646 character value to
5509                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
5510                  *    than 0x10FFFF.
5511                  *
5512                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
5513                  *       terminate.
5514                  *
5515                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
5516                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
5517                  *       represented in 20 bits.
5518                  *
5519                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
5520                  *       0xDC00, respectively. These integers each have 10 bits free to
5521                  *       encode the character value, for a total of 20 bits.
5522                  *
5523                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
5524                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
5525                  *       bits of W2. Terminate.
5526                  *
5527                  *    Graphically, steps 2 through 4 look like:
5528                  *    U' = yyyyyyyyyyxxxxxxxxxx
5529                  *    W1 = 110110yyyyyyyyyy
5530                  *    W2 = 110111xxxxxxxxxx
5531                  * </pre>
5532                  * @param array $unicode array containing UTF-8 unicode values
5533                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5534                  * @return string
5535                  * @access protected
5536                  * @author Nicola Asuni
5537                  * @since 2.1.000 (2008-01-08)
5538                  * @see UTF8ToUTF16BE()
5539                  */
5540                 function arrUTF8ToUTF16BE($unicode, $setbom=true) {
5541                         $outstr = ""; // string to be returned
5542                         if ($setbom) {
5543                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
5544                         }
5545                         foreach($unicode as $char) {
5546                                 if ($char == 0xFFFD) {
5547                                         $outstr .= "\xFF\xFD"; // replacement character
5548                                 } elseif ($char < 0x10000) {
5549                                         $outstr .= chr($char >> 0x08);
5550                                         $outstr .= chr($char & 0xFF);
5551                                 } else {
5552                                         $char -= 0x10000;
5553                                         $w1 = 0xD800 | ($char >> 0x10);
5554                                         $w2 = 0xDC00 | ($char & 0x3FF);
5555                                         $outstr .= chr($w1 >> 0x08);
5556                                         $outstr .= chr($w1 & 0xFF);
5557                                         $outstr .= chr($w2 >> 0x08);
5558                                         $outstr .= chr($w2 & 0xFF);
5559                                 }
5560                         }
5561                         return $outstr;
5562                 }
5563                 // ====================================================
5564
5565                 /**
5566                  * Set header font.
5567                  * @param array $font font
5568                  * @since 1.1
5569                  */
5570                 function setHeaderFont($font) {
5571                         $this->header_font = $font;
5572                 }
5573
5574                 /**
5575                  * Get header font.
5576                  * @return array()
5577                  * @since 4.0.012 (2008-07-24)
5578                  */
5579                 function getHeaderFont() {
5580                         return $this->header_font;
5581                 }
5582
5583                 /**
5584                  * Set footer font.
5585                  * @param array $font font
5586                  * @since 1.1
5587                  */
5588                 function setFooterFont($font) {
5589                         $this->footer_font = $font;
5590                 }
5591
5592                 /**
5593                  * Get Footer font.
5594                  * @return array()
5595                  * @since 4.0.012 (2008-07-24)
5596                  */
5597                 function getFooterFont() {
5598                         return $this->footer_font;
5599                 }
5600
5601                 /**
5602                  * Set language array.
5603                  * @param array $language
5604                  * @since 1.1
5605                  */
5606                 function setLanguageArray($language) {
5607                         $this->l = $language;
5608                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
5609                 }
5610
5611                 /**
5612                  * Returns the PDF data.
5613                  */
5614                 function getPDFData() {
5615                         if ($this->state < 3) {
5616                                 $this->Close();
5617                         }
5618                         return $this->buffer;
5619                 }
5620
5621                 /**
5622                  * Sets font style.
5623                  * @param string $tag tag name in lowercase. Supported tags are:<ul>
5624                  * <li>b : bold text</li>
5625                  * <li>i : italic</li>
5626                  * <li>u : underlined</li>
5627                  * <li>d : line-through</li></ul>
5628                  * @param boolean $enable
5629                  * @access protected
5630                  */
5631                 function setStyle($tag, $enable) {
5632                         $this->$tag += ($enable ? 1 : -1);
5633                         $style = '';
5634                         foreach(array('b', 'i', 'u', 'd') as $s) {
5635                                 if ($this->$s > 0) {
5636                                         $style .= $s;
5637                                 }
5638                         }
5639                         $this->SetFont('', $style);
5640                 }
5641
5642                 /**
5643                  * Output anchor link.
5644                  * @param string $url link URL
5645                  * @param string $name link name
5646                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
5647                  * @param boolean $firstline if true prints only the first line and return the remaining string.
5648                  * @return the number of cells used or the remaining text if $firstline = true;
5649                  * @access public
5650                  */
5651                 function addHtmlLink($url, $name, $fill=0, $firstline=false) {
5652                         $prevcolor = $this->fgcolor;
5653                         $this->SetTextColor(0, 0, 255);
5654                         $this->setStyle('u', true);
5655                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
5656                         $this->setStyle('u', false);
5657                         $this->SetTextColorArray($prevcolor);
5658                         return $ret;
5659                 }
5660
5661                 /**
5662                  * 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).
5663                  * @param string $color html color
5664                  * @return array
5665                  * @access protected
5666                  */
5667                 function convertHTMLColorToDec($color="#000000") {
5668                         global $webcolor;
5669                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
5670                         // set default color to be returned in case of error
5671                         $returncolor = array ('R' => 0, 'G' => 0, 'B' => 0);
5672                         if (empty($color)) {
5673                                 return $returncolor;
5674                         }
5675                         if (substr(strtolower($color), 0, 3) == 'rgb') {
5676                                 $codes = substr($color, 4);
5677                                 $codes = str_replace(')', '', $codes);
5678                                 $returncolor = explode(',', $codes, 3);
5679                                 return $returncolor;    
5680                         }
5681                         if (substr($color, 0, 1) != "#") {
5682                                 // decode color name
5683                                 if (isset($webcolor[strtolower($color)])) {
5684                                         $color_code = $webcolor[strtolower($color)];
5685                                 } else {
5686                                         return $returncolor;
5687                                 }
5688                         } else {
5689                                 $color_code = substr($color, 1);
5690                         }
5691                         switch (strlen($color_code)) {
5692                                 case 3: {
5693                                         // three-digit hexadecimal representation
5694                                         $r = substr($color_code, 0, 1);
5695                                         $g = substr($color_code, 1, 1);
5696                                         $b = substr($color_code, 2, 1);
5697                                         $returncolor['R'] = hexdec($r.$r);
5698                                         $returncolor['G'] = hexdec($g.$g);
5699                                         $returncolor['B'] = hexdec($b.$b);
5700                                         break;
5701                                 }
5702                                 case 6: {
5703                                         // six-digit hexadecimal representation
5704                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
5705                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
5706                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
5707                                         break;
5708                                 }
5709                         }
5710                         return $returncolor;
5711                 }
5712
5713                 /**
5714                  * Converts pixels to Units.
5715                  * @param int $px pixels
5716                  * @return float millimeters
5717                  * @access public
5718                  */
5719                 function pixelsToUnits($px){
5720                         return $px / $this->k;
5721                 }
5722
5723                 /**
5724                  * Reverse function for htmlentities.
5725                  * Convert entities in UTF-8.
5726                  * @param $text_to_convert Text to convert.
5727                  * @return string converted
5728                  */
5729                 function unhtmlentities($text_to_convert) {
5730                         if (!$this->isunicode) {
5731                                 return html_entity_decode($text_to_convert);
5732                         }
5733                         return html_entity_decode_php4($text_to_convert);
5734                 }
5735
5736                 // ENCRYPTION METHODS ----------------------------------
5737                 // SINCE 2.0.000 (2008-01-02)
5738                 /**
5739                 * Compute encryption key depending on object number where the encrypted data is stored
5740                 * @param int $n object number
5741                 * @since 2.0.000 (2008-01-02)
5742                 */
5743                 function _objectkey($n) {
5744                         return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10);
5745                 }
5746
5747                 /**
5748                  * Put encryption on PDF document.
5749                  * @since 2.0.000 (2008-01-02)
5750                  */
5751                 function _putencryption() {
5752                         $this->_out('/Filter /Standard');
5753                         $this->_out('/V 1');
5754                         $this->_out('/R 2');
5755                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
5756                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
5757                         $this->_out('/P '.$this->Pvalue);
5758                 }
5759
5760                 /**
5761                 * Returns the input text exrypted using RC4 algorithm and the specified key.
5762                 * RC4 is the standard encryption algorithm used in PDF format
5763                 * @param string $key encryption key
5764                 * @param String $text input text to be encrypted
5765                 * @return String encrypted text
5766                 * @since 2.0.000 (2008-01-02)
5767                 * @author Klemen Vodopivec
5768                 */
5769                 function _RC4($key, $text) {
5770                         if ($this->last_rc4_key != $key) {
5771                                 $k = str_repeat($key, 256/strlen($key)+1);
5772                                 $rc4 = range(0,255);
5773                                 $j = 0;
5774                                 for ($i=0; $i < 256; $i++) {
5775                                         $t = $rc4[$i];
5776                                         $j = ($j + $t + ord($k{$i})) % 256;
5777                                         $rc4[$i] = $rc4[$j];
5778                                         $rc4[$j] = $t;
5779                                 }
5780                                 $this->last_rc4_key = $key;
5781                                 $this->last_rc4_key_c = $rc4;
5782                         } else {
5783                                 $rc4 = $this->last_rc4_key_c;
5784                         }
5785                         $len = strlen($text);
5786                         $a = 0;
5787                         $b = 0;
5788                         $out = '';
5789                         for ($i=0; $i < $len; $i++) {
5790                                 $a = ($a + 1) % 256;
5791                                 $t = $rc4[$a];
5792                                 $b = ($b + $t) % 256;
5793                                 $rc4[$a] = $rc4[$b];
5794                                 $rc4[$b] = $t;
5795                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
5796                                 $out .= chr(ord($text{$i}) ^ $k);
5797                         }
5798                         return $out;
5799                 }
5800
5801                 /**
5802                 * Encrypts a string using MD5 and returns it's value as a binary string.
5803                 * @param string $str input string
5804                 * @return String MD5 encrypted binary string
5805                 * @since 2.0.000 (2008-01-02)
5806                 * @author Klemen Vodopivec
5807                 */
5808                 function _md5_16($str) {
5809                         return pack('H*',md5($str));
5810                 }
5811
5812                 /**
5813                 * Compute O value (used for RC4 encryption)
5814                 * @param String $user_pass user password
5815                 * @param String $owner_pass user password
5816                 * @return String O value
5817                 * @since 2.0.000 (2008-01-02)
5818                 * @author Klemen Vodopivec
5819                 */
5820                 function _Ovalue($user_pass, $owner_pass) {
5821                         $tmp = $this->_md5_16($owner_pass);
5822                         $owner_RC4_key = substr($tmp,0,5);
5823                         return $this->_RC4($owner_RC4_key, $user_pass);
5824                 }
5825
5826                 /**
5827                 * Compute U value (used for RC4 encryption)
5828                 * @return String U value
5829                 * @since 2.0.000 (2008-01-02)
5830                 * @author Klemen Vodopivec
5831                 */
5832                 function _Uvalue() {
5833                         return $this->_RC4($this->encryption_key, $this->padding);
5834                 }
5835
5836                 /**
5837                 * Compute encryption key
5838                 * @param String $user_pass user password
5839                 * @param String $owner_pass user password
5840                 * @param String $protection protection type
5841                 * @since 2.0.000 (2008-01-02)
5842                 * @author Klemen Vodopivec
5843                 */
5844                 function _generateencryptionkey($user_pass, $owner_pass, $protection) {
5845                         // Pad passwords
5846                         $user_pass = substr($user_pass.$this->padding,0,32);
5847                         $owner_pass = substr($owner_pass.$this->padding,0,32);
5848                         // Compute O value
5849                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
5850                         // Compute encyption key
5851                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
5852                         $this->encryption_key = substr($tmp,0,5);
5853                         // Compute U value
5854                         $this->Uvalue = $this->_Uvalue();
5855                         // Compute P value
5856                         $this->Pvalue = -(($protection^255)+1);
5857                 }
5858
5859                 /**
5860                 * Set document protection
5861                 * The permission array is composed of values taken from the following ones:
5862                 * - copy: copy text and images to the clipboard
5863                 * - print: print the document
5864                 * - modify: modify it (except for annotations and forms)
5865                 * - annot-forms: add annotations and forms
5866                 * Remark: the protection against modification is for people who have the full Acrobat product.
5867                 * 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.
5868                 * 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.
5869                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
5870                 * @param String $user_pass user password. Empty by default.
5871                 * @param String $owner_pass owner password. If not specified, a random value is used.
5872                 * @since 2.0.000 (2008-01-02)
5873                 * @author Klemen Vodopivec
5874                 */
5875                 function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
5876                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
5877                         $protection = 192;
5878                         foreach($permissions as $permission) {
5879                                 if (!isset($options[$permission])) {
5880                                         $this->Error('Incorrect permission: '.$permission);
5881                                 }
5882                                 $protection += $options[$permission];
5883                         }
5884                         if ($owner_pass === null) {
5885                                 $owner_pass = uniqid(rand());
5886                         }
5887                         $this->encrypted = true;
5888                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
5889                 }
5890
5891                 // END OF ENCRYPTION FUNCTIONS -------------------------
5892
5893                 // START TRANSFORMATIONS SECTION -----------------------
5894                 // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni
5895
5896                 /**
5897                 * Starts a 2D tranformation saving current graphic state.
5898                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
5899                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5900                 * @since 2.1.000 (2008-01-07)
5901                 * @see StartTransform(), StopTransform()
5902                 */
5903                 function StartTransform() {
5904                         $this->_out('q');
5905                 }
5906
5907                 /**
5908                 * Stops a 2D tranformation restoring previous graphic state.
5909                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
5910                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5911                 * @since 2.1.000 (2008-01-07)
5912                 * @see StartTransform(), StopTransform()
5913                 */
5914                 function StopTransform() {
5915                         $this->_out('Q');
5916                 }
5917                 /**
5918                 * Horizontal Scaling.
5919                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5920                 * @param int $x abscissa of the scaling center. Default is current x position
5921                 * @param int $y ordinate of the scaling center. Default is current y position
5922                 * @since 2.1.000 (2008-01-07)
5923                 * @see StartTransform(), StopTransform()
5924                 */
5925                 function ScaleX($s_x, $x='', $y=''){
5926                         $this->Scale($s_x, 100, $x, $y);
5927                 }
5928
5929                 /**
5930                 * Vertical Scaling.
5931                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5932                 * @param int $x abscissa of the scaling center. Default is current x position
5933                 * @param int $y ordinate of the scaling center. Default is current y position
5934                 * @since 2.1.000 (2008-01-07)
5935                 * @see StartTransform(), StopTransform()
5936                 */
5937                 function ScaleY($s_y, $x='', $y=''){
5938                         $this->Scale(100, $s_y, $x, $y);
5939                 }
5940
5941                 /**
5942                 * Vertical and horizontal proportional Scaling.
5943                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
5944                 * @param int $x abscissa of the scaling center. Default is current x position
5945                 * @param int $y ordinate of the scaling center. Default is current y position
5946                 * @since 2.1.000 (2008-01-07)
5947                 * @see StartTransform(), StopTransform()
5948                 */
5949                 function ScaleXY($s, $x='', $y=''){
5950                         $this->Scale($s, $s, $x, $y);
5951                 }
5952
5953                 /**
5954                 * Vertical and horizontal non-proportional Scaling.
5955                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5956                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5957                 * @param int $x abscissa of the scaling center. Default is current x position
5958                 * @param int $y ordinate of the scaling center. Default is current y position
5959                 * @since 2.1.000 (2008-01-07)
5960                 * @see StartTransform(), StopTransform()
5961                 */
5962                 function Scale($s_x, $s_y, $x='', $y=''){
5963                         if ($x === '') {
5964                                 $x=$this->x;
5965                         }
5966                         if ($y === '') {
5967                                 $y=$this->y;
5968                         }
5969                         if ($this->rtl) {
5970                                 $x = $this->w - $x;
5971                         }
5972                         if (($s_x == 0) OR ($s_y == 0)) {
5973                                 $this->Error('Please do not use values equal to zero for scaling');
5974                         }
5975                         $y = ($this->h - $y) * $this->k;
5976                         $x *= $this->k;
5977                         //calculate elements of transformation matrix
5978                         $s_x /= 100;
5979                         $s_y /= 100;
5980                         $tm[0] = $s_x;
5981                         $tm[1] = 0;
5982                         $tm[2] = 0;
5983                         $tm[3] = $s_y;
5984                         $tm[4] = $x * (1 - $s_x);
5985                         $tm[5] = $y * (1 - $s_y);
5986                         //scale the coordinate system
5987                         $this->Transform($tm);
5988                 }
5989
5990                 /**
5991                 * Horizontal Mirroring.
5992                 * @param int $x abscissa of the point. Default is current x position
5993                 * @since 2.1.000 (2008-01-07)
5994                 * @see StartTransform(), StopTransform()
5995                 */
5996                 function MirrorH($x=''){
5997                         $this->Scale(-100, 100, $x);
5998                 }
5999
6000                 /**
6001                 * Verical Mirroring.
6002                 * @param int $y ordinate of the point. Default is current y position
6003                 * @since 2.1.000 (2008-01-07)
6004                 * @see StartTransform(), StopTransform()
6005                 */
6006                 function MirrorV($y=''){
6007                         $this->Scale(100, -100, '', $y);
6008                 }
6009
6010                 /**
6011                 * Point reflection mirroring.
6012                 * @param int $x abscissa of the point. Default is current x position
6013                 * @param int $y ordinate of the point. Default is current y position
6014                 * @since 2.1.000 (2008-01-07)
6015                 * @see StartTransform(), StopTransform()
6016                 */
6017                 function MirrorP($x='',$y=''){
6018                         $this->Scale(-100, -100, $x, $y);
6019                 }
6020
6021                 /**
6022                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
6023                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
6024                 * @param int $x abscissa of the point. Default is current x position
6025                 * @param int $y ordinate of the point. Default is current y position
6026                 * @since 2.1.000 (2008-01-07)
6027                 * @see StartTransform(), StopTransform()
6028                 */
6029                 function MirrorL($angle=0, $x='',$y=''){
6030                         $this->Scale(-100, 100, $x, $y);
6031                         $this->Rotate(-2*($angle-90), $x, $y);
6032                 }
6033
6034                 /**
6035                 * Translate graphic object horizontally.
6036                 * @param int $t_x movement to the right (or left for RTL)
6037                 * @since 2.1.000 (2008-01-07)
6038                 * @see StartTransform(), StopTransform()
6039                 */
6040                 function TranslateX($t_x){
6041                         $this->Translate($t_x, 0);
6042                 }
6043
6044                 /**
6045                 * Translate graphic object vertically.
6046                 * @param int $t_y movement to the bottom
6047                 * @since 2.1.000 (2008-01-07)
6048                 * @see StartTransform(), StopTransform()
6049                 */
6050                 function TranslateY($t_y){
6051                         $this->Translate(0, $t_y);
6052                 }
6053
6054                 /**
6055                 * Translate graphic object horizontally and vertically.
6056                 * @param int $t_x movement to the right
6057                 * @param int $t_y movement to the bottom
6058                 * @since 2.1.000 (2008-01-07)
6059                 * @see StartTransform(), StopTransform()
6060                 */
6061                 function Translate($t_x, $t_y){
6062                         if ($this->rtl) {
6063                                 $t_x = -$t_x;
6064                         }
6065                         //calculate elements of transformation matrix
6066                         $tm[0] = 1;
6067                         $tm[1] = 0;
6068                         $tm[2] = 0;
6069                         $tm[3] = 1;
6070                         $tm[4] = $t_x * $this->k;
6071                         $tm[5] = -$t_y * $this->k;
6072                         //translate the coordinate system
6073                         $this->Transform($tm);
6074                 }
6075
6076                 /**
6077                 * Rotate object.
6078                 * @param float $angle angle in degrees for counter-clockwise rotation
6079                 * @param int $x abscissa of the rotation center. Default is current x position
6080                 * @param int $y ordinate of the rotation center. Default is current y position
6081                 * @since 2.1.000 (2008-01-07)
6082                 * @see StartTransform(), StopTransform()
6083                 */
6084                 function Rotate($angle, $x='', $y=''){
6085                         if ($x === '') {
6086                                 $x=$this->x;
6087                         }
6088                         if ($y === '') {
6089                                 $y=$this->y;
6090                         }
6091                         if ($this->rtl) {
6092                                 $x = $this->w - $x;
6093                                 $angle = -$angle;
6094                         }
6095                         $y = ($this->h - $y) * $this->k;
6096                         $x *= $this->k;
6097                         //calculate elements of transformation matrix
6098                         $tm[0] = cos(deg2rad($angle));
6099                         $tm[1] = sin(deg2rad($angle));
6100                         $tm[2] = -$tm[1];
6101                         $tm[3] = $tm[0];
6102                         $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
6103                         $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
6104                         //rotate the coordinate system around ($x,$y)
6105                         $this->Transform($tm);
6106                 }
6107
6108                 /**
6109                 * Skew horizontally.
6110                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6111                 * @param int $x abscissa of the skewing center. default is current x position
6112                 * @param int $y ordinate of the skewing center. default is current y position
6113                 * @since 2.1.000 (2008-01-07)
6114                 * @see StartTransform(), StopTransform()
6115                 */
6116                 function SkewX($angle_x, $x='', $y=''){
6117                         $this->Skew($angle_x, 0, $x, $y);
6118                 }
6119
6120                 /**
6121                 * Skew vertically.
6122                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6123                 * @param int $x abscissa of the skewing center. default is current x position
6124                 * @param int $y ordinate of the skewing center. default is current y position
6125                 * @since 2.1.000 (2008-01-07)
6126                 * @see StartTransform(), StopTransform()
6127                 */
6128                 function SkewY($angle_y, $x='', $y=''){
6129                         $this->Skew(0, $angle_y, $x, $y);
6130                 }
6131
6132                 /**
6133                 * Skew.
6134                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6135                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6136                 * @param int $x abscissa of the skewing center. default is current x position
6137                 * @param int $y ordinate of the skewing center. default is current y position
6138                 * @since 2.1.000 (2008-01-07)
6139                 * @see StartTransform(), StopTransform()
6140                 */
6141                 function Skew($angle_x, $angle_y, $x='', $y=''){
6142                         if ($x === '') {
6143                                 $x = $this->x;
6144                         }
6145                         if ($y === '') {
6146                                 $y = $this->y;
6147                         }
6148                         if ($this->rtl) {
6149                                 $x = $this->w - $x;
6150                                 $angle_x = -$angle_x;
6151                         }
6152                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
6153                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
6154                         }
6155                         $x *= $this->k;
6156                         $y = ($this->h - $y) * $this->k;
6157                         //calculate elements of transformation matrix
6158                         $tm[0] = 1;
6159                         $tm[1] = tan(deg2rad($angle_y));
6160                         $tm[2] = tan(deg2rad($angle_x));
6161                         $tm[3] = 1;
6162                         $tm[4] = -$tm[2] * $y;
6163                         $tm[5] = -$tm[1] * $x;
6164                         //skew the coordinate system
6165                         $this->Transform($tm);
6166                 }
6167
6168                 /**
6169                 * Apply graphic transformations.
6170                 * @since 2.1.000 (2008-01-07)
6171                 * @see StartTransform(), StopTransform()
6172                 */
6173                 function Transform($tm){
6174                         $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
6175                 }
6176
6177                 // END TRANSFORMATIONS SECTION -------------------------
6178
6179
6180                 // START GRAPHIC FUNCTIONS SECTION ---------------------
6181                 // The following section is based on the code provided by David Hernandez Sanz
6182
6183                 /**
6184                 * 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.
6185                 * @param float $width The width.
6186                 * @since 1.0
6187                 * @see Line(), Rect(), Cell(), MultiCell()
6188                 */
6189                 function SetLineWidth($width) {
6190                         //Set line width
6191                         $this->LineWidth = $width;
6192                         $this->linestyleWidth = sprintf('%.2f w', ($width * $this->k));
6193                         $this->_out($this->linestyleWidth);
6194                 }
6195
6196                 /**
6197                 * Returns the current the line width.
6198                 * @return int Line width
6199                 * @since 2.1.000 (2008-01-07)
6200                 * @see Line(), SetLineWidth()
6201                 */
6202                 function GetLineWidth() {
6203                         return $this->LineWidth;
6204                 }
6205
6206                 /**
6207                 * Set line style.
6208                 * @param array $style Line style. Array with keys among the following:
6209                 * <ul>
6210                 *        <li>width (float): Width of the line in user units.</li>
6211                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
6212                 * butt, round, square. The difference between "square" and "butt" is that
6213                 * "square" projects a flat end past the end of the line.</li>
6214                 *        <li>join (string): Type of join. Possible values are: miter, round,
6215                 * bevel.</li>
6216                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
6217                 * series of length values, which are the lengths of the on and off dashes.
6218                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
6219                 * 1 off, 2 on, 1 off, ...</li>
6220                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
6221                 * the point at which the pattern starts.</li>
6222                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
6223                 * </ul>
6224                 * @access public
6225                 * @since 2.1.000 (2008-01-08)
6226                 */
6227                 function SetLineStyle($style) {
6228                         extract($style);
6229                         if (isset($width)) {
6230                                 $width_prev = $this->LineWidth;
6231                                 $this->SetLineWidth($width);
6232                                 $this->LineWidth = $width_prev;
6233                         }
6234                         if (isset($cap)) {
6235                                 $ca = array("butt" => 0, "round"=> 1, "square" => 2);
6236                                 if (isset($ca[$cap])) {
6237                                         $this->linestyleCap = $ca[$cap]." J";
6238                                         $this->_out($this->linestyleCap);
6239                                 }
6240                         }
6241                         if (isset($join)) {
6242                                 $ja = array("miter" => 0, "round" => 1, "bevel" => 2);
6243                                 if (isset($ja[$join])) {
6244                                         $this->linestyleJoin = $ja[$join]." j";
6245                                         $this->_out($this->linestyleJoin);
6246                                 }
6247                         }
6248                         if (isset($dash)) {
6249                                 $dash_string = "";
6250                                 if ($dash) {
6251                                         if (ereg("^.+,", $dash)) {
6252                                                 $tab = explode(",", $dash);
6253                                         } else {
6254                                                 $tab = array($dash);
6255                                         }
6256                                         $dash_string = "";
6257                                         foreach ($tab as $i => $v) {
6258                                                 if ($i) {
6259                                                         $dash_string .= " ";
6260                                                 }
6261                                                 $dash_string .= sprintf("%.2f", $v);
6262                                         }
6263                                 }
6264                                 if (!isset($phase) OR !$dash) {
6265                                         $phase = 0;
6266                                 }
6267                                 $this->linestyleDash = sprintf("[%s] %.2f d", $dash_string, $phase);
6268                                 $this->_out($this->linestyleDash);
6269                         }
6270                         if (isset($color)) {
6271                                 $this->SetDrawColorArray($color);
6272                         }
6273                 }
6274
6275                 /*
6276                 * Set a draw point.
6277                 * @param float $x Abscissa of point.
6278                 * @param float $y Ordinate of point.
6279                 * @access protected
6280                 * @since 2.1.000 (2008-01-08)
6281                 */
6282                 function _outPoint($x, $y) {
6283                         if ($this->rtl) {
6284                                 $x = $this->w - $x;
6285                         }
6286                         $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k));
6287                 }
6288
6289                 /*
6290                 * Draws a line from last draw point.
6291                 * @param float $x Abscissa of end point.
6292                 * @param float $y Ordinate of end point.
6293                 * @access protected
6294                 * @since 2.1.000 (2008-01-08)
6295                 */
6296                 function _outLine($x, $y) {
6297                         if ($this->rtl) {
6298                                 $x = $this->w - $x;
6299                         }
6300                         $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k));
6301                 }
6302
6303                 /**
6304                 * Draws a rectangle.
6305                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6306                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6307                 * @param float $w Width.
6308                 * @param float $h Height.
6309                 * @param string $op options
6310                 * @access protected
6311                 * @since 2.1.000 (2008-01-08)
6312                 */
6313                 function _outRect($x, $y, $w, $h, $op) {
6314                         if ($this->rtl) {
6315                                 $x = $this->w - $x - $w;
6316                         }
6317                         $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
6318                 }
6319
6320                 /*
6321                 * Draws a Bezier curve from last draw point.
6322                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
6323                 * @param float $x1 Abscissa of control point 1.
6324                 * @param float $y1 Ordinate of control point 1.
6325                 * @param float $x2 Abscissa of control point 2.
6326                 * @param float $y2 Ordinate of control point 2.
6327                 * @param float $x3 Abscissa of end point.
6328                 * @param float $y3 Ordinate of end point.
6329                 * @access protected
6330                 * @since 2.1.000 (2008-01-08)
6331                 */
6332                 function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
6333                         if ($this->rtl) {
6334                                 $x1 = $this->w - $x1;
6335                                 $x2 = $this->w - $x2;
6336                                 $x3 = $this->w - $x3;
6337                         }
6338                         $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));
6339                 }
6340
6341                 /**
6342                 * Draws a line between two points.
6343                 * @param float $x1 Abscissa of first point.
6344                 * @param float $y1 Ordinate of first point.
6345                 * @param float $x2 Abscissa of second point.
6346                 * @param float $y2 Ordinate of second point.
6347                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6348                 * @access public
6349                 * @since 1.0
6350                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
6351                 */
6352                 function Line($x1, $y1, $x2, $y2, $style=array()) {
6353                         if ($style) {
6354                                 $this->SetLineStyle($style);
6355                         }
6356                         $this->_outPoint($x1, $y1);
6357                         $this->_outLine($x2, $y2);
6358                         $this->_out(" S");
6359                 }
6360
6361                 /**
6362                 * Draws a rectangle.
6363                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6364                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6365                 * @param float $w Width.
6366                 * @param float $h Height.
6367                 * @param string $style Style of rendering. Possible values are:
6368                 * <ul>
6369                 *        <li>D or empty string: Draw (default).</li>
6370                 *        <li>F: Fill.</li>
6371                 *        <li>DF or FD: Draw and fill.</li>
6372                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6373                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6374                 * </ul>
6375                 * @param array $border_style Border style of rectangle. Array with keys among the following:
6376                 * <ul>
6377                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
6378                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
6379                 * </ul>
6380                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
6381                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6382                 * @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).
6383                 * @access public
6384                 * @since 1.0
6385                 * @see SetLineStyle()
6386                 */
6387                 function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
6388                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6389                                 $this->SetFillColorArray($fill_color);
6390                         }
6391                         switch ($style) {
6392                                 case "F": {
6393                                         $op = 'f';
6394                                         $border_style = array();
6395                                         $this->_outRect($x, $y, $w, $h, $op);
6396                                         break;
6397                                 }
6398                                 case "DF":
6399                                 case "FD": {
6400                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6401                                                 $op = 'B';
6402                                                 if (isset($border_style["all"])) {
6403                                                         $this->SetLineStyle($border_style["all"]);
6404                                                         $border_style = array();
6405                                                 }
6406                                         } else {
6407                                                 $op = 'f';
6408                                         }
6409                                         $this->_outRect($x, $y, $w, $h, $op);
6410                                         break;
6411                                 }
6412                                 case "CNZ": {
6413                                         $op = "W n";
6414                                         break;
6415                                 }
6416                                 case "CEO": {
6417                                         $op = "W* n";
6418                                         break;
6419                                 }
6420                                 default: {
6421                                         $op = 'S';
6422                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6423                                                 if (isset($border_style["all"]) AND $border_style["all"]) {
6424                                                         $this->SetLineStyle($border_style["all"]);
6425                                                         $border_style = array();
6426                                                 }
6427                                                 $this->_outRect($x, $y, $w, $h, $op);
6428                                         }
6429                                         break;
6430                                 }
6431                         }
6432                         if ($border_style) {
6433                                 $border_style2 = array();
6434                                 foreach ($border_style as $line => $value) {
6435                                         $lenght = strlen($line);
6436                                         for ($i = 0; $i < $lenght; $i++) {
6437                                                 $border_style2[$line[$i]] = $value;
6438                                         }
6439                                 }
6440                                 $border_style = $border_style2;
6441                                 if (isset($border_style["L"]) AND $border_style["L"]) {
6442                                         $this->Line($x, $y, $x, $y + $h, $border_style["L"]);
6443                                 }
6444                                 if (isset($border_style["T"]) AND $border_style["T"]) {
6445                                         $this->Line($x, $y, $x + $w, $y, $border_style["T"]);
6446                                 }
6447                                 if (isset($border_style["R"]) AND $border_style["R"]) {
6448                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]);
6449                                 }
6450                                 if (isset($border_style["B"]) AND $border_style["B"]) {
6451                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]);
6452                                 }
6453                         }
6454                 }
6455
6456
6457                 /**
6458                 * Draws a Bezier curve.
6459                 * The Bezier curve is a tangent to the line between the control points at
6460                 * either end of the curve.
6461                 * @param float $x0 Abscissa of start point.
6462                 * @param float $y0 Ordinate of start point.
6463                 * @param float $x1 Abscissa of control point 1.
6464                 * @param float $y1 Ordinate of control point 1.
6465                 * @param float $x2 Abscissa of control point 2.
6466                 * @param float $y2 Ordinate of control point 2.
6467                 * @param float $x3 Abscissa of end point.
6468                 * @param float $y3 Ordinate of end point.
6469                 * @param string $style Style of rendering. Possible values are:
6470                 * <ul>
6471                 *        <li>D or empty string: Draw (default).</li>
6472                 *        <li>F: Fill.</li>
6473                 *        <li>DF or FD: Draw and fill.</li>
6474                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6475                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6476                 * </ul>
6477                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6478                 * @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).
6479                 * @access public
6480                 * @see SetLineStyle()
6481                 * @since 2.1.000 (2008-01-08)
6482                 */
6483                 function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style="", $line_style=array(), $fill_color=array()) {
6484                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6485                                 $this->SetFillColorArray($fill_color);
6486                         }
6487                         switch ($style) {
6488                                 case "F": {
6489                                         $op = "f";
6490                                         $line_style = array();
6491                                         break;
6492                                 }
6493                                 case "FD":
6494                                 case "DF": {
6495                                         $op = "B";
6496                                         break;
6497                                 }
6498                                 case "CNZ": {
6499                                         $op = "W n";
6500                                         break;
6501                                 }
6502                                 case "CEO": {
6503                                         $op = "W* n";
6504                                         break;
6505                                 }
6506                                 default: {
6507                                         $op = "S";
6508                                         break;
6509                                 }
6510                         }
6511                         if ($line_style) {
6512                                 $this->SetLineStyle($line_style);
6513                         }
6514                         $this->_outPoint($x0, $y0);
6515                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6516                         $this->_out($op);
6517                 }
6518
6519                 /**
6520                 * Draws a poly-Bezier curve.
6521                 * Each Bezier curve segment is a tangent to the line between the control points at
6522                 * either end of the curve.
6523                 * @param float $x0 Abscissa of start point.
6524                 * @param float $y0 Ordinate of start point.
6525                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
6526                 * @param string $style Style of rendering. Possible values are:
6527                 * <ul>
6528                 *        <li>D or empty string: Draw (default).</li>
6529                 *        <li>F: Fill.</li>
6530                 *        <li>DF or FD: Draw and fill.</li>
6531                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6532                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6533                 * </ul>
6534                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6535                 * @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).
6536                 * @access public
6537                 * @see SetLineStyle()
6538                 * @since 3.0008 (2008-05-12)
6539                 */
6540                 function Polycurve($x0, $y0, $segments, $style="", $line_style=array(), $fill_color=array()) {
6541                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6542                                 $this->SetFillColorArray($fill_color);
6543                         }
6544                         switch ($style) {
6545                                 case "F": {
6546                                         $op = "f";
6547                                         $line_style = array();
6548                                         break;
6549                                 }
6550                                 case "FD":
6551                                 case "DF": {
6552                                         $op = "B";
6553                                         break;
6554                                 }
6555                                 case "CNZ": {
6556                                         $op = "W n";
6557                                         break;
6558                                 }
6559                                 case "CEO": {
6560                                         $op = "W* n";
6561                                         break;
6562                                 }
6563                                 default: {
6564                                         $op = "S";
6565                                         break;
6566                                 }
6567                         }
6568                         if ($line_style) {
6569                                 $this->SetLineStyle($line_style);
6570                         }
6571                         $this->_outPoint($x0, $y0);
6572                         foreach ($segments as $segment) {
6573                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
6574                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6575                         }
6576                         $this->_out($op);
6577                 }
6578
6579                 /**
6580                 * Draws an ellipse.
6581                 * An ellipse is formed from n Bezier curves.
6582                 * @param float $x0 Abscissa of center point.
6583                 * @param float $y0 Ordinate of center point.
6584                 * @param float $rx Horizontal radius.
6585                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
6586                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6587                 * @param float $astart: Angle start of draw line. Default value: 0.
6588                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6589                 * @param string $style Style of rendering. Possible values are:
6590                 * <ul>
6591                 *        <li>D or empty string: Draw (default).</li>
6592                 *        <li>F: Fill.</li>
6593                 *        <li>DF or FD: Draw and fill.</li>
6594                 *        <li>C: Draw close.</li>
6595                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6596                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6597                 * </ul>
6598                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6599                 * @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).
6600                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
6601                 * @access public
6602                 * @since 2.1.000 (2008-01-08)
6603                 */
6604                 function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6605                         if ($angle) {
6606                                 $this->StartTransform();
6607                                 $this->Rotate($angle, $x0, $y0);
6608                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6609                                 $this->StopTransform();
6610                                 return;
6611                         }
6612                         if ($rx) {
6613                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6614                                         $this->SetFillColorArray($fill_color);
6615                                 }
6616                                 switch ($style) {
6617                                         case "F": {
6618                                                 $op = "f";
6619                                                 $line_style = array();
6620                                                 break;
6621                                         }
6622                                         case "FD":
6623                                         case "DF": {
6624                                                 $op = "B";
6625                                                 break;
6626                                         }
6627                                         case "C": {
6628                                                 $op = "s"; // Small "s" signifies closing the path as well
6629                                                 break;
6630                                         }
6631                                         case "CNZ": {
6632                                                 $op = "W n";
6633                                                 break;
6634                                         }
6635                                         case "CEO": {
6636                                                 $op = "W* n";
6637                                                 break;
6638                                         }
6639                                         default: {
6640                                                 $op = "S";
6641                                                 break;
6642                                         }
6643                                 }
6644                                 if ($line_style) {
6645                                         $this->SetLineStyle($line_style);
6646                                 }
6647                                 if (!$ry) {
6648                                         $ry = $rx;
6649                                 }
6650                                 $rx *= $this->k;
6651                                 $ry *= $this->k;
6652                                 if ($nc < 2){
6653                                         $nc = 2;
6654                                 }
6655                                 $astart = deg2rad((float) $astart);
6656                                 $afinish = deg2rad((float) $afinish);
6657                                 $total_angle = $afinish - $astart;
6658                                 $dt = $total_angle / $nc;
6659                                 $dtm = $dt / 3;
6660                                 $x0 *= $this->k;
6661                                 $y0 = ($this->h - $y0) * $this->k;
6662                                 $t1 = $astart;
6663                                 $a0 = $x0 + ($rx * cos($t1));
6664                                 $b0 = $y0 + ($ry * sin($t1));
6665                                 $c0 = -$rx * sin($t1);
6666                                 $d0 = $ry * cos($t1);
6667                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
6668                                 for ($i = 1; $i <= $nc; $i++) {
6669                                         // Draw this bit of the total curve
6670                                         $t1 = ($i * $dt) + $astart;
6671                                         $a1 = $x0 + ($rx * cos($t1));
6672                                         $b1 = $y0 + ($ry * sin($t1));
6673                                         $c1 = -$rx * sin($t1);
6674                                         $d1 = $ry * cos($t1);
6675                                         $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));
6676                                         $a0 = $a1;
6677                                         $b0 = $b1;
6678                                         $c0 = $c1;
6679                                         $d0 = $d1;
6680                                 }
6681                                 $this->_out($op);
6682                         }
6683                 }
6684
6685                 /**
6686                 * Draws a circle.
6687                 * A circle is formed from n Bezier curves.
6688                 * @param float $x0 Abscissa of center point.
6689                 * @param float $y0 Ordinate of center point.
6690                 * @param float $r Radius.
6691                 * @param float $astart: Angle start of draw line. Default value: 0.
6692                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6693                 * @param string $style Style of rendering. Possible values are:
6694                 * <ul>
6695                 *        <li>D or empty string: Draw (default).</li>
6696                 *        <li>F: Fill.</li>
6697                 *        <li>DF or FD: Draw and fill.</li>
6698                 *        <li>C: Draw close.</li>
6699                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6700                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6701                 * </ul>
6702                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6703                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6704                 * @param integer $nc Number of curves used in circle. Default value: 8.
6705                 * @access public
6706                 * @since 2.1.000 (2008-01-08)
6707                 */
6708                 function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6709                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6710                 }
6711
6712                 /**
6713                 * Draws a polygon.
6714                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
6715                 * @param string $style Style of rendering. Possible values are:
6716                 * <ul>
6717                 *        <li>D or empty string: Draw (default).</li>
6718                 *        <li>F: Fill.</li>
6719                 *        <li>DF or FD: Draw and fill.</li>
6720                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6721                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6722                 * </ul>
6723                 * @param array $line_style Line style of polygon. Array with keys among the following:
6724                 * <ul>
6725                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
6726                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
6727                 * </ul>
6728                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
6729                 * @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).
6730                 * @access public
6731                 * @since 2.1.000 (2008-01-08)
6732                 */
6733                 function Polygon($p, $style="", $line_style=array(), $fill_color=array()) {
6734                         $np = count($p) / 2;
6735                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6736                                 $this->SetFillColorArray($fill_color);
6737                         }
6738                         switch ($style) {
6739                                 case "F": {
6740                                         $line_style = array();
6741                                         $op = "f";
6742                                         break;
6743                                 }
6744                                 case "FD":
6745                                 case "DF": {
6746                                         $op = "B";
6747                                         break;
6748                                 }
6749                                 case "CNZ": {
6750                                         $op = "W n";
6751                                         break;
6752                                 }
6753                                 case "CEO": {
6754                                         $op = "W* n";
6755                                         break;
6756                                 }
6757                                 default: {
6758                                         $op = "S";
6759                                         break;
6760                                 }
6761                         }
6762                         $draw = true;
6763                         if ($line_style) {
6764                                 if (isset($line_style["all"])) {
6765                                         $this->SetLineStyle($line_style["all"]);
6766                                 } else { // 0 .. (np - 1), op = {B, S}
6767                                         $draw = false;
6768                                         if ("B" == $op) {
6769                                                 $op = "f";
6770                                                 $this->_outPoint($p[0], $p[1]);
6771                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6772                                                         $this->_outLine($p[$i], $p[$i + 1]);
6773                                                 }
6774                                                 $this->_outLine($p[0], $p[1]);
6775                                                 $this->_out($op);
6776                                         }
6777                                         $p[($np * 2)] = $p[0];
6778                                         $p[(($np * 2) + 1)] = $p[1];
6779                                         for ($i = 0; $i < $np; $i++) {
6780                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
6781                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
6782                                                 }
6783                                         }
6784                                 }
6785                         }
6786                         if ($draw) {
6787                                 $this->_outPoint($p[0], $p[1]);
6788                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6789                                         $this->_outLine($p[$i], $p[$i + 1]);
6790                                 }
6791                                 $this->_outLine($p[0], $p[1]);
6792                                 $this->_out($op);
6793                         }
6794                 }
6795
6796                 /**
6797                 * Draws a regular polygon.
6798                 * @param float $x0 Abscissa of center point.
6799                 * @param float $y0 Ordinate of center point.
6800                 * @param float $r: Radius of inscribed circle.
6801                 * @param integer $ns Number of sides.
6802                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
6803                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
6804                 * @param string $style Style of rendering. Possible values are:
6805                 * <ul>
6806                 *        <li>D or empty string: Draw (default).</li>
6807                 *        <li>F: Fill.</li>
6808                 *        <li>DF or FD: Draw and fill.</li>
6809                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6810                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6811                 * </ul>
6812                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6813                 * <ul>
6814                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
6815                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6816                 * </ul>
6817                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6818                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6819                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6820                 * <ul>
6821                 *        <li>D or empty string: Draw (default).</li>
6822                 *        <li>F: Fill.</li>
6823                 *        <li>DF or FD: Draw and fill.</li>
6824                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6825                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6826                 * </ul>
6827                 * @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).
6828                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6829                 * @access public
6830                 * @since 2.1.000 (2008-01-08)
6831                 */
6832                 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()) {
6833                         if (3 > $ns) {
6834                                 $ns = 3;
6835                         }
6836                         if ($draw_circle) {
6837                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6838                         }
6839                         $p = array();
6840                         for ($i = 0; $i < $ns; $i++) {
6841                                 $a = $angle + ($i * 360 / $ns);
6842                                 $a_rad = deg2rad((float) $a);
6843                                 $p[] = $x0 + ($r * sin($a_rad));
6844                                 $p[] = $y0 + ($r * cos($a_rad));
6845                         }
6846                         $this->Polygon($p, $style, $line_style, $fill_color);
6847                 }
6848
6849                 /**
6850                 * Draws a star polygon
6851                 * @param float $x0 Abscissa of center point.
6852                 * @param float $y0 Ordinate of center point.
6853                 * @param float $r Radius of inscribed circle.
6854                 * @param integer $nv Number of vertices.
6855                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
6856                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6857                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
6858                 * @param string $style Style of rendering. Possible values are:
6859                 * <ul>
6860                 *        <li>D or empty string: Draw (default).</li>
6861                 *        <li>F: Fill.</li>
6862                 *        <li>DF or FD: Draw and fill.</li>
6863                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6864                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6865                 * </ul>
6866                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6867                 * <ul>
6868                 *        <li>all: Line style of all sides. Array like for
6869                 * {@link SetLineStyle SetLineStyle}.</li>
6870                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6871                 * </ul>
6872                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6873                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6874                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6875                 * <ul>
6876                 *        <li>D or empty string: Draw (default).</li>
6877                 *        <li>F: Fill.</li>
6878                 *        <li>DF or FD: Draw and fill.</li>
6879                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6880                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6881                 * </ul>
6882                 * @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).
6883                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6884                 * @access public
6885                 * @since 2.1.000 (2008-01-08)
6886                 */
6887                 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()) {
6888                         if (2 > $nv) {
6889                                 $nv = 2;
6890                         }
6891                         if ($draw_circle) {
6892                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6893                         }
6894                         $p2 = array();
6895                         $visited = array();
6896                         for ($i = 0; $i < $nv; $i++) {
6897                                 $a = $angle + ($i * 360 / $nv);
6898                                 $a_rad = deg2rad((float) $a);
6899                                 $p2[] = $x0 + ($r * sin($a_rad));
6900                                 $p2[] = $y0 + ($r * cos($a_rad));
6901                                 $visited[] = false;
6902                         }
6903                         $p = array();
6904                         $i = 0;
6905                         do {
6906                                 $p[] = $p2[$i * 2];
6907                                 $p[] = $p2[($i * 2) + 1];
6908                                 $visited[$i] = true;
6909                                 $i += $ng;
6910                                 $i %= $nv;
6911                         } while (!$visited[$i]);
6912                         $this->Polygon($p, $style, $line_style, $fill_color);
6913                 }
6914
6915                 /**
6916                 * Draws a rounded rectangle.
6917                 * @param float $x Abscissa of upper-left corner.
6918                 * @param float $y Ordinate of upper-left corner.
6919                 * @param float $w Width.
6920                 * @param float $h Height.
6921                 * @param float $r Radius of the rounded corners.
6922                 * @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").
6923                 * @param string $style Style of rendering. Possible values are:
6924                 * <ul>
6925                 *        <li>D or empty string: Draw (default).</li>
6926                 *        <li>F: Fill.</li>
6927                 *        <li>DF or FD: Draw and fill.</li>
6928                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6929                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6930                 * </ul>
6931                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6932                 * @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).
6933                 * @access public
6934                 * @since 2.1.000 (2008-01-08)
6935                 */
6936                 function RoundedRect($x, $y, $w, $h, $r, $round_corner="1111", $style="", $border_style=array(), $fill_color=array()) {
6937                         if ("0000" == $round_corner) { // Not rounded
6938                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
6939                         } else { // Rounded
6940                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6941                                         $this->SetFillColorArray($fill_color);
6942                                 }
6943                                 switch ($style) {
6944                                         case "F": {
6945                                                 $border_style = array();
6946                                                 $op = "f";
6947                                                 break;
6948                                         }
6949                                         case "FD":
6950                                         case "DF": {
6951                                                 $op = "B";
6952                                                 break;
6953                                         }
6954                                         case "CNZ": {
6955                                                 $op = "W n";
6956                                                 break;
6957                                         }
6958                                         case "CEO": {
6959                                                 $op = "W* n";
6960                                                 break;
6961                                         }
6962                                         default: {
6963                                                 $op = "S";
6964                                                 break;
6965                                         }
6966                                 }
6967                                 if ($border_style) {
6968                                         $this->SetLineStyle($border_style);
6969                                 }
6970                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
6971                                 $this->_outPoint($x + $r, $y);
6972                                 $xc = $x + $w - $r;
6973                                 $yc = $y + $r;
6974                                 $this->_outLine($xc, $y);
6975                                 if ($round_corner[0]) {
6976                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
6977                                 } else {
6978                                         $this->_outLine($x + $w, $y);
6979                                 }
6980                                 $xc = $x + $w - $r;
6981                                 $yc = $y + $h - $r;
6982                                 $this->_outLine($x + $w, $yc);
6983                                 if ($round_corner[1]) {
6984                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
6985                                 } else {
6986                                         $this->_outLine($x + $w, $y + $h);
6987                                 }
6988                                 $xc = $x + $r;
6989                                 $yc = $y + $h - $r;
6990                                 $this->_outLine($xc, $y + $h);
6991                                 if ($round_corner[2]) {
6992                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
6993                                 } else {
6994                                         $this->_outLine($x, $y + $h);
6995                                 }
6996                                 $xc = $x + $r;
6997                                 $yc = $y + $r;
6998                                 $this->_outLine($x, $yc);
6999                                 if ($round_corner[3]) {
7000                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
7001                                 } else {
7002                                         $this->_outLine($x, $y);
7003                                         $this->_outLine($x + $r, $y);
7004                                 }
7005                                 $this->_out($op);
7006                         }
7007                 }
7008
7009                 // END GRAPHIC FUNCTIONS SECTION -----------------------
7010
7011                 // BIDIRECTIONAL TEXT SECTION --------------------------
7012                 /**
7013                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7014                  * @param string $str string to manipulate.
7015                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7016                  * @return string
7017                  * @author Nicola Asuni
7018                  * @since 2.1.000 (2008-01-08)
7019                 */
7020                 function utf8StrRev($str, $setbom=false, $forcertl=false) {
7021                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $forcertl), $setbom);
7022                 }
7023
7024                 /**
7025                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7026                  * @param array $ta array of characters composing the string.
7027                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7028                  * @return string
7029                  * @author Nicola Asuni
7030                  * @since 2.4.000 (2008-03-06)
7031                 */
7032                 function utf8Bidi($ta, $forcertl=false) {
7033                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
7034                         // paragraph embedding level
7035                         $pel = 0;
7036                         // max level
7037                         $maxlevel = 0;
7038                         // create string from array
7039                         $str = $this->UTF8ArrSubString($ta);
7040                         // check if string contains arabic text
7041                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
7042                                 $arabic = true;
7043                         } else {
7044                                 $arabic = false;
7045                         }
7046                         // check if string contains RTL text
7047                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
7048                                 return $ta;
7049                         }
7050                         
7051                         // get number of chars
7052                         $numchars = count($ta);
7053                         
7054                         if ($forcertl == 'R') {
7055                                         $pel = 1;
7056                         } elseif ($forcertl == 'L') {
7057                                         $pel = 0;
7058                         } else {
7059                                 // P2. In each paragraph, find the first character of type L, AL, or R.
7060                                 // 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.
7061                                 for ($i=0; $i < $numchars; $i++) {
7062                                         $type = $unicode[$ta[$i]];
7063                                         if ($type == 'L') {
7064                                                 $pel = 0;
7065                                                 break;
7066                                         } elseif (($type == 'AL') OR ($type == 'R')) {
7067                                                 $pel = 1;
7068                                                 break;
7069                                         }
7070                                 }
7071                         }
7072                         
7073                         // Current Embedding Level
7074                         $cel = $pel;
7075                         // directional override status
7076                         $dos = 'N';
7077                         $remember = array();
7078                         // start-of-level-run
7079                         $sor = $pel % 2 ? 'R' : 'L';
7080                         $eor = $sor;
7081
7082                         //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array()));
7083                         //$current_level = &$levels[count( $levels )-1];
7084
7085                         // Array of characters data
7086                         $chardata = Array();
7087
7088                         // 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.
7089                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
7090                         for ($i=0; $i < $numchars; $i++) {
7091                                 if ($ta[$i] == K_RLE) {
7092                                         // X2. With each RLE, compute the least greater odd embedding level.
7093                                         //      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.
7094                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7095                                         $next_level = $cel + ($cel % 2) + 1;
7096                                         if ($next_level < 62) {
7097                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
7098                                                 $cel = $next_level;
7099                                                 $dos = 'N';
7100                                                 $sor = $eor;
7101                                                 $eor = $cel % 2 ? 'R' : 'L';
7102                                         }
7103                                 } elseif ($ta[$i] == K_LRE) {
7104                                         // X3. With each LRE, compute the least greater even embedding level.
7105                                         //      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.
7106                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7107                                         $next_level = $cel + 2 - ($cel % 2);
7108                                         if ( $next_level < 62 ) {
7109                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
7110                                                 $cel = $next_level;
7111                                                 $dos = 'N';
7112                                                 $sor = $eor;
7113                                                 $eor = $cel % 2 ? 'R' : 'L';
7114                                         }
7115                                 } elseif ($ta[$i] == K_RLO) {
7116                                         // X4. With each RLO, compute the least greater odd embedding level.
7117                                         //      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.
7118                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7119                                         $next_level = $cel + ($cel % 2) + 1;
7120                                         if ($next_level < 62) {
7121                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
7122                                                 $cel = $next_level;
7123                                                 $dos = 'R';
7124                                                 $sor = $eor;
7125                                                 $eor = $cel % 2 ? 'R' : 'L';
7126                                         }
7127                                 } elseif ($ta[$i] == K_LRO) {
7128                                         // X5. With each LRO, compute the least greater even embedding level.
7129                                         //      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.
7130                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7131                                         $next_level = $cel + 2 - ($cel % 2);
7132                                         if ( $next_level < 62 ) {
7133                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
7134                                                 $cel = $next_level;
7135                                                 $dos = 'L';
7136                                                 $sor = $eor;
7137                                                 $eor = $cel % 2 ? 'R' : 'L';
7138                                         }
7139                                 } elseif ($ta[$i] == K_PDF) {
7140                                         // 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.
7141                                         if (count($remember)) {
7142                                                 $last = count($remember ) - 1;
7143                                                 if (($remember[$last]['num'] == K_RLE) OR
7144                                                           ($remember[$last]['num'] == K_LRE) OR
7145                                                           ($remember[$last]['num'] == K_RLO) OR
7146                                                           ($remember[$last]['num'] == K_LRO)) {
7147                                                         $match = array_pop($remember);
7148                                                         $cel = $match['cel'];
7149                                                         $dos = $match['dos'];
7150                                                         $sor = $eor;
7151                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
7152                                                 }
7153                                         }
7154                                 } elseif (($ta[$i] != K_RLE) AND
7155                                                                  ($ta[$i] != K_LRE) AND
7156                                                                  ($ta[$i] != K_RLO) AND
7157                                                                  ($ta[$i] != K_LRO) AND
7158                                                                  ($ta[$i] != K_PDF)) {
7159                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
7160                                         //      a. Set the level of the current character to the current embedding level.
7161                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
7162                                         if ($dos != 'N') {
7163                                                 $chardir = $dos;
7164                                         } else {
7165                                                 $chardir = $unicode[$ta[$i]];
7166                                         }
7167                                         // stores string characters and other information
7168                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
7169                                 }
7170                         } // end for each char
7171
7172                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
7173                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
7174                         // 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.
7175
7176                         // 3.3.3 Resolving Weak Types
7177                         // 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.
7178                         // Nonspacing marks are now resolved based on the previous characters.
7179                         $numchars = count($chardata);
7180
7181                         // 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.
7182                         $prevlevel = -1; // track level changes
7183                         $levcount = 0; // counts consecutive chars at the same level
7184                         for ($i=0; $i < $numchars; $i++) {
7185                                 if ($chardata[$i]['type'] == 'NSM') {
7186                                         if ($levcount) {
7187                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7188                                         } elseif ($i > 0) {
7189                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
7190                                         }
7191                                 }
7192                                 if ($chardata[$i]['level'] != $prevlevel) {
7193                                         $levcount = 0;
7194                                 } else {
7195                                         $levcount++;
7196                                 }
7197                                 $prevlevel = $chardata[$i]['level'];
7198                         }
7199
7200                         // 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.
7201                         $prevlevel = -1;
7202                         $levcount = 0;
7203                         for ($i=0; $i < $numchars; $i++) {
7204                                 if ($chardata[$i]['char'] == 'EN') {
7205                                         for ($j=$levcount; $j >= 0; $j--) {
7206                                                 if ($chardata[$j]['type'] == 'AL') {
7207                                                         $chardata[$i]['type'] = 'AN';
7208                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
7209                                                         break;
7210                                                 }
7211                                         }
7212                                 }
7213                                 if ($chardata[$i]['level'] != $prevlevel) {
7214                                         $levcount = 0;
7215                                 } else {
7216                                         $levcount++;
7217                                 }
7218                                 $prevlevel = $chardata[$i]['level'];
7219                         }
7220
7221                         // W3. Change all ALs to R.
7222                         for ($i=0; $i < $numchars; $i++) {
7223                                 if ($chardata[$i]['type'] == 'AL') {
7224                                         $chardata[$i]['type'] = 'R';
7225                                 }
7226                         }
7227
7228                         // 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.
7229                         $prevlevel = -1;
7230                         $levcount = 0;
7231                         for ($i=0; $i < $numchars; $i++) {
7232                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7233                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7234                                                 $chardata[$i]['type'] = 'EN';
7235                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7236                                                 $chardata[$i]['type'] = 'EN';
7237                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
7238                                                 $chardata[$i]['type'] = 'AN';
7239                                         }
7240                                 }
7241                                 if ($chardata[$i]['level'] != $prevlevel) {
7242                                         $levcount = 0;
7243                                 } else {
7244                                         $levcount++;
7245                                 }
7246                                 $prevlevel = $chardata[$i]['level'];
7247                         }
7248
7249                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
7250                         $prevlevel = -1;
7251                         $levcount = 0;
7252                         for ($i=0; $i < $numchars; $i++) {
7253                                 if ($chardata[$i]['type'] == 'ET') {
7254                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
7255                                                 $chardata[$i]['type'] = 'EN';
7256                                         } else {
7257                                                 $j = $i+1;
7258                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
7259                                                         if ($chardata[$j]['type'] == 'EN') {
7260                                                                 $chardata[$i]['type'] = 'EN';
7261                                                                 break;
7262                                                         } elseif ($chardata[$j]['type'] != 'ET') {
7263                                                                 break;
7264                                                         }
7265                                                         $j++;
7266                                                 }
7267                                         }
7268                                 }
7269                                 if ($chardata[$i]['level'] != $prevlevel) {
7270                                         $levcount = 0;
7271                                 } else {
7272                                         $levcount++;
7273                                 }
7274                                 $prevlevel = $chardata[$i]['level'];
7275                         }
7276
7277                         // W6. Otherwise, separators and terminators change to Other Neutral.
7278                         $prevlevel = -1;
7279                         $levcount = 0;
7280                         for ($i=0; $i < $numchars; $i++) {
7281                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
7282                                         $chardata[$i]['type'] = 'ON';
7283                                 }
7284                                 if ($chardata[$i]['level'] != $prevlevel) {
7285                                         $levcount = 0;
7286                                 } else {
7287                                         $levcount++;
7288                                 }
7289                                 $prevlevel = $chardata[$i]['level'];
7290                         }
7291
7292                         //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.
7293                         $prevlevel = -1;
7294                         $levcount = 0;
7295                         for ($i=0; $i < $numchars; $i++) {
7296                                 if ($chardata[$i]['char'] == 'EN') {
7297                                         for ($j=$levcount; $j >= 0; $j--) {
7298                                                 if ($chardata[$j]['type'] == 'L') {
7299                                                         $chardata[$i]['type'] = 'L';
7300                                                 } elseif ($chardata[$j]['type'] == 'R') {
7301                                                         break;
7302                                                 }
7303                                         }
7304                                 }
7305                                 if ($chardata[$i]['level'] != $prevlevel) {
7306                                         $levcount = 0;
7307                                 } else {
7308                                         $levcount++;
7309                                 }
7310                                 $prevlevel = $chardata[$i]['level'];
7311                         }
7312
7313                         // 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.
7314                         $prevlevel = -1;
7315                         $levcount = 0;
7316                         for ($i=0; $i < $numchars; $i++) {
7317                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7318                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7319                                                 $chardata[$i]['type'] = 'L';
7320                                         } elseif (($chardata[$i]['type'] == 'N') AND
7321                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7322                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7323                                                 $chardata[$i]['type'] = 'R';
7324                                         } elseif ($chardata[$i]['type'] == 'N') {
7325                                                 // N2. Any remaining neutrals take the embedding direction
7326                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7327                                         }
7328                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7329                                         // first char
7330                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7331                                                 $chardata[$i]['type'] = 'L';
7332                                         } elseif (($chardata[$i]['type'] == 'N') AND
7333                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
7334                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7335                                                 $chardata[$i]['type'] = 'R';
7336                                         } elseif ($chardata[$i]['type'] == 'N') {
7337                                                 // N2. Any remaining neutrals take the embedding direction
7338                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7339                                         }
7340                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
7341                                         //last char
7342                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
7343                                                 $chardata[$i]['type'] = 'L';
7344                                         } elseif (($chardata[$i]['type'] == 'N') AND
7345                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7346                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
7347                                                 $chardata[$i]['type'] = 'R';
7348                                         } elseif ($chardata[$i]['type'] == 'N') {
7349                                                 // N2. Any remaining neutrals take the embedding direction
7350                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7351                                         }
7352                                 } elseif ($chardata[$i]['type'] == 'N') {
7353                                         // N2. Any remaining neutrals take the embedding direction
7354                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
7355                                 }
7356                                 if ($chardata[$i]['level'] != $prevlevel) {
7357                                         $levcount = 0;
7358                                 } else {
7359                                         $levcount++;
7360                                 }
7361                                 $prevlevel = $chardata[$i]['level'];
7362                         }
7363
7364                         // 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.
7365                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
7366                         for ($i=0; $i < $numchars; $i++) {
7367                                 $odd = $chardata[$i]['level'] % 2;
7368                                 if ($odd) {
7369                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7370                                                 $chardata[$i]['level'] += 1;
7371                                         }
7372                                 } else {
7373                                         if ($chardata[$i]['type'] == 'R') {
7374                                                 $chardata[$i]['level'] += 1;
7375                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7376                                                 $chardata[$i]['level'] += 2;
7377                                         }
7378                                 }
7379                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
7380                         }
7381
7382                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
7383                         //      1. Segment separators,
7384                         //      2. Paragraph separators,
7385                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
7386                         //      4. Any sequence of white space characters at the end of the line.
7387                         for ($i=0; $i < $numchars; $i++) {
7388                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
7389                                         $chardata[$i]['level'] = $pel;
7390                                 } elseif ($chardata[$i]['type'] == 'WS') {
7391                                         $j = $i+1;
7392                                         while ($j < $numchars) {
7393                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
7394                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
7395                                                         $chardata[$i]['level'] = $pel;
7396                                                         break;
7397                                                 } elseif ($chardata[$j]['type'] != 'WS') {
7398                                                         break;
7399                                                 }
7400                                                 $j++;
7401                                         }
7402                                 }
7403                         }
7404
7405                         // Arabic Shaping
7406                         // 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.
7407                         if ($arabic) {
7408                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
7409                                 $alfletter = array(1570,1571,1573,1575);
7410                                 $chardata2 = $chardata;
7411                                 $laaletter = false;
7412                                 $charAL = array();
7413                                 $x = 0;
7414                                 for ($i=0; $i < $numchars; $i++) {
7415                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
7416                                                 $charAL[$x] = $chardata[$i];
7417                                                 $charAL[$x]['i'] = $i;
7418                                                 $chardata[$i]['x'] = $x;
7419                                                 $x++;
7420                                         }
7421                                 }
7422                                 $numAL = $x;
7423                                 for ($i=0; $i < $numchars; $i++) {
7424                                         $thischar = $chardata[$i];
7425                                         if ($i > 0) {
7426                                                 $prevchar = $chardata[($i-1)];
7427                                         } else {
7428                                                 $prevchar = false;
7429                                         }
7430                                         if (($i+1) < $numchars) {
7431                                                 $nextchar = $chardata[($i+1)];
7432                                         } else {
7433                                                 $nextchar = false;
7434                                         }
7435                                         if ($unicode[$thischar['char']] == 'AL') {
7436                                                 $x = $thischar['x'];
7437                                                 if ($x > 0) {
7438                                                         $prevchar = $charAL[($x-1)];
7439                                                 } else {
7440                                                         $prevchar = false;
7441                                                 }
7442                                                 if (($x+1) < $numAL) {
7443                                                         $nextchar = $charAL[($x+1)];
7444                                                 } else {
7445                                                         $nextchar = false;
7446                                                 }
7447                                                 // if laa letter
7448                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
7449                                                         $arabicarr = $laa_array;
7450                                                         $laaletter = true;
7451                                                         if ($x > 1) {
7452                                                                 $prevchar = $charAL[($x-2)];
7453                                                         } else {
7454                                                                 $prevchar = false;
7455                                                         }
7456                                                 } else {
7457                                                         $arabicarr = $unicode_arlet;
7458                                                         $laaletter = false;
7459                                                 }
7460                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
7461                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7462                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7463                                                         ($prevchar['type'] == $thischar['type']) AND
7464                                                         ($nextchar['type'] == $thischar['type']) AND
7465                                                         ($nextchar['char'] != 1567)) {
7466                                                         if (in_array($prevchar['char'], $endedletter)) {
7467                                                                 if (isset($arabicarr[$thischar['char']][2])) {
7468                                                                         // initial
7469                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7470                                                                 }
7471                                                         } else {
7472                                                                 if (isset($arabicarr[$thischar['char']][3])) {
7473                                                                         // medial
7474                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
7475                                                                 }
7476                                                         }
7477                                                 } elseif (($nextchar !== false) AND
7478                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7479                                                         ($nextchar['type'] == $thischar['type']) AND
7480                                                         ($nextchar['char'] != 1567)) {
7481                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
7482                                                                 // initial
7483                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7484                                                         }
7485                                                 } elseif ((($prevchar !== false) AND
7486                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7487                                                         ($prevchar['type'] == $thischar['type'])) OR
7488                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
7489                                                         // final
7490                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
7491                                                                 ($chardata[$i-1]['char'] == 1604) AND
7492                                                                 ($chardata[$i-2]['char'] == 1604)) {
7493                                                                 //Allah Word
7494                                                                 // mark characters to delete with false
7495                                                                 $chardata2[$i-2]['char'] = false;
7496                                                                 $chardata2[$i-1]['char'] = false;
7497                                                                 $chardata2[$i]['char'] = 65010;
7498                                                         } else {
7499                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
7500                                                                         if (isset($arabicarr[$thischar['char']][0])) {
7501                                                                                 // isolated
7502                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7503                                                                         }
7504                                                                 } else {
7505                                                                         if (isset($arabicarr[$thischar['char']][1])) {
7506                                                                                 // final
7507                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
7508                                                                         }
7509                                                                 }
7510                                                         }
7511                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
7512                                                         // isolated
7513                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7514                                                 }
7515                                                 // if laa letter
7516                                                 if ($laaletter) {
7517                                                         // mark characters to delete with false
7518                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
7519                                                 }
7520                                         } // end if AL (Arabic Letter)
7521                                 } // end for each char
7522                                 /*
7523                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594.
7524                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
7525                                  */
7526                                 $cw = &$this->CurrentFont['cw'];
7527                                 for ($i=0; $i < ($numchars-1); $i++) {
7528                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
7529                                                 // check if the subtitution font is defined on current font
7530                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
7531                                                         $chardata2[$i]['char'] = false;
7532                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
7533                                                 }
7534                                         }
7535                                 }
7536                                 // remove marked characters
7537                                 foreach($chardata2 as $key => $value) {
7538                                         if ($value['char'] === false) {
7539                                                 unset($chardata2[$key]);
7540                                         }
7541                                 }
7542                                 $chardata = array_values($chardata2);
7543                                 $numchars = count($chardata);
7544                                 unset($chardata2);
7545                                 unset($arabicarr);
7546                                 unset($laaletter);
7547                                 unset($charAL);
7548                         }
7549
7550                         // 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.
7551                         for ($j=$maxlevel; $j > 0; $j--) {
7552                                 $ordarray = Array();
7553                                 $revarr = Array();
7554                                 $onlevel = false;
7555                                 for ($i=0; $i < $numchars; $i++) {
7556                                         if ($chardata[$i]['level'] >= $j) {
7557                                                 $onlevel = true;
7558                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
7559                                                         // 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.
7560                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
7561                                                 }
7562                                                 $revarr[] = $chardata[$i];
7563                                         } else {
7564                                                 if ($onlevel) {
7565                                                         $revarr = array_reverse($revarr);
7566                                                         $ordarray = array_merge($ordarray, $revarr);
7567                                                         $revarr = Array();
7568                                                         $onlevel = false;
7569                                                 }
7570                                                 $ordarray[] = $chardata[$i];
7571                                         }
7572                                 }
7573                                 if ($onlevel) {
7574                                         $revarr = array_reverse($revarr);
7575                                         $ordarray = array_merge($ordarray, $revarr);
7576                                 }
7577                                 $chardata = $ordarray;
7578                         }
7579
7580                         $ordarray = array();
7581                         for ($i=0; $i < $numchars; $i++) {
7582                                 $ordarray[] = $chardata[$i]['char'];
7583                         }
7584
7585                         return $ordarray;
7586                 }
7587
7588                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
7589
7590                 /*
7591                 * Adds a bookmark.
7592                 * @param string $txt bookmark description.
7593                 * @param int $level bookmark level (minimum value is 0).
7594                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
7595                 * @access public
7596                 * @author Olivier Plathey, Nicola Asuni
7597                 * @since 2.1.002 (2008-02-12)
7598                 */
7599                 function Bookmark($txt, $level=0, $y=-1) {
7600                         if ($level < 0) {
7601                                 $level = 0;
7602                         }
7603                         if (isset($this->outlines[0])) {
7604                                 $lastoutline = end($this->outlines);
7605                                 $maxlevel = $lastoutline['l'] + 1;
7606                         } else {
7607                                 $maxlevel = 0;
7608                         }
7609                         if ($level > $maxlevel) {
7610                                 $level = $maxlevel;
7611                         }
7612                         if ($y == -1) {
7613                                 $y = $this->GetY();
7614                         }
7615                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->PageNo());
7616                 }
7617
7618                 /*
7619                 * Create a bookmark PDF string.
7620                 * @access protected
7621                 * @author Olivier Plathey, Nicola Asuni
7622                 * @since 2.1.002 (2008-02-12)
7623                 */
7624                 function _putbookmarks() {
7625                         $nb = count($this->outlines);
7626                         if ($nb == 0) {
7627                                 return;
7628                         }
7629                         $lru = array();
7630                         $level = 0;
7631                         foreach($this->outlines as $i => $o) {
7632                                 if ($o['l'] > 0) {
7633                                         $parent = $lru[($o['l'] - 1)];
7634                                         //Set parent and last pointers
7635                                         $this->outlines[$i]['parent'] = $parent;
7636                                         $this->outlines[$parent]['last'] = $i;
7637                                         if ($o['l'] > $level) {
7638                                                 //Level increasing: set first pointer
7639                                                 $this->outlines[$parent]['first'] = $i;
7640                                         }
7641                                 } else {
7642                                         $this->outlines[$i]['parent'] = $nb;
7643                                 }
7644                                 if (($o['l'] <= $level) AND ($i > 0)) {
7645                                         //Set prev and next pointers
7646                                         $prev = $lru[$o['l']];
7647                                         $this->outlines[$prev]['next'] = $i;
7648                                         $this->outlines[$i]['prev'] = $prev;
7649                                 }
7650                                 $lru[$o['l']] = $i;
7651                                 $level = $o['l'];
7652                         }
7653                         //Outline items
7654                         $n = $this->n + 1;
7655                         foreach($this->outlines as $i => $o) {
7656                                 $this->_newobj();
7657                                 $this->_out('<</Title '.$this->_textstring($o['t']));
7658                                 $this->_out('/Parent '.($n+$o['parent']).' 0 R');
7659                                 if (isset($o['prev']))
7660                                 $this->_out('/Prev '.($n+$o['prev']).' 0 R');
7661                                 if (isset($o['next']))
7662                                 $this->_out('/Next '.($n+$o['next']).' 0 R');
7663                                 if (isset($o['first']))
7664                                 $this->_out('/First '.($n+$o['first']).' 0 R');
7665                                 if (isset($o['last']))
7666                                 $this->_out('/Last '.($n+$o['last']).' 0 R');
7667                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]', 1+2*$o['p'], ($this->h-$o['y'])*$this->k));
7668                                 $this->_out('/Count 0>>');
7669                                 $this->_out('endobj');
7670                         }
7671                         //Outline root
7672                         $this->_newobj();
7673                         $this->OutlineRoot=$this->n;
7674                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
7675                         $this->_out('/Last '.($n+$lru[0]).' 0 R>>');
7676                         $this->_out('endobj');
7677                 }
7678
7679
7680                 // --- JAVASCRIPT - FORMS ------------------------------
7681
7682                 /*
7683                 * Adds a javascript
7684                 * @access public
7685                 * @author Johannes Güntert, Nicola Asuni
7686                 * @since 2.1.002 (2008-02-12)
7687                 */
7688                 function IncludeJS($script) {
7689                         $this->javascript .= $script;
7690                 }
7691
7692                 /*
7693                 * Create a javascript PDF string.
7694                 * @access protected
7695                 * @author Johannes Güntert, Nicola Asuni
7696                 * @since 2.1.002 (2008-02-12)
7697                 */
7698                 function _putjavascript() {
7699                         if (empty($this->javascript)) {
7700                                 return;
7701                         }
7702                         // the following two lines are uded to avoid form fields duplication after saving
7703                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
7704                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
7705                         $this->_newobj();
7706                         $this->n_js = $this->n;
7707                         $this->_out('<<');
7708                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
7709                         $this->_out('>>');
7710                         $this->_out('endobj');
7711                         $this->_newobj();
7712                         $this->_out('<<');
7713                         $this->_out('/S /JavaScript');
7714                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
7715                         $this->_out('>>');
7716                         $this->_out('endobj');
7717                 }
7718
7719                 /*
7720                 * Convert color to javascript color.
7721                 * @param string $color color name or #RRGGBB
7722                 * @access protected
7723                 * @author Denis Van Nuffelen, Nicola Asuni
7724                 * @since 2.1.002 (2008-02-12)
7725                 */
7726                 function _JScolor($color) {
7727                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
7728                         if (substr($color,0,1) == '#') {
7729                                 return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
7730                         }
7731                         if (!in_array($color,$aColors)) {
7732                                 $this->Error('Invalid color: '.$color);
7733                         }
7734                         return 'color.'.$color;
7735                 }
7736
7737                 /*
7738                 * Adds a javascript form field.
7739                 * @param string $type field type
7740                 * @param string $name field name
7741                 * @param int $x horizontal position
7742                 * @param int $y vertical position
7743                 * @param int $w width
7744                 * @param int $h height
7745                 * @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>
7746                 * @access protected
7747                 * @author Denis Van Nuffelen, Nicola Asuni
7748                 * @since 2.1.002 (2008-02-12)
7749                 */
7750                 function _addfield($type, $name, $x, $y, $w, $h, $prop) {
7751                         // the followind avoid fields duplication after saving the document
7752                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
7753                         $k = $this->k;
7754                         $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";
7755                         $this->javascript .= "f".$name.".textSize=".$this->FontSizePt.";\n";
7756                         while (list($key, $val) = each($prop)) {
7757                                 if (strcmp(substr($key,-5),"Color") == 0) {
7758                                         $val = $this->_JScolor($val);
7759                                 } else {
7760                                         $val = "'".$val."'";
7761                                 }
7762                                 $this->javascript .= "f".$name.".".$key."=".$val.";\n";
7763                         }
7764                         $this->x += $w;
7765                         $this->javascript .= "}";
7766                 }
7767
7768                 /*
7769                 * Creates a text field
7770                 * @param string $name field name
7771                 * @param int $w width
7772                 * @param int $h height
7773                 * @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>
7774                 * @access public
7775                 * @author Denis Van Nuffelen, Nicola Asuni
7776                 * @since 2.1.002 (2008-02-12)
7777                 */
7778                 function TextField($name, $w, $h, $prop=array()) {
7779                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
7780                 }
7781
7782                 /*
7783                 * Creates a RadioButton field
7784                 * @param string $name field name
7785                 * @param int $w width
7786                 * @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>
7787                 * @access public
7788                 * @author Nicola Asuni
7789                 * @since 2.2.003 (2008-03-03)
7790                 */
7791                 function RadioButton($name, $w, $prop=array()) {
7792                         if (!isset($prop['strokeColor'])) {
7793                                 $prop['strokeColor']='black';
7794                         }
7795                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
7796                 }
7797
7798                 /*
7799                 * Creates a List-box field
7800                 * @param string $name field name
7801                 * @param int $w width
7802                 * @param int $h height
7803                 * @param array $values array containing the list of values.
7804                 * @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>
7805                 * @access public
7806                 * @author Nicola Asuni
7807                 * @since 2.2.003 (2008-03-03)
7808                 */
7809                 function ListBox($name, $w, $h, $values, $prop=array()) {
7810                         if (!isset($prop['strokeColor'])) {
7811                                 $prop['strokeColor'] = 'ltGray';
7812                         }
7813                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
7814                         $s = '';
7815                         foreach($values as $value) {
7816                                 $s .= "'".addslashes($value)."',";
7817                         }
7818                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7819                 }
7820
7821                 /*
7822                 * Creates a Combo-box field
7823                 * @param string $name field name
7824                 * @param int $w width
7825                 * @param int $h height
7826                 * @param array $values array containing the list of values.
7827                 * @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>
7828                 * @access public
7829                 * @author Denis Van Nuffelen, Nicola Asuni
7830                 * @since 2.1.002 (2008-02-12)
7831                 */
7832                 function ComboBox($name, $w, $h, $values, $prop=array()) {
7833                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
7834                         $s = '';
7835                         foreach($values as $value) {
7836                                 $s .= "'".addslashes($value)."',";
7837                         }
7838                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7839                 }
7840
7841                 /*
7842                 * Creates a CheckBox field
7843                 * @param string $name field name
7844                 * @param int $w width
7845                 * @param boolean $checked define the initial state (default = false).
7846                 * @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>
7847                 * @access public
7848                 * @author Denis Van Nuffelen, Nicola Asuni
7849                 * @since 2.1.002 (2008-02-12)
7850                 */
7851                 function CheckBox($name, $w, $checked=false, $prop=array()) {
7852                         $prop['value'] = ($checked ? 'Yes' : 'Off');
7853                         if (!isset($prop['strokeColor'])) {
7854                                 $prop['strokeColor'] = 'black';
7855                         }
7856                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
7857                 }
7858
7859                 /*
7860                 * Creates a button field
7861                 * @param string $name field name
7862                 * @param int $w width
7863                 * @param int $h height
7864                 * @param string $caption caption.
7865                 * @param string $action action triggered by the button (JavaScript code).
7866                 * @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>
7867                 * @access public
7868                 * @author Denis Van Nuffelen, Nicola Asuni
7869                 * @since 2.1.002 (2008-02-12)
7870                 */
7871                 function Button($name, $w, $h, $caption, $action, $prop=array()) {
7872                         if (!isset($prop['strokeColor'])) {
7873                                 $prop['strokeColor'] = 'black';
7874                         }
7875                         if (!isset($prop['borderStyle'])) {
7876                                 $prop['borderStyle'] = 'beveled';
7877                         }
7878                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
7879                         $this->javascript .= "f".$name.".buttonSetCaption('".addslashes($caption)."');\n";
7880                         $this->javascript .= "f".$name.".setAction('MouseUp','".addslashes($action)."');\n";
7881                         $this->javascript .= "f".$name.".highlight='push';\n";
7882                         $this->javascript .= "f".$name.".print=false;\n";
7883                 }
7884
7885                 // END JAVASCRIPT - FORMS ------------------------------
7886
7887                 /*
7888                 * Enable Write permissions for PDF Reader.
7889                 * @access protected
7890                 * @author Nicola Asuni
7891                 * @since 2.9.000 (2008-03-26)
7892                 */
7893                 function _putuserrights() {
7894                         if (!$this->ur) {
7895                                 return;
7896                         }
7897                         $this->_out('/Perms');
7898                         $this->_out('<<');
7899                         $this->_out('/UR3');
7900                         $this->_out('<<');
7901                         //$this->_out('/SubFilter/adbe.pkcs7.detached/Filter/Adobe.PPKLite/Contents');
7902                         //$this->_out('<0>');
7903                         //$this->_out('/ByteRange[0 3]');
7904                         $this->_out('/M '.$this->_datestring('D:'.date('YmdHis')));
7905                         $this->_out('/Name(TCPDF)');
7906                         $this->_out('/Reference[');
7907                         $this->_out('<<');
7908                         $this->_out('/TransformParams');
7909                         $this->_out('<<');
7910                         $this->_out('/Type/TransformParams');
7911                         $this->_out('/V/2.2');
7912                         if (!empty($this->ur_document)) {
7913                                 $this->_out('/Document['.$this->ur_document.']');
7914                         }
7915                         if (!empty($this->ur_annots)) {
7916                                 $this->_out('/Annots['.$this->ur_annots.']');
7917                         }
7918                         if (!empty($this->ur_form)) {
7919                                 $this->_out('/Form['.$this->ur_form.']');
7920                         }
7921                         if (!empty($this->ur_signature)) {
7922                                 $this->_out('/Signature['.$this->ur_signature.']');
7923                         }
7924                         $this->_out('>>');
7925                         $this->_out('/TransformMethod/UR3');
7926                         $this->_out('/Type/SigRef');
7927                         $this->_out('>>');
7928                         $this->_out(']');
7929                         $this->_out('/Type/Sig');
7930                         $this->_out('>>');
7931                         $this->_out('>>');
7932                 }
7933
7934                 /*
7935                 * Set User's Rights for PDF Reader
7936                 * Check the PDF Reference 8.7.1 Transform Methods,
7937                 * Table 8.105 Entries in the UR transform parameters dictionary
7938                 * @param boolean $enable if true enable user's rights on PDF reader
7939                 * @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.
7940                 * @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.
7941                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
7942                 * @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.
7943                 * @access public
7944                 * @author Nicola Asuni
7945                 * @since 2.9.000 (2008-03-26)
7946                 */
7947                 function setUserRights(
7948                                 $enable=true,
7949                                 $document="/FullSave",
7950                                 $annots="/Create/Delete/Modify/Copy/Import/Export",
7951                                 $form="/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate",
7952                                 $signature="/Modify") {
7953                         $this->ur = $enable;
7954                         $this->ur_document = $document;
7955                         $this->ur_annots = $annots;
7956                         $this->ur_form = $form;
7957                         $this->ur_signature = $signature;
7958                 }
7959
7960                 /*
7961                 * Create a new page group.
7962                 * NOTE: call this function before calling AddPage()
7963                 * @access public
7964                 * @since 3.0.000 (2008-03-27)
7965                 */
7966                 function startPageGroup() {
7967                         $this->newpagegroup = true;
7968                 }
7969
7970                 /*
7971                 * Return the current page in the group.
7972                 * @return current page in the group
7973                 * @access public
7974                 * @since 3.0.000 (2008-03-27)
7975                 */
7976                 function getGroupPageNo() {
7977                         return $this->pagegroups[$this->currpagegroup];
7978                 }
7979
7980                 /*
7981                 * Return the alias of the current page group
7982         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
7983                 * (will be replaced by the total number of pages in this group).
7984                 * @return alias of the current page group
7985                 * @access public
7986                 * @since 3.0.000 (2008-03-27)
7987                 */
7988                 function getPageGroupAlias() {
7989                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
7990                                 return "{".$this->currpagegroup."}";
7991             }
7992                         return $this->currpagegroup;
7993                 }
7994
7995                 /*
7996                 * Put visibility settings.
7997                 * @access protected
7998                 * @since 3.0.000 (2008-03-27)
7999                 */
8000                 function _putocg() {
8001                         $this->_newobj();
8002                         $this->n_ocg_print = $this->n;
8003                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
8004                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
8005                         $this->_out('endobj');
8006                         $this->_newobj();
8007                         $this->n_ocg_view=$this->n;
8008                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
8009                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
8010                         $this->_out('endobj');
8011                 }
8012
8013                 /*
8014                 * Set the visibility of the successive elements.
8015                 * This can be useful, for instance, to put a background
8016                 * image or color that will show on screen but won't print.
8017                 * @param string $v visibility mode. Legal values are: all, print, screen.
8018                 * @access public
8019                 * @since 3.0.000 (2008-03-27)
8020                 */
8021                 function setVisibility($v) {
8022                         if ($this->openMarkedContent) {
8023                                 // close existing open marked-content
8024                                 $this->_out('EMC');
8025                                 $this->openMarkedContent = false;
8026                         }
8027                         switch($v) {
8028                                 case "print": {
8029                                         $this->_out('/OC /OC1 BDC');
8030                                         $this->openMarkedContent = true;
8031                                         break;
8032                                 }
8033                                 case "screen": {
8034                                         $this->_out('/OC /OC2 BDC');
8035                                         $this->openMarkedContent = true;
8036                                         break;
8037                                 }
8038                                 case "all": {
8039                                         $this->_out('');
8040                                         break;
8041                                 }
8042                                 default: {
8043                                         $this->Error('Incorrect visibility: '.$v);
8044                                         break;
8045                                 }
8046                         }
8047                         $this->visibility = $v;
8048                 }
8049
8050                 /*
8051                 * Add transparency parameters to the current extgstate
8052                 * @param array $params parameters
8053                 * @return the number of extgstates
8054                 * @access protected
8055                 * @since 3.0.000 (2008-03-27)
8056                 */
8057                 function addExtGState($parms) {
8058                         $n = count($this->extgstates) + 1;
8059                         $this->extgstates[$n]['parms'] = $parms;
8060                         return $n;
8061                 }
8062
8063                 /*
8064                 * Add an extgstate
8065                 * @param array $gs extgstate
8066                 * @access protected
8067                 * @since 3.0.000 (2008-03-27)
8068                 */
8069                 function setExtGState($gs) {
8070                         $this->_out(sprintf('/GS%d gs', $gs));
8071                 }
8072
8073                 /*
8074                 * Put extgstates for object transparency
8075                 * @param array $gs extgstate
8076                 * @access protected
8077                 * @since 3.0.000 (2008-03-27)
8078                 */
8079                 function _putextgstates() {
8080                         $ne = count($this->extgstates);
8081                         for ($i = 1; $i <= $ne; $i++) {
8082                                 $this->_newobj();
8083                                 $this->extgstates[$i]['n'] = $this->n;
8084                                 $this->_out('<</Type /ExtGState');
8085                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
8086                                         $this->_out('/'.$k.' '.$v);
8087                                 }
8088                                 $this->_out('>>');
8089                                 $this->_out('endobj');
8090                         }
8091                 }
8092
8093                 /*
8094                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
8095                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
8096                 * @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
8097                 * @access public
8098                 * @since 3.0.000 (2008-03-27)
8099                 */
8100                 function setAlpha($alpha, $bm='Normal') {
8101                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
8102                         $this->setExtGState($gs);
8103                 }
8104
8105                 /*
8106                 * Set the default JPEG compression quality (1-100)
8107                 * @param int $quality JPEG quality, integer between 1 and 100
8108                 * @access public
8109                 * @since 3.0.000 (2008-03-27)
8110                 */
8111                 function setJPEGQuality($quality) {
8112                         if (($quality < 1) OR ($quality > 100)) {
8113                                 $quality = 75;
8114                         }
8115                         $this->jpeg_quality = intval($quality);
8116                 }
8117
8118                 /*
8119                 * Set the default number of columns in a row for HTML tables.
8120                 * @param int $cols number of columns
8121                 * @access public
8122                 * @since 3.0.014 (2008-06-04)
8123                 */
8124                 function setDefaultTableColumns($cols=4) {
8125                         $this->default_table_columns = intval($cols);
8126                 }
8127
8128                 /*
8129                 * Set the height of cell repect font height.
8130                 * @param int $h cell proportion respect font height (typical value = 1.25).
8131                 * @access public
8132                 * @since 3.0.014 (2008-06-04)
8133                 */
8134                 function setCellHeightRatio($h) {
8135                         $this->cell_height_ratio = $h;
8136                 }
8137
8138                 /*
8139                 * return the height of cell repect font height.
8140                 * @access public
8141                 * @since 4.0.012 (2008-07-24)
8142                 */
8143                 function getCellHeightRatio() {
8144                         return $this->cell_height_ratio;
8145                 }
8146
8147                 /*
8148                 * Set the PDF version (check PDF reference for valid values).
8149                 * Default value is 1.t
8150                 * @access public
8151                 * @since 3.1.000 (2008-06-09)
8152                 */
8153                 function setPDFVersion($version="1.7") {
8154                         $this->PDFVersion = $version;
8155                 }
8156
8157                 /*
8158                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
8159                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
8160                 * <ul>
8161                 * <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>
8162                 * <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>
8163                 * <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>
8164                 * <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>
8165                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
8166                 * <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>
8167                 * <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>
8168                 * <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>
8169                 * <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>
8170                 * <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>
8171                 * <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>
8172                 * <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>
8173                 * <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>
8174                 * <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>
8175                 * <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>
8176                 * <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>
8177                 * </ul>
8178                 * @param array $preferences array of options.
8179                 * @author Nicola Asuni
8180                 * @access public
8181                 * @since 3.1.000 (2008-06-09)
8182                 */
8183                 function setViewerPreferences($preferences) {
8184                         $this->viewer_preferences = $preferences;
8185                 }
8186
8187                 /**
8188                 * Paints a linear colour gradient.
8189                 * @param float $x abscissa of the top left corner of the rectangle.
8190                 * @param float $y ordinate of the top left corner of the rectangle.
8191                 * @param float $w width of the rectangle.
8192                 * @param float $h height of the rectangle.
8193                 * @param array $col1 first color (RGB components).
8194                 * @param array $col2 second color (RGB components).
8195                 * @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).
8196                 * @author Andreas Würmser, Nicola Asuni
8197                 * @since 3.1.000 (2008-06-09)
8198                 * @access public
8199                 */
8200                 function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
8201                         $this->Clip($x, $y, $w, $h);
8202                         $this->Gradient(2, $col1, $col2, $coords);
8203                 }
8204
8205                 /**
8206                 * Paints a radial colour gradient.
8207                 * @param float $x abscissa of the top left corner of the rectangle.
8208                 * @param float $y ordinate of the top left corner of the rectangle.
8209                 * @param float $w width of the rectangle.
8210                 * @param float $h height of the rectangle.
8211                 * @param array $col1 first color (RGB components).
8212                 * @param array $col2 second color (RGB components).
8213                 * @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.
8214                 * @author Andreas Würmser, Nicola Asuni
8215                 * @since 3.1.000 (2008-06-09)
8216                 * @access public
8217                 */
8218                 function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
8219                         $this->Clip($x, $y, $w, $h);
8220                         $this->Gradient(3, $col1, $col2, $coords);
8221                 }
8222
8223                 /**
8224                 * Paints a coons patch mesh.
8225                 * @param float $x abscissa of the top left corner of the rectangle.
8226                 * @param float $y ordinate of the top left corner of the rectangle.
8227                 * @param float $w width of the rectangle.
8228                 * @param float $h height of the rectangle.
8229                 * @param array $col1 first color (lower left corner) (RGB components).
8230                 * @param array $col2 second color (lower right corner) (RGB components).
8231                 * @param array $col3 third color (upper right corner) (RGB components).
8232                 * @param array $col4 fourth color (upper left corner) (RGB components).
8233                 * @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>
8234                 * @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
8235                 * @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
8236                 * @author Andreas Würmser, Nicola Asuni
8237                 * @since 3.1.000 (2008-06-09)
8238                 * @access public
8239                 */
8240                 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) {
8241                         $this->Clip($x, $y, $w, $h);
8242                         $n = count($this->gradients) + 1;
8243                         $this->gradients[$n]['type'] = 6; //coons patch mesh
8244                         //check the coords array if it is the simple array or the multi patch array
8245                         if (!isset($coords[0]['f'])){
8246                                 //simple array -> convert to multi patch array
8247                                 if (!isset($col1[1])) {
8248                                         $col1[1] = $col1[2] = $col1[0];
8249                                 }
8250                                 if (!isset($col2[1])) {
8251                                         $col2[1] = $col2[2] = $col2[0];
8252                                 }
8253                                 if (!isset($col3[1])) {
8254                                         $col3[1] = $col3[2] = $col3[0];
8255                                 }
8256                                 if (!isset($col4[1])) {
8257                                         $col4[1] = $col4[2] = $col4[0];
8258                                 }
8259                                 $patch_array[0]['f'] = 0;
8260                                 $patch_array[0]['points'] = $coords;
8261                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
8262                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
8263                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
8264                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
8265                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
8266                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
8267                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
8268                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
8269                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
8270                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
8271                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
8272                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
8273                         } else {
8274                                 //multi patch array
8275                                 $patch_array = $coords;
8276                         }
8277                         $bpcd = 65535; //16 BitsPerCoordinate
8278                         //build the data stream
8279                         $this->gradients[$n]['stream'] = "";
8280                         for($i=0; $i < count($patch_array); $i++) {
8281                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
8282                                 for($j=0; $j < count($patch_array[$i]['points']); $j++) {
8283                                         //each point as 16 bit
8284                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j]-$coords_min)/($coords_max-$coords_min))*$bpcd;
8285                                         if ($patch_array[$i]['points'][$j] < 0) {
8286                                                 $patch_array[$i]['points'][$j] = 0;
8287                                         }
8288                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
8289                                                 $patch_array[$i]['points'][$j] = $bpcd;
8290                                         }
8291                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]/256));
8292                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]%256));
8293                                 }
8294                                 for($j=0; $j < count($patch_array[$i]['colors']); $j++) {
8295                                         //each color component as 8 bit
8296                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
8297                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
8298                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
8299                                 }
8300                         }
8301                         //paint the gradient
8302                         $this->_out('/Sh'.$n.' sh');
8303                         //restore previous Graphic State
8304                         $this->_out('Q');
8305                 }
8306
8307                 /**
8308                 * Set a rectangular clipping area.
8309                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
8310                 * @param float $y ordinate of the top left corner of the rectangle.
8311                 * @param float $w width of the rectangle.
8312                 * @param float $h height of the rectangle.
8313                 * @author Andreas Würmser, Nicola Asuni
8314                 * @since 3.1.000 (2008-06-09)
8315                 * @access protected
8316                 */
8317                 function Clip($x, $y, $w, $h){
8318                         if ($this->rtl) {
8319                                 $x = $this->w - $x - $w;
8320                         }
8321                         //save current Graphic State
8322                         $s = 'q';
8323                         //set clipping area
8324                         $s .= sprintf(' %.2f %.2f %.2f %.2f re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
8325                         //set up transformation matrix for gradient
8326                         $s .= sprintf(' %.3f 0 0 %.3f %.3f %.3f cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
8327                         $this->_out($s);
8328                 }
8329
8330                 /**
8331                 * Output gradient.
8332                 * @param int $type type of gradient.
8333                 * @param array $col1 first color (RGB components).
8334                 * @param array $col2 second color (RGB components).
8335                 * @param array $coords array of coordinates.
8336                 * @author Andreas Würmser, Nicola Asuni
8337                 * @since 3.1.000 (2008-06-09)
8338                 * @access protected
8339                 */
8340                 function Gradient($type, $col1, $col2, $coords){
8341                         $n = count($this->gradients) + 1;
8342                         $this->gradients[$n]['type'] = $type;
8343                         if (!isset($col1[1])) {
8344                                 $col1[1]=$col1[2]=$col1[0];
8345                         }
8346                         $this->gradients[$n]['col1'] = sprintf('%.3f %.3f %.3f', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
8347                         if (!isset($col2[1])) {
8348                                 $col2[1] = $col2[2] = $col2[0];
8349                         }
8350                         $this->gradients[$n]['col2'] = sprintf('%.3f %.3f %.3f', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
8351                         $this->gradients[$n]['coords'] = $coords;
8352                         //paint the gradient
8353                         $this->_out('/Sh'.$n.' sh');
8354                         //restore previous Graphic State
8355                         $this->_out('Q');
8356                 }
8357
8358                 /**
8359                 * Output shaders.
8360                 * @author Andreas Würmser, Nicola Asuni
8361                 * @since 3.1.000 (2008-06-09)
8362                 * @access protected
8363                 */
8364                 function _putshaders() {
8365                         foreach($this->gradients as $id => $grad) {
8366                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
8367                                         $this->_newobj();
8368                                         $this->_out('<<');
8369                                         $this->_out('/FunctionType 2');
8370                                         $this->_out('/Domain [0.0 1.0]');
8371                                         $this->_out('/C0 ['.$grad['col1'].']');
8372                                         $this->_out('/C1 ['.$grad['col2'].']');
8373                                         $this->_out('/N 1');
8374                                         $this->_out('>>');
8375                                         $this->_out('endobj');
8376                                         $f1 = $this->n;
8377                                 }
8378                                 $this->_newobj();
8379                                 $this->_out('<<');
8380                                 $this->_out('/ShadingType '.$grad['type']);
8381                                 $this->_out('/ColorSpace /DeviceRGB');
8382                                 if ($grad['type'] == 2) {
8383                                         $this->_out(sprintf('/Coords [%.3f %.3f %.3f %.3f]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
8384                                         $this->_out('/Function '.$f1.' 0 R');
8385                                         $this->_out('/Extend [true true] ');
8386                                         $this->_out('>>');
8387                                 } elseif ($grad['type'] == 3) {
8388                                         //x0, y0, r0, x1, y1, r1
8389                                         //at this this time radius of inner circle is 0
8390                                         $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]));
8391                                         $this->_out('/Function '.$f1.' 0 R');
8392                                         $this->_out('/Extend [true true] ');
8393                                         $this->_out('>>');
8394                                 } elseif ($grad['type'] == 6) {
8395                                         $this->_out('/BitsPerCoordinate 16');
8396                                         $this->_out('/BitsPerComponent 8');
8397                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
8398                                         $this->_out('/BitsPerFlag 8');
8399                                         $this->_out('/Length '.strlen($grad['stream']));
8400                                         $this->_out('>>');
8401                                         $this->_putstream($grad['stream']);
8402                                 }
8403                                 $this->_out('endobj');
8404                                 $this->gradients[$id]['id'] = $this->n;
8405                         }
8406                 }
8407
8408                 /**
8409                 * Output an arc
8410                 * @author Maxime Delorme, Nicola Asuni
8411                 * @since 3.1.000 (2008-06-09)
8412                 * @access protected
8413                 */
8414                 function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
8415                         $h = $this->h;
8416                         $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));
8417                 }
8418
8419                 /**
8420                 * Draw the sector of a circle.
8421                 * It can be used for instance to render pie charts.
8422                 * @param float $xc abscissa of the center.
8423                 * @param float $yc ordinate of the center.
8424                 * @param float $r radius.
8425                 * @param float $a start angle (in degrees).
8426                 * @param float $b end angle (in degrees).
8427                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
8428                 * @param float $cw: indicates whether to go clockwise (default: true).
8429                 * @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.
8430                 * @author Maxime Delorme, Nicola Asuni
8431                 * @since 3.1.000 (2008-06-09)
8432                 * @access public
8433                 */
8434                 function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
8435                         if ($this->rtl) {
8436                                 $xc = $this->w - $xc - $w;
8437                         }
8438                         if ($cw) {
8439                                 $d = $b;
8440                                 $b = $o - $a;
8441                                 $a = $o - $d;
8442                         } else {
8443                                 $b += $o;
8444                                 $a += $o;
8445                         }
8446                         $a = ($a % 360) + 360;
8447                         $b = ($b % 360) + 360;
8448                         if ($a > $b) {
8449                                 $b +=360;
8450                         }
8451                         $b = $b / 360 * 2 * M_PI;
8452                         $a = $a / 360 * 2 * M_PI;
8453                         $d = $b - $a;
8454                         if ($d == 0 ) {
8455                                 $d = 2 * M_PI;
8456                         }
8457                         $k = $this->k;
8458                         $hp = $this->h;
8459                         if ($style=='F') {
8460                                 $op = 'f';
8461                         } elseif ($style=='FD' or $style=='DF') {
8462                                 $op = 'b';
8463                         } else {
8464                                 $op = 's';
8465                         }
8466                         if (sin($d/2)) {
8467                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
8468                         }
8469                         //first put the center
8470                         $this->_out(sprintf('%.2f %.2f m', ($xc)*$k, ($hp-$yc)*$k));
8471                         //put the first point
8472                         $this->_out(sprintf('%.2f %.2f l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
8473                         //draw the arc
8474                         if ($d < (M_PI/2)) {
8475                                 $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));
8476                         } else {
8477                                 $b = $a + $d/4;
8478                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
8479                                 $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));
8480                                 $a = $b;
8481                                 $b = $a + $d/4;
8482                                 $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));
8483                                 $a = $b;
8484                                 $b = $a + $d/4;
8485                                 $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) );
8486                                 $a = $b;
8487                                 $b = $a + $d/4;
8488                                 $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));
8489                         }
8490                         //terminate drawing
8491                         $this->_out($op);
8492                 }
8493
8494                 /**
8495                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
8496                 * Only vector drawing is supported, not text or bitmap.
8497                 * 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).
8498                 * @param string $file Name of the file containing the image.
8499                 * @param float $x Abscissa of the upper-left corner.
8500                 * @param float $y Ordinate of the upper-left corner.
8501                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
8502                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
8503                 * @param mixed $link URL or identifier returned by AddLink().
8504                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
8505                 * @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>
8506                 * @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>
8507                 * @author Valentin Schmidt, Nicola Asuni
8508                 * @since 3.1.000 (2008-06-09)
8509                 * @access public
8510                 */
8511                 function ImageEps($file, $x, $y, $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='') {
8512                         $data = file_get_contents($file);
8513                         if ($data === false) {
8514                                 $this->Error('EPS file not found: '.$file);
8515                         }
8516                         $regs = array();
8517                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
8518                         preg_match ('/%%Creator:([^\r\n]+)/', $data, $regs); # find Creator
8519                         if (count($regs) > 1) {
8520                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
8521                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
8522                                         $versexp = explode(' ', $version_str);
8523                                         $version = (float)array_pop($versexp);
8524                                         if ($version >= 9) {
8525                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
8526                                         }
8527                                 }
8528                         }
8529                         // strip binary bytes in front of PS-header
8530                         $start = strpos($data, '%!PS-Adobe');
8531                         if ($start > 0) {
8532                                 $data = substr($data, $start);
8533                         }
8534                         // find BoundingBox params
8535                         preg_match ("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
8536                         if (count($regs) > 1) {
8537                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
8538                         } else {
8539                                 $this->Error('No BoundingBox found in EPS file: '.$file);
8540                         }
8541                         $start = strpos($data, '%%EndSetup');
8542                         if ($start === false) {
8543                                 $start = strpos($data, '%%EndProlog');
8544                         }
8545                         if ($start === false) {
8546                                 $start = strpos($data, '%%BoundingBox');
8547                         }
8548                         $data = substr($data, $start);
8549                         $end = strpos($data, '%%PageTrailer');
8550                         if ($end===false) {
8551                                 $end = strpos($data, 'showpage');
8552                         }
8553                         if ($end) {
8554                                 $data = substr($data, 0, $end);
8555                         }
8556                         $k = $this->k;
8557                         if ($w > 0) {
8558                                 $scale_x = $w/(($x2-$x1)/$k);
8559                                 if ($h > 0) {
8560                                         $scale_y = $h/(($y2-$y1)/$k);
8561                                 } else {
8562                                         $scale_y = $scale_x;
8563                                         $h = ($y2-$y1)/$k * $scale_y;
8564                                 }
8565                         } else {
8566                                 if ($h > 0) {
8567                                         $scale_y = $h/(($y2-$y1)/$k);
8568                                         $scale_x = $scale_y;
8569                                         $w = ($x2-$x1)/$k * $scale_x;
8570                                 } else {
8571                                         $w = ($x2 - $x1) / $k;
8572                                         $h = ($y2 - $y1) / $k;
8573                                 }
8574                         }
8575                         // Check whether we need a new page first as this does not fit
8576                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
8577                                 // Automatic page break
8578                                 $this->AddPage($this->CurOrientation);
8579                                 // Reset coordinates to top fo next page
8580                                 //$x = $this->GetX();
8581                                 $y = $this->GetY() + $this->cMargin;
8582                         }
8583                         // set bottomcoordinates
8584                         $this->img_rb_y = $y + $h;
8585                         // set alignment
8586                         if ($this->rtl) {
8587                                 if ($palign == 'L') {
8588                                         $ximg = $this->lMargin;
8589                                         // set right side coordinate
8590                                         $this->img_rb_x = $ximg + $w;
8591                                 } elseif ($palign == 'C') {
8592                                         $ximg = ($this->w - $x - $w) / 2;
8593                                         // set right side coordinate
8594                                         $this->img_rb_x = $ximg + $w;
8595                                 } else {
8596                                         $ximg = $this->w - $x - $w;
8597                                         // set left side coordinate
8598                                         $this->img_rb_x = $ximg;
8599                                 }
8600                         } else {
8601                                 if ($palign == 'R') {
8602                                         $ximg = $this->w - $this->rMargin - $w;
8603                                         // set left side coordinate
8604                                         $this->img_rb_x = $ximg;
8605                                 } elseif ($palign == 'C') {
8606                                         $ximg = ($this->w - $x - $w) / 2;
8607                                         // set right side coordinate
8608                                         $this->img_rb_x = $ximg + $w;
8609                                 } else {
8610                                         $ximg = $x;
8611                                         // set right side coordinate
8612                                         $this->img_rb_x = $ximg + $w;
8613                                 }
8614                         }
8615                         if ($useBoundingBox){
8616                                 $dx = $ximg * $k - $x1;
8617                                 $dy = $y * $k - $y1;
8618                         } else {
8619                                 $dx = $ximg * $k;
8620                                 $dy = $y * $k;
8621                         }
8622                         // save the current graphic state
8623                         $this->_out('q');
8624                         // translate
8625                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy+($this->hPt - 2*$y*$k - ($y2-$y1))));
8626                         // scale
8627                         if (isset($scale_x)) {
8628                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1*(1-$scale_x), $y2*(1-$scale_y)));
8629                         }
8630                         // handle pc/unix/mac line endings
8631                         $lines = split("\r\n|[\r\n]", $data);
8632                         $u=0;
8633                         $cnt = count($lines);
8634                         for ($i=0; $i < $cnt; $i++) {
8635                                 $line = $lines[$i];
8636                                 if (($line == '') OR ($line{0} == '%')) {
8637                                         continue;
8638                                 }
8639                                 $len = strlen($line);
8640                                 $chunks = explode(' ', $line);
8641                                 $cmd = array_pop($chunks);
8642                                 // RGB
8643                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
8644                                         $b = array_pop($chunks);
8645                                         $g = array_pop($chunks);
8646                                         $r = array_pop($chunks);
8647                                         $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!
8648                                         continue;
8649                                 }
8650                                 switch ($cmd) {
8651                                         case 'm':
8652                                         case 'l':
8653                                         case 'v':
8654                                         case 'y':
8655                                         case 'c':
8656                                         case 'k':
8657                                         case 'K':
8658                                         case 'g':
8659                                         case 'G':
8660                                         case 's':
8661                                         case 'S':
8662                                         case 'J':
8663                                         case 'j':
8664                                         case 'w':
8665                                         case 'M':
8666                                         case 'd':
8667                                         case 'n':
8668                                         case 'v': {
8669                                                 $this->_out($line);
8670                                                 break;
8671                                         }
8672                                         case 'x': {// custom fill color
8673                                                 list($c,$m,$y,$k) = $chunks;
8674                                                 $this->_out("$c $m $y $k k");
8675                                                 break;
8676                                         }
8677                                         case 'X': { // custom stroke color
8678                                                 list($c,$m,$y,$k) = $chunks;
8679                                                 $this->_out("$c $m $y $k K");
8680                                                 break;
8681                                         }
8682                                         case 'Y':
8683                                         case 'N':
8684                                         case 'V':
8685                                         case 'L':
8686                                         case 'C': {
8687                                                 $line{$len-1} = strtolower($cmd);
8688                                                 $this->_out($line);
8689                                                 break;
8690                                         }
8691                                         case 'b':
8692                                         case 'B': {
8693                                                 $this->_out($cmd . '*');
8694                                                 break;
8695                                         }
8696                                         case 'f':
8697                                         case 'F': {
8698                                                 if ($u > 0) {
8699                                                         $isU = false;
8700                                                         $max = min($i+5, $cnt);
8701                                                         for ($j=$i+1; $j < $max; $j++)
8702                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
8703                                                         if ($isU) {
8704                                                                 $this->_out("f*");
8705                                                         }
8706                                                 } else {
8707                                                         $this->_out("f*");
8708                                                 }
8709                                                 break;
8710                                         }
8711                                         case '*u': {
8712                                                 $u++;
8713                                                 break;
8714                                         }
8715                                         case '*U': {
8716                                                 $u--;
8717                                                 break;
8718                                         }
8719                                 }
8720                         }
8721                         // restore previous graphic state
8722                         $this->_out('Q');
8723                         if ($link) {
8724                                 $this->Link($ximg, $y, $w, $h, $link);
8725                         }
8726                         // set pointer to align the successive text/objects
8727                         switch($align) {
8728                                 case 'T':{
8729                                         $this->y = $y;
8730                                         $this->x = $this->img_rb_x;
8731                                         break;
8732                                 }
8733                                 case 'M':{
8734                                         $this->y = $y + round($h/2);
8735                                         $this->x = $this->img_rb_x;
8736                                         break;
8737                                 }
8738                                 case 'B':{
8739                                         $this->y = $this->img_rb_y;
8740                                         $this->x = $this->img_rb_x;
8741                                         break;
8742                                 }
8743                                 case 'N':{
8744                                         $this->SetY($this->img_rb_y);
8745                                         break;
8746                                 }
8747                                 default:{
8748                                         break;
8749                                 }
8750                         }
8751                         $this->endlinex = $this->img_rb_x;
8752                 }
8753
8754                 /**
8755                  * Set document barcode.
8756                  * @param string $bc barcode
8757                  */
8758                 function setBarcode($bc="") {
8759                         $this->barcode = $bc;
8760                 }
8761
8762                 /**
8763                  * Get current barcode.
8764                  * @return string
8765                  * @since 4.0.012 (2008-07-24)
8766                  */
8767                 function getBarcode() {
8768                         return $this->barcode;
8769                 }
8770
8771                 /**
8772                  * Print Barcode.
8773                  * @param string $code code to print
8774                  * @param string $type type of barcode.
8775                  * @param int $x x position in user units
8776                  * @param int $y y position in user units
8777                  * @param int $w width in user units
8778                  * @param int $h height position in user units
8779                  * @param float $xres width of the smallest bar in user units
8780                  * @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>
8781                  * @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>
8782                  * @author Nicola Asuni
8783                  * @since 3.1.000 (2008-06-09)
8784                  * @access public
8785                  */
8786                 function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
8787                         if (empty($code)) {
8788                                 return;
8789                         }
8790                         $barcodeobj = new TCPDFbarcode($code, $type);
8791                         $arrcode = $barcodeobj->getBarcodeArray();
8792                         if ($arrcode === false) {
8793                                 $this->Error('Error in barcode string');
8794                         }
8795                         // set default values
8796                         if (!isset($style["position"])) {
8797                                 if ($this->rtl) {
8798                                         $style["position"] = "R";
8799                                 } else {
8800                                         $style["position"] = "L";
8801                                 }
8802                         }
8803                         if (!isset($style["padding"])) {
8804                                 $style["padding"] = 0;
8805                         }
8806                         if (!isset($style["fgcolor"])) {
8807                                 $style["fgcolor"] = array(0,0,0); // default black
8808                         }
8809                         if (!isset($style["bgcolor"])) {
8810                                 $style["bgcolor"] = false; // default transparent
8811                         }
8812                         if (!isset($style["border"])) {
8813                                 $style["border"] = false;
8814                         }
8815                         if (!isset($style["text"])) {
8816                                 $style["text"] = false;
8817                                 $fontsize = 0;
8818                         }
8819                         if ($style["text"] AND isset($style["font"])) {
8820                                 $prevFontFamily = $this->FontFamily;
8821                                 $prevFontStyle = $this->FontStyle;
8822                                 $prevFontSizePt = $this->FontSizePt;
8823                                 if (isset($style["fontsize"])) {
8824                                         $fontsize = $style["fontsize"];
8825                                 } else {
8826                                         $fontsize = 0;
8827                                 }
8828                                 $this->SetFont($style["font"], '', $fontsize);
8829                         }
8830                         if (!isset($style["stretchtext"])) {
8831                                 $style["stretchtext"] = 4;
8832                         }
8833                         // set foreground color
8834                         $prevDrawColor = $this->DrawColor;
8835                         $prevTextColor = $this->TextColor;
8836                         $this->SetDrawColorArray($style["fgcolor"]);
8837                         $this->SetTextColorArray($style["fgcolor"]);
8838                         if (empty($w) OR ($w <= 0)) {
8839                                 if ($this->rtl) {
8840                                         $w = $this->x - $this->lMargin;
8841                                 } else {
8842                                         $w = $this->w - $this->rMargin - $this->x;
8843                                 }
8844                         }
8845                         if (empty($x)) {
8846                                 $x = $this->GetX();
8847                         }
8848                         if ($this->rtl) {
8849                                 $x = $this->w - $x;
8850                         }
8851                         if (empty($y)) {
8852                                 $y = $this->GetY();
8853                         }
8854                         if (empty($xres)) {
8855                                 $xres = 0.4;
8856                         }
8857                         $fbw = ($arrcode["maxw"] * $xres) + (2 * $style["padding"]);
8858                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style["padding"]);
8859                         if (empty($h)) {
8860                                 $h = 10 + $extraspace;
8861                         }
8862                         if ((($y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
8863                                 //Automatic page break
8864                                 $x = $this->x;
8865                                 $ws = $this->ws;
8866                                 if ($ws > 0) {
8867                                         $this->ws = 0;
8868                                         $this->_out('0 Tw');
8869                                 }
8870                                 $this->AddPage($this->CurOrientation);
8871                                 if ($ws > 0) {
8872                                         $this->ws = $ws;
8873                                         $this->_out(sprintf('%.3f Tw',$ws * $k));
8874                                 }
8875                                 $this->x = $x;
8876                                 $y = $this->y;
8877                         }
8878                         // maximum bar heigth
8879                         $barh = $h - $extraspace;
8880                         switch ($style["position"]) {
8881                                 case "L": { // left
8882                                         if ($this->rtl) {
8883                                                 $xpos = $x - $w;
8884                                         } else {
8885                                                 $xpos = $x;
8886                                         }
8887                                         break;
8888                                 }
8889                                 case "C": { // center
8890                                         $xdiff = (($w - $fbw) / 2);
8891                                         if ($this->rtl) {
8892                                                 $xpos = $x - $w + $xdiff;
8893                                         } else {
8894                                                 $xpos = $x + $xdiff;
8895                                         }
8896                                         break;
8897                                 }
8898                                 case "R": { // right
8899                                         if ($this->rtl) {
8900                                                 $xpos = $x - $fbw;
8901                                         } else {
8902                                                 $xpos = $x + $w - $fbw;
8903                                         }
8904                                         break;
8905                                 }
8906                                 case "S": { // stretch
8907                                         $fbw = $w;
8908                                         $xres = ($w - (2 * $style["padding"])) / $arrcode["maxw"];
8909                                         if ($this->rtl) {
8910                                                 $xpos = $x - $w;
8911                                         } else {
8912                                                 $xpos = $x;
8913                                         }
8914                                         break;
8915                                 }
8916                         }
8917                         $xpos_rect = $xpos;
8918                         $xpos = $xpos_rect + $style["padding"];
8919                         $xpos_text = $xpos;
8920                         // barcode is always printed in LTR direction
8921                         $tempRTL = $this->rtl;
8922                         $this->rtl = false;
8923                         // print background color
8924                         if ($style["bgcolor"]) {
8925                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style["bgcolor"]);
8926                         } elseif ($style["border"]) {
8927                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
8928                         }
8929                         // print bars
8930                         if ($arrcode !== false) {
8931                                 foreach ($arrcode["bcode"] as $k => $v) {
8932                                         $bw = ($v["w"] * $xres);
8933                                         if ($v["t"]) {
8934                                                 // braw a vertical bar
8935                                                 $ypos = $y + $style["padding"] + ($v["p"] * $barh / $arrcode["maxh"]);
8936                                                 $this->Rect($xpos, $ypos, $bw, ($v["h"] * $barh  / $arrcode["maxh"]), 'DF', array("L"=>0,"T"=>0,"R"=>0,"B"=>0), $style["fgcolor"]);
8937                                         }
8938                                         $xpos += $bw;
8939                                 }
8940                         }
8941                         // print text
8942                         if ($style["text"]) {
8943                                 // print text
8944                                 $this->x = $xpos_text;
8945                                 $this->y = $y + $style["padding"] + $barh;
8946                                 $this->Cell(($arrcode["maxw"] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style["stretchtext"]);
8947                         }
8948                         // restore original direction
8949                         $this->rtl = $tempRTL;
8950                         // restore previous font
8951                         if ($style["text"] AND isset($style["font"])) {
8952                                 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
8953                         }
8954                         // restore colors
8955                         $this->DrawColor = $prevDrawColor;
8956                         $this->TextColor = $prevTextColor;
8957                         // set bottomcoordinates
8958                         $this->img_rb_y = $y + $h;
8959                         if ($this->rtl) {
8960                                 // set left side coordinate
8961                                 $this->img_rb_x = ($this->w - $x - $w);
8962                         } else {
8963                                 // set right side coordinate
8964                                 $this->img_rb_x = $x + $w;
8965                         }
8966                         // set pointer to align the successive text/objects
8967                         switch($align) {
8968                                 case 'T':{
8969                                         $this->y = $y;
8970                                         $this->x = $this->img_rb_x;
8971                                         break;
8972                                 }
8973                                 case 'M':{
8974                                         $this->y = $y + round($h/2);
8975                                         $this->x = $this->img_rb_x;
8976                                         break;
8977                                 }
8978                                 case 'B':{
8979                                         $this->y = $this->img_rb_y;
8980                                         $this->x = $this->img_rb_x;
8981                                         break;
8982                                 }
8983                                 case 'N':{
8984                                         $this->SetY($this->img_rb_y);
8985                                         break;
8986                                 }
8987                                 default:{
8988                                         break;
8989                                 }
8990                         }
8991                 }
8992
8993                 /**
8994                  * This function is DEPRECATED, please use the new write1DBarcode() function.
8995                  * @param int $x x position in user units
8996                  * @param int $y y position in user units
8997                  * @param int $w width in user units
8998                  * @param int $h height position in user units
8999                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
9000                  * @param string $style barcode style
9001                  * @param string $font font for text
9002                  * @param int $xres x resolution
9003                  * @param string $code code to print
9004                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
9005                  * @see write1DBarcode()
9006                  */
9007                 function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
9008                         // convert old settings for the new write1DBarcode() function.
9009                         $xres = 1 / $xres;
9010                         $newstyle = array(
9011                                 "position" => "L",
9012                                 "border" => false,
9013                                 "padding" => 0,
9014                                 "fgcolor" => array(0,0,0),
9015                                 "bgcolor" => false,
9016                                 "text" => true,
9017                                 "font" => $font,
9018                                 "fontsize" => 8,
9019                                 "stretchtext" => 4
9020                         );
9021                         if ($style & 1) {
9022                                 $newstyle["border"] = true;
9023                         }
9024                         if ($style & 2) {
9025                                 $newstyle["bgcolor"] = false;
9026                         }
9027                         if ($style & 4) {
9028                                 $newstyle["position"] = "C";
9029                         } elseif ($style & 8) {
9030                                 $newstyle["position"] = "L";
9031                         } elseif ($style & 16) {
9032                                 $newstyle["position"] = "R";
9033                         }
9034                         if ($style & 128) {
9035                                 $newstyle["text"] = true;
9036                         }
9037                         if ($style & 256) {
9038                                 $newstyle["stretchtext"] = 4;
9039                         }
9040                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
9041                 }
9042
9043                 /**
9044                  * Returns an array containing current margins:
9045                  * <ul>
9046                                 <li>$ret['left'] = left  margin</li>
9047                                 <li>$ret['right'] = right margin</li>
9048                                 <li>$ret['top'] = top margin</li>
9049                                 <li>$ret['bottom'] = bottom margin</li>
9050                                 <li>$ret['header'] = header margin</li>
9051                                 <li>$ret['footer'] = footer margin</li>
9052                                 <li>$ret['cell'] = cell margin</li>
9053                  * </ul>
9054                  * @return array containing all margins measures
9055                  * @since 3.2.000 (2008-06-23)
9056                  */
9057                 function getMargins() {
9058                         $ret = array(
9059                                 'left' => $this->lMargin,
9060                                 'right' => $this->rMargin,
9061                                 'top' => $this->tMargin,
9062                                 'bottom' => $this->bMargin,
9063                                 'header' => $this->header_margin,
9064                                 'footer' => $this->footer_margin,
9065                                 'cell' => $this->cMargin,
9066                         );
9067                         return $ret;
9068                 }
9069
9070                 /**
9071                  * Returns an array containing original margins:
9072                  * <ul>
9073                                 <li>$ret['left'] = left  margin</li>
9074                                 <li>$ret['right'] = right margin</li>
9075                  * </ul>
9076                  * @return array containing all margins measures
9077                  * @since 4.0.012 (2008-07-24)
9078                  */
9079                 function getOriginalMargins() {
9080                         $ret = array(
9081                                 'left' => $this->original_lMargin,
9082                                 'right' => $this->original_rMargin
9083                         );
9084                         return $ret;
9085                 }
9086
9087                 /**
9088                  * Returns the current font size.
9089                  * @return current font size
9090                  * @since 3.2.000 (2008-06-23)
9091                  */
9092                 function getFontSize() {
9093                         return $this->FontSize;
9094                 }
9095
9096                 /**
9097                  * Returns the current font size in points unit.
9098                  * @return current font size in points unit
9099                  * @since 3.2.000 (2008-06-23)
9100                  */
9101                 function getFontSizePt() {
9102                         return $this->FontSizePt;
9103                 }
9104
9105                 /**
9106                  * Prints a cell (rectangular area) with optional borders, background color and html text string.
9107                  * 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 />
9108                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
9109                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
9110                  * @param float $h Cell minimum height. The cell extends automatically if needed.
9111                  * @param float $x upper-left corner X coordinate
9112                  * @param float $y upper-left corner Y coordinate
9113                  * @param string $html html text to print. Default value: empty string.
9114                  * @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>
9115                  * @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>
9116         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
9117                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
9118                  * @param boolean $reseth if true reset the last cell height (default true).
9119                  * @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>
9120                  * @uses MultiCell()
9121                  * @see Multicell(), writeHTML()
9122                  */
9123                 function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='') {
9124                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true);
9125                 }
9126
9127                 /**
9128                  * Returns the HTML DOM array.
9129                  * <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>
9130                  * @param string $html html code
9131                  * @return array
9132                  * @since 3.2.000 (2008-06-20)
9133                  */
9134                 function getHtmlDomArray($html) {
9135                         // remove all unsupported tags (the line below lists all supported tags)
9136                         $html = strip_tags($html, "<a><b><blockquote><br><br/><dd><del><div><dl><dt><em><font><h1><h2><h3><h4><h5><h6><hr><i><img><li><ol><p><small><span><strong><sub><sup><table><td><th><tr><u><ul>");
9137                         //replace carriage returns, newlines and tabs
9138                         $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ", "\\" => "\\\\");
9139                         $html = strtr($html, $repTable);
9140                         // remove extra spaces from tables
9141                         $html = preg_replace('/[\s]*<\/table>[\s]*/', '</table>', $html);
9142                         $html = preg_replace('/[\s]*<\/tr>[\s]*/', '</tr>', $html);
9143                         $html = preg_replace('/[\s]*<tr/', '<tr', $html);
9144                         $html = preg_replace('/[\s]*<\/th>[\s]*/', '</th>', $html);
9145                         $html = preg_replace('/[\s]*<th/', '<th', $html);
9146                         $html = preg_replace('/[\s]*<\/td>[\s]*/', '</td>', $html);
9147                         $html = preg_replace('/[\s]*<td/', '<td', $html);
9148                         $html = preg_replace('/<\/th>/', '<span></span></th>', $html);
9149                         $html = preg_replace('/<\/td>/', '<span></span></td>', $html);
9150                         // pattern for generic tag
9151                         $tagpattern = '/(<[^>]+>)/Uu';
9152                         // explodes the string
9153                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9154                         // count elements
9155                         $maxel = count($a);
9156                         $key = 0;
9157                         // create an array of elements
9158                         $dom = array();
9159                         $dom[$key] = array();
9160                         // set first void element
9161                         $dom[$key]['tag'] = false;
9162                         $dom[$key]['value'] = "";
9163                         $dom[$key]['parent'] = 0;
9164                         $dom[$key]['fontname'] = $this->FontFamily;
9165                         $dom[$key]['fontstyle'] = $this->FontStyle;
9166                         $dom[$key]['fontsize'] = $this->FontSizePt;
9167                         $dom[$key]['bgcolor'] = false;
9168                         $dom[$key]['fgcolor'] = $this->fgcolor;
9169                         $dom[$key]['align'] = '';
9170                         $key++;
9171                         $level = array();
9172                         array_push($level, 0); // root
9173                         while ($key <= $maxel) {
9174                                 if ($key > 0) {
9175                                         $dom[$key] = array();
9176                                 }
9177                                 $element = $a[($key-1)];
9178                                 if (preg_match($tagpattern, $element)) {
9179                                         // html tag
9180                                         $dom[$key]['tag'] = true;
9181                                         $element = substr($element, 1, -1);
9182                                         // get tag name
9183                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
9184                                         $dom[$key]['value'] = strtolower($tag[1]);
9185                                         if ($element{0} == '/') {
9186                                                 // closing html tag
9187                                                 $dom[$key]['opening'] = false;
9188                                                 $dom[$key]['parent'] = end($level);
9189                                                 array_pop($level);
9190                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
9191                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
9192                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
9193                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
9194                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
9195                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
9196                                                 // set the number of columns in table tag
9197                                                 if (($dom[$key]['value'] == "tr") AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
9198                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
9199                                                 }
9200                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9201                                                         $dom[($dom[$key]['parent'])]['content'] = "";
9202                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; $i++) {
9203                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[($i-1)];
9204                                                         }
9205                                                         $key = $i;
9206                                                 }
9207                                         } else {
9208                                                 // opening html tag
9209                                                 $dom[$key]['opening'] = true;
9210                                                 $dom[$key]['parent'] = end($level);
9211                                                 if (substr($element, -1, 1) != '/') {
9212                                                         // not self-closing tag
9213                                                         array_push($level, $key);
9214                                                         $dom[$key]['self'] = false;
9215                                                 } else {
9216                                                         $dom[$key]['self'] = true;
9217                                                 }
9218                                                 // copy some values from parent
9219                                                 if ($key > 0) {
9220                                                         $dom[$key]['fontname'] = $dom[($dom[$key]['parent'])]['fontname'];
9221                                                         $dom[$key]['fontstyle'] = $dom[($dom[$key]['parent'])]['fontstyle'];
9222                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'];
9223                                                         $dom[$key]['bgcolor'] = $dom[($dom[$key]['parent'])]['bgcolor'];
9224                                                         $dom[$key]['fgcolor'] = $dom[($dom[$key]['parent'])]['fgcolor'];
9225                                                         $dom[$key]['align'] = $dom[($dom[$key]['parent'])]['align'];
9226                                                 }
9227                                                 // get attributes
9228                                                 preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER);
9229                                                 $dom[$key]['attribute'] = array(); // reset attribute array
9230                                                 while (list($id, $name) = each($attr_array[1])) {
9231                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
9232                                                 }
9233                                                 // split style attributes
9234                                                 if (isset($dom[$key]['attribute']['style'])) {
9235                                                         // get style attributes
9236                                                         preg_match_all('/([^:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
9237                                                         $dom[$key]['style'] = array(); // reset style attribute array
9238                                                         while (list($id, $name) = each($style_array[1])) {
9239                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
9240                                                         }
9241                                                         // --- get some style attributes ---
9242                                                         if (isset($dom[$key]['style']['font-family'])) {
9243                                                                 // font family
9244                                                                 if (isset($dom[$key]['style']['font-family'])) {
9245                                                                         $fontslist = split(",", strtolower($dom[$key]['style']['font-family']));
9246                                                                         foreach($fontslist as $font) {
9247                                                                                 $font = trim(strtolower($font));
9248                                                                                 if (in_array($font, $this->fontlist)){
9249                                                                                         $dom[$key]['fontname'] = $font;
9250                                                                                         break;
9251                                                                                 }
9252                                                                         }
9253                                                                 }
9254                                                         }
9255                                                         // font size
9256                                                         if (isset($dom[$key]['style']['font-size'])) {
9257                                                                 $fsize = trim($dom[$key]['style']['font-size']);
9258                                                                 switch ($fsize) {
9259                                                                         case 'xx-small': {
9260                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
9261                                                                                 break;
9262                                                                         }
9263                                                                         case 'x-small': {
9264                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
9265                                                                                 break;
9266                                                                         }
9267                                                                         case 'small': {
9268                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
9269                                                                                 break;
9270                                                                         }
9271                                                                         case 'medium': {
9272                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
9273                                                                                 break;
9274                                                                         }
9275                                                                         case 'large': {
9276                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
9277                                                                                 break;
9278                                                                         }
9279                                                                         case 'x-large': {
9280                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
9281                                                                                 break;
9282                                                                         }
9283                                                                         case 'xx-large': {
9284                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
9285                                                                                 break;
9286                                                                         }
9287                                                                         default: {
9288                                                                                 $dom[$key]['fontsize'] = intval($fsize);
9289                                                                         }
9290                                                                 }
9291                                                         }
9292                                                         // font style
9293                                                         $dom[$key]['fontstyle'] = "";
9294                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == "b")) {
9295                                                                 $dom[$key]['fontstyle'] .= "B";
9296                                                         }
9297                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == "i")) {
9298                                                                 $dom[$key]['fontstyle'] .= "I";
9299                                                         }
9300                                                         // font color
9301                                                         if (isset($dom[$key]['style']['color']) AND (!empty($dom[$key]['style']['color']))) {
9302                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
9303                                                         }
9304                                                         // background color
9305                                                         if (isset($dom[$key]['style']['background-color']) AND (!empty($dom[$key]['style']['background-color']))) {
9306                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
9307                                                         }
9308                                                         // text-decoration
9309                                                         if (isset($dom[$key]['style']['text-decoration'])) {
9310                                                                 $decors = explode(" ", strtolower($dom[$key]['style']['text-decoration']));
9311                                                                 foreach ($decors as $dec) {
9312                                                                         $dec = trim($dec);
9313                                                                         if ($dec{0} == "u") {
9314                                                                                 $dom[$key]['fontstyle'] .= "U";
9315                                                                         } elseif ($dec{0} == "l") {
9316                                                                                 $dom[$key]['fontstyle'] .= "D";
9317                                                                         }
9318                                                                 }
9319                                                         }
9320                                                         // check for width attribute
9321                                                         if (isset($dom[$key]['style']['width'])) {
9322                                                                 $dom[$key]['width'] = intval($dom[$key]['style']['width']);
9323                                                         }
9324                                                         // check for height attribute
9325                                                         if (isset($dom[$key]['style']['height'])) {
9326                                                                 $dom[$key]['height'] = intval($dom[$key]['style']['height']);
9327                                                         }
9328                                                         // check for text alignment
9329                                                         if (isset($dom[$key]['style']['text-align'])) {
9330                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
9331                                                         }
9332                                                 }
9333                                                 // check for font tag
9334                                                 if ($dom[$key]['value'] == "font") {
9335                                                         // font family
9336                                                         if (isset($dom[$key]['attribute']['face'])) {
9337                                                                 $fontslist = split(",", strtolower($dom[$key]['attribute']['face']));
9338                                                                 foreach($fontslist as $font) {
9339                                                                         $font = trim(strtolower($font));
9340                                                                         if (in_array($font, $this->fontlist)){
9341                                                                                 $dom[$key]['fontname'] = $font;
9342                                                                                 break;
9343                                                                         }
9344                                                                 }
9345                                                         }
9346                                                         // font size
9347                                                         if (isset($dom[$key]['attribute']['size'])) {
9348                                                                 if ($key > 0) {
9349                                                                         if ($dom[$key]['attribute']['size']{0} == "+") {
9350                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
9351                                                                         } elseif ($dom[$key]['attribute']['size']{0} == "-") {
9352                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
9353                                                                         } else {
9354                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9355                                                                         }
9356                                                                 } else {
9357                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9358                                                                 }
9359                                                         }
9360                                                 }
9361                                                 if (($dom[$key]['value'] == "ul") OR ($dom[$key]['value'] == "ol") OR ($dom[$key]['value'] == "dl")) {
9362                                                         // force natural alignment for lists
9363                                                         if ($this->rtl) {
9364                                                                 $dom[$key]['align'] = "R";
9365                                                         } else {
9366                                                                 $dom[$key]['align'] = "L";
9367                                                         }
9368                                                 }
9369                                                 if (($dom[$key]['value'] == "small") OR ($dom[$key]['value'] == "sup") OR ($dom[$key]['value'] == "sub")) {
9370                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
9371                                                 }
9372                                                 if (($dom[$key]['value'] == "strong") OR ($dom[$key]['value'] == "b")) {
9373                                                         $dom[$key]['fontstyle'] .= "B";
9374                                                 }
9375                                                 if (($dom[$key]['value'] == "em") OR ($dom[$key]['value'] == "i")) {
9376                                                         $dom[$key]['fontstyle'] .= "I";
9377                                                 }
9378                                                 if (($dom[$key]['value']{0} == "h") AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
9379                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
9380                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
9381                                                         $dom[$key]['fontstyle'] .= "B";
9382                                                 }
9383                                                 if (($dom[$key]['value'] == "table")) {
9384                                                         $dom[$key]['rows'] = 0; // number of rows
9385                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
9386                                                 }
9387                                                 if (($dom[$key]['value'] == "tr")) {
9388                                                         $dom[$key]['cols'] = 0;
9389                                                         // store the number of rows on table element
9390                                                         $dom[($dom[$key]['parent'])]['rows']++;
9391                                                         // store the TR elements IDs on table element
9392                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
9393                                                 }
9394                                                 if (($dom[$key]['value'] == "th") OR ($dom[$key]['value'] == "td")) {
9395                                                         if (isset($dom[$key]['attribute']['colspan'])) {
9396                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
9397                                                         } else {
9398                                                                 $colspan = 1;
9399                                                         }
9400                                                         $dom[$key]['attribute']['colspan'] = $colspan;
9401                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
9402                                                 }
9403                                                 // set foreground color attribute
9404                                                 if (isset($dom[$key]['attribute']['color']) AND (!empty($dom[$key]['attribute']['color']))) {
9405                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
9406                                                 }
9407                                                 // set background color attribute
9408                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!empty($dom[$key]['attribute']['bgcolor']))) {
9409                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
9410                                                 }
9411                                                 // check for width attribute
9412                                                 if (isset($dom[$key]['attribute']['width'])) {
9413                                                         $dom[$key]['width'] = intval($dom[$key]['attribute']['width']);
9414                                                 }
9415                                                 // check for height attribute
9416                                                 if (isset($dom[$key]['attribute']['height'])) {
9417                                                         $dom[$key]['height'] = intval($dom[$key]['attribute']['height']);
9418                                                 }
9419                                                 // check for text alignment
9420                                                 if (isset($dom[$key]['attribute']['align']) AND (!empty($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
9421                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
9422                                                 }
9423                                         } // end opening tag
9424                                 } else {
9425                                         // text
9426                                         $dom[$key]['tag'] = false;
9427                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
9428                                         $dom[$key]['parent'] = end($level);
9429                                         // calculate text width
9430                                         //$dom[$key]['width'] = $this->GetStringWidth($dom[$key]['value'], $dom[($dom[$key]['parent'])]['fontname'], $dom[($dom[$key]['parent'])]['fontstyle'], $dom[($dom[$key]['parent'])]['fontsize']);
9431                                 }
9432                                 $key++;
9433                         }
9434                         return $dom;
9435                 }
9436
9437                 /**
9438                  * Allows to preserve some HTML formatting (limited support).<br />
9439                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
9440                  * 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, small, span, strong, sub, sup, table, td, th, tr, u, ul,
9441                  * @param string $html text to display
9442                  * @param boolean $ln if true add a new line after text (default = true)
9443                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
9444                  * @param boolean $reseth if true reset the last cell height (default false).
9445                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
9446                  * @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>
9447                  */
9448                 function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
9449                         // store current values
9450                         $prevlMargin = $this->lMargin;
9451                         $prevrMargin = $this->rMargin;
9452                         $prevcMargin = $this->cMargin;
9453                         $prevFontFamily = $this->FontFamily;
9454                         $prevFontStyle = $this->FontStyle;
9455                         $prevFontSizePt = $this->FontSizePt;
9456                         $curfontname = $prevFontFamily;
9457                         $curfontstyle = $prevFontStyle;
9458                         $curfontsize = $prevFontSizePt;
9459                         $prevbgcolor = $this->bgcolor;
9460                         $prevfgcolor = $this->fgcolor;
9461                         $this->newline = true;
9462                         $minstartliney = $this->y;
9463                         $yshift = 0;
9464                         $startlinepage = $this->page;
9465                         $newline = true;
9466                         if (isset($this->footerlen[$this->page])) {
9467                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9468                         } else {
9469                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9470                         }
9471                         $startlinepos = $this->footerpos[$this->page];
9472                         $lalign = $align;
9473                         $plalign = $align;
9474                         if ($this->rtl) {
9475                                 $w = $this->x - $this->lMargin;
9476                         } else {
9477                                 $w = $this->w - $this->rMargin - $this->x;
9478                         }
9479                         $w -= (2 * $this->cMargin);
9480                         if ($cell) {
9481                                 if ($this->rtl) {
9482                                         $this->x -= $this->cMargin;
9483                                 } else {
9484                                         $this->x += $this->cMargin;
9485                                 }
9486                         }
9487                         $this->listindent = $this->GetStringWidth("0000");
9488                         $this->listnum = 0;
9489                         if ((empty($this->lasth))OR ($reseth)) {
9490                                 //set row height
9491                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9492                         }
9493                         $dom = $this->getHtmlDomArray($html);
9494                         $maxel = count($dom);
9495                         $key = 0;
9496                         while ($key < $maxel) {
9497                                 if ($dom[$key]['tag'] OR ($key == 0)) {
9498                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
9499                                                 $dom[$key]['align'] = ($this->rtl)?'R':'L';
9500                                         }
9501                                         // vertically align image in line
9502                                         if ((!$this->newline) AND ($dom[$key]['value'] == 'img') 
9503                                                 AND (isset($dom[$key]['attribute']['height']))
9504                                                 AND ($dom[$key]['attribute']['height'] > 0)
9505                                                 ) {
9506                                                 $this->y += (($curfontsize / $this->k) - $this->pixelsToUnits($dom[$key]['attribute']['height']));
9507                                                 $minstartliney = min($this->y, $minstartliney);
9508                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
9509                                                 // account for different font size
9510                                                 $pfontname = $curfontname;
9511                                                 $pfontstyle = $curfontstyle;
9512                                                 $pfontsize = $curfontsize;
9513                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
9514                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
9515                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
9516                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
9517                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9518                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
9519                                                         if (is_numeric($fontsize) AND ($fontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($fontsize != $curfontsize) AND (!$this->newline)) {
9520                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
9521                                                                 $minstartliney = min($this->y, $minstartliney);
9522                                                         }
9523                                                         $curfontname = $fontname;
9524                                                         $curfontstyle = $fontstyle;
9525                                                         $curfontsize = $fontsize;
9526                                                 }
9527                                         }
9528                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
9529                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
9530                                                 $wfill = true;
9531                                         } else {
9532                                                 $wfill = $fill | false;
9533                                         }
9534                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
9535                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
9536                                         }
9537                                         if (isset($dom[$key]['align'])) {
9538                                                 $lalign = $dom[$key]['align'];
9539                                         }
9540                                         if (empty($lalign)) {
9541                                                 $lalign = $align;
9542                                         }
9543                                 }
9544                                 // align lines
9545                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
9546                                         $newline = true;
9547                                         // we are at the beginning of a new line
9548                                         if (isset($startlinex)) {
9549                                                 $yshift = $minstartliney - $startliney;
9550                                                 if ($yshift > 0) {
9551                                                         $yshift = 0;
9552                                                 }
9553                                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9554                                                         // the last line must be shifted to be aligned as requested
9555                                                         $linew = abs($this->endlinex - $startlinex);
9556                                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9557                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9558                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9559                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9560                                                         } elseif (isset($opentagpos)) {
9561                                                                 $midpos = $opentagpos;
9562                                                         } elseif (isset($this->footerlen[$startlinepage])) {
9563                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9564                                                                 $midpos = $this->footerpos[$startlinepage];
9565                                                         } else {
9566                                                                 $midpos = 0;
9567                                                         }
9568                                                         if ($midpos > 0) {
9569                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9570                                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9571                                                         } else {
9572                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9573                                                                 $pend = "";
9574                                                         }
9575                                                         // calculate shifting amount
9576                                                         $mdiff = abs($w - $linew);
9577                                                         if ($plalign == "C") {
9578                                                                 if ($this->rtl) {
9579                                                                         $t_x = -($mdiff / 2);
9580                                                                 } else {
9581                                                                         $t_x = ($mdiff / 2);
9582                                                                 }
9583                                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9584                                                                 // right alignment on LTR document
9585                                                                 $t_x = $mdiff;
9586                                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9587                                                                 // left alignment on RTL document
9588                                                                 $t_x = -$mdiff;
9589                                                         } else {
9590                                                                 $t_x = 0;
9591                                                         }
9592                                                         if (($t_x != 0) OR ($yshift < 0)) {
9593                                                                 // shift the line
9594                                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9595                                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9596                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9597                                                                 // shift the annotations and links
9598                                                                 if (isset($this->PageAnnots[$this->page])) {
9599                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9600                                                                                 if ($pac['y'] >= $minstartliney) {
9601                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9602                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9603                                                                                 }
9604                                                                         }
9605                                                                 }
9606                                                                 $this->y -= $yshift;
9607                                                         }
9608                                                 }
9609                                         }
9610                                         $this->checkPageBreak($this->lasth);
9611                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9612                                         if ($wfill) {
9613                                                 $this->SetFillColorArray($this->bgcolor);
9614                                         }
9615                                         $startlinex = $this->x;
9616                                         $startliney = $this->y;
9617                                         $minstartliney = $this->y;
9618                                         $startlinepage = $this->page;
9619                                         if (isset($endlinepos)) {
9620                                                 $startlinepos = $endlinepos;
9621                                                 unset($endlinepos);
9622                                         } else {
9623                                                 if (isset($this->footerlen[$this->page])) {
9624                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9625                                                 } else {
9626                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9627                                                 }
9628                                                 $startlinepos = $this->footerpos[$this->page];
9629                                         }
9630                                         $plalign = $lalign;
9631                                         $this->newline = false;
9632                                 }
9633                                 if (isset($opentagpos)) {
9634                                         unset($opentagpos);
9635                                 }
9636                                 if ($dom[$key]['tag']) {
9637                                         if ($dom[$key]['opening']) {
9638                                                 // table content is handled in a special way
9639                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9640                                                         $trid = $dom[$key]['parent'];
9641                                                         $table_el = $dom[$trid]['parent'];
9642                                                         if (!isset($dom[$table_el]['cols'])) {
9643                                                                 $dom[$table_el]['cols'] = $trid['cols'];
9644                                                         }
9645                                                         // calculate cell width
9646                                                         if (isset($dom[($dom[$key]['parent'])]['width'])) {
9647                                                                 $table_width = $this->pixelsToUnits($dom[($dom[$key]['parent'])]['width']);
9648                                                         } else {
9649                                                                 $table_width = $w;
9650                                                         }
9651                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
9652                                                                 $currentcmargin = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding']);
9653                                                         } else {
9654                                                                 $currentcmargin = 0;
9655                                                         }
9656                                                         $this->cMargin = $currentcmargin;
9657                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
9658                                                                 $cellspacing = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing']);
9659                                                         } else {
9660                                                                 $cellspacing = 0;
9661                                                         }
9662                                                         if ($this->rtl) {
9663                                                                 $cellspacingx = -$cellspacing;
9664                                                         } else {
9665                                                                 $cellspacingx = $cellspacing;
9666                                                         }
9667                                                         $colspan = $dom[$key]['attribute']['colspan'];
9668                                                         if (isset($dom[$key]['width'])) {
9669                                                                 $cellw = $this->pixelsToUnits($dom[$key]['width']);
9670                                                         } else {
9671                                                                 $cellw = ($colspan * ($table_width / $dom[$table_el]['cols']));
9672                                                         }
9673                                                         $cellw -= $cellspacing;
9674                                                         if (isset($dom[$key]['content'])) {
9675                                                                 $cell_content = $dom[$key]['content'];
9676                                                         } else {
9677                                                                 $cell_content = "&nbsp;";
9678                                                         }
9679                                                         $tagtype = $dom[$key]['value'];
9680                                                         $parentid = $key;
9681                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
9682                                                                 // move $key index forward
9683                                                                 $key++;
9684                                                         }
9685                                                         if (!isset($dom[$trid]['startpage'])) {
9686                                                                 $dom[$trid]['startpage'] = $this->page;
9687                                                         } else {
9688                                                                 $this->setPage($dom[$trid]['startpage']);
9689                                                         }
9690                                                         if (!isset($dom[$trid]['starty'])) {
9691                                                                 $dom[$trid]['starty'] = $this->y;
9692                                                         } else {
9693                                                                 $this->y = $dom[$trid]['starty'];
9694                                                         }
9695                                                         if (!isset($dom[$trid]['startx'])) {
9696                                                                 $dom[$trid]['startx'] = $this->x;
9697                                                         }
9698                                                         $this->x += ($cellspacingx / 2);
9699                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
9700                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
9701                                                         }       else {
9702                                                                 $rowspan = 1;
9703                                                         }
9704                                                         // skip row-spanned cells started on the previous rows
9705                                                         if (isset($dom[$table_el]['rowspans'])) {
9706                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9707                                                                         if  (($trwsp['startx'] == $this->x) AND (($trwsp['starty'] < $this->y) OR ($trwsp['startpage'] < $this->page)) AND ($trwsp['rowspan'] > 0)) {
9708                                                                                 $this->x = $trwsp['endx'] + $cellspacingx;
9709                                                                         }
9710                                                                 }
9711                                                         }
9712                                                         // add rowspan information to table element
9713                                                         if ($rowspan > 1) {
9714                                                                 if (isset($this->footerlen[$this->page])) {
9715                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9716                                                                 } else {
9717                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9718                                                                 }
9719                                                                 $trintmrkpos = $this->footerpos[$this->page];
9720                                                                 $trsid = array_push($dom[$table_el]['rowspans'], array('rowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
9721                                                         }
9722                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
9723                                                         if ($rowspan > 1) {
9724                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
9725                                                         }
9726                                                         // push background colors
9727                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
9728                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
9729                                                         }
9730
9731                                                         // write the cell content
9732                                                         $this->MultiCell($cellw, 0, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
9733
9734                                                         $this->cMargin = $currentcmargin;
9735                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
9736                                                         
9737                                                         // update the end of row position
9738                                                         if ($rowspan <= 1) {
9739                                                                 if (isset($dom[$trid]['endy'])) {
9740                                                                         if ($this->page == $dom[$trid]['endpage']) {
9741                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
9742                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
9743                                                                                 $dom[$trid]['endy'] = $this->y;
9744                                                                         }
9745                                                                 } else {
9746                                                                         $dom[$trid]['endy'] = $this->y;
9747                                                                 }
9748                                                                 if (isset($dom[$trid]['endpage'])) {
9749                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
9750                                                                 } else {
9751                                                                         $dom[$trid]['endpage'] = $this->page;
9752                                                                 }
9753                                                         } else {
9754                                                         // account for row-spanned cells
9755                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
9756                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
9757                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
9758                                                         }
9759                                                         if (isset($dom[$table_el]['rowspans'])) {
9760                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9761                                                                         if ($trwsp['rowspan'] > 0) {
9762                                                                                 if (isset($dom[$trid]['endpage'])) {
9763                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
9764                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
9765                                                                                         } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[$trid]['endpage']) {
9766                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $trwsp['endy'];
9767                                                                                         } else {
9768                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
9769                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
9770                                                                                         }
9771                                                                                 }
9772                                                                         }
9773                                                                 }
9774                                                         }
9775                                                         $this->x += ($cellspacingx / 2);
9776                                                 } else {
9777                                                         // opening tag (or self-closing tag)
9778                                                         if (!isset($opentagpos)) {
9779                                                                 if (isset($this->footerlen[$this->page])) {
9780                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9781                                                                 } else {
9782                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9783                                                                 }
9784                                                                 $opentagpos = $this->footerpos[$this->page];
9785                                                         }
9786                                                         $this->openHTMLTagHandler($dom, $key, $cell);
9787                                                 }
9788                                         } else {
9789                                                 // closing tag
9790                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
9791                                         }
9792                                 } elseif (strlen($dom[$key]['value']) > 0) {
9793                                         // print list-item
9794                                         if (!empty($this->lispacer)) {
9795                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
9796                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9797                                                 $minstartliney = $this->y;
9798                                                 $tmpx = $this->x;
9799                                                 $lspace = $this->GetStringWidth($this->lispacer."  ");
9800                                                 if ($this->rtl) {
9801                                                         $this->x += $lspace;
9802                                                 } else {
9803                                                         $this->x -= $lspace;
9804                                                 }
9805                                                 $this->Write($this->lasth, $this->lispacer, '', false, '', false, 0, false);
9806                                                 $this->x = $tmpx;
9807                                                 $this->lispacer = "";
9808                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
9809                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9810                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
9811                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
9812                                                         $minstartliney = min($this->y, $minstartliney);
9813                                                 }
9814                                         }
9815                                         // text
9816                                         $this->htmlvspace = 0;
9817                                         if ($newline) {
9818                                                 if ($this->rtl OR $this->tmprtl) {
9819                                                         $dom[$key]['value'] = rtrim($dom[$key]['value']);
9820                                                 } else {
9821                                                         $dom[$key]['value'] = ltrim($dom[$key]['value']);
9822                                                 }
9823                                                 $newline = false;
9824                                         }
9825                                         if ($this->HREF) {
9826                                                 // HTML <a> Link
9827                                                 $strrest = $this->addHtmlLink($this->HREF, $dom[$key]['value'], $wfill, true);
9828                                         } else {
9829                                                 $ctmpmargin = $this->cMargin;
9830                                                 $this->cMargin = 0;
9831                                                 // write only until the end of the line and get the rest
9832                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, "", false, 0, true);
9833                                                 $this->cMargin = $ctmpmargin;
9834                                         }
9835                                         if (strlen($strrest) > 0) {
9836                                                 // store the remaining string on the previous $key position
9837                                                 $this->newline = true;
9838                                                 if ($cell) {
9839                                                         if ($this->rtl) {
9840                                                                 $this->x -= $this->cMargin;
9841                                                         } else {
9842                                                                 $this->x += $this->cMargin;
9843                                                         }
9844                                                 }
9845                                                 $dom[$key]['value'] = ltrim($strrest);
9846                                                 $key--;
9847                                         }
9848                                 }
9849                                 $key++;
9850                         } // end for each $key
9851                         // align the last line
9852                         if (isset($startlinex)) {
9853                                 $yshift = $minstartliney - $startliney;
9854                                 if ($yshift > 0) {
9855                                         $yshift = 0;
9856                                 }
9857                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9858                                         // the last line must be shifted to be aligned as requested
9859                                         $linew = abs($this->endlinex - $startlinex);
9860                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9861                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9862                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9863                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9864                                         } elseif (isset($opentagpos)) {
9865                                                 $midpos = $opentagpos;
9866                                         } elseif (isset($this->footerlen[$startlinepage])) {
9867                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9868                                                 $midpos = $this->footerpos[$startlinepage];
9869                                         } else {
9870                                                 $midpos = 0;
9871                                         }
9872                                         if ($midpos > 0) {
9873                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9874                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9875                                         } else {
9876                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9877                                                 $pend = "";
9878                                         }
9879                                         // calculate shifting amount
9880                                         $mdiff = abs($w - $linew);
9881                                         if ($plalign == "C") {
9882                                                 if ($this->rtl) {
9883                                                         $t_x = -($mdiff / 2);
9884                                                 } else {
9885                                                         $t_x = ($mdiff / 2);
9886                                                 }
9887                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9888                                                 // right alignment on LTR document
9889                                                 $t_x = $mdiff;
9890                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9891                                                 // left alignment on RTL document
9892                                                 $t_x = -$mdiff;
9893                                         } else {
9894                                                 $t_x = 0;
9895                                         }
9896                                         if (($t_x != 0) OR ($yshift < 0)) {
9897                                                 // shift the line
9898                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9899                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9900                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9901                                                 // shift the annotations and links
9902                                                 if (isset($this->PageAnnots[$this->page])) {
9903                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9904                                                                 if ($pac['y'] >= $minstartliney) {
9905                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9906                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9907                                                                 }
9908                                                         }
9909                                                 }
9910                                                 $this->y -= $yshift;
9911                                         }
9912                                 }
9913                         }
9914                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == "table")))) {
9915                                 $this->Ln($this->lasth);
9916                         }
9917                         // restore previous values
9918                         $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
9919                         $this->SetFillColorArray($prevbgcolor);
9920                         $this->SetTextColorArray($prevfgcolor);
9921                         $this->lMargin = $prevlMargin;
9922                         $this->rMargin = $prevrMargin;
9923                         $this->cMargin = $prevcMargin;
9924                         unset($dom);
9925                 }
9926
9927                 /**
9928                  * Process opening tags.
9929                  * @param array $dom html dom array
9930                  * @param int $key current element id
9931                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
9932                  * @access protected
9933                  */
9934                 function openHTMLTagHandler(&$dom, $key, $cell=false) {
9935                         $tag = $dom[$key];
9936                         $parent = $dom[($dom[$key]['parent'])];
9937                         // check for text direction attribute
9938                         if (isset($tag['attribute']['dir'])) {
9939                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
9940                         } else {
9941                                 $this->tmprtl = false;
9942                         }
9943                         //Opening tag
9944                         switch($tag['value']) {
9945                                 case 'table': {
9946                                         $dom[$key]['rowspans'] = array();
9947                                         if (isset($tag['attribute']['cellpadding'])) {
9948                                                 $this->oldcMargin = $this->cMargin;
9949                                                 $this->cMargin = $this->pixelsToUnits($tag['attribute']['cellpadding']);
9950                                         }
9951                                         break;
9952                                 }
9953                                 case 'tr': {
9954                                         // array of columns positions
9955                                         $dom[$key]['cellpos'] = array();
9956                                         break;
9957                                 }
9958                                 case 'td':
9959                                 case 'th': {
9960                                         break;
9961                                 }
9962                                 case 'hr': {
9963                                         $this->addHTMLVertSpace(1, $cell);
9964                                         $this->htmlvspace = 0;
9965                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
9966                                                 $hrWidth = $this->pixelsToUnits($tag['attribute']['width']);
9967                                         } else {
9968                                                 $hrWidth = $this->w - $this->lMargin - $this->rMargin;
9969                                         }
9970                                         $x = $this->GetX();
9971                                         $y = $this->GetY();
9972                                         $prevlinewidth = $this->GetLineWidth();
9973                                         $this->Line($x, $y, $x + $hrWidth, $y);
9974                                         $this->SetLineWidth($prevlinewidth);
9975                                         $this->addHTMLVertSpace(1, $cell);
9976                                         break;
9977                                 }
9978                                 case 'b': {
9979                                         $this->setStyle('b', true);
9980                                         break;
9981                                 }
9982                                 case 'i': {
9983                                         $this->setStyle('i', true);
9984                                         break;
9985                                 }
9986                                 case 'u': {
9987                                         $this->setStyle('u', true);
9988                                         break;
9989                                 }
9990                                 case 'del': {
9991                                         $this->setStyle('d', true);
9992                                         break;
9993                                 }
9994                                 case 'a': {
9995                                         if (array_key_exists('href', $tag['attribute'])) {
9996                                                 $this->HREF = $tag['attribute']['href'];
9997                                         }
9998                                         break;
9999                                 }
10000                                 case 'img': {
10001                                         if (isset($tag['attribute']['src'])) {
10002                                                 // replace relative path with real server path
10003                                                 if ($tag['attribute']['src'][0] == '/') {
10004                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
10005                                                 }
10006                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
10007                                                 if (!isset($tag['attribute']['width'])) {
10008                                                         $tag['attribute']['width'] = 0;
10009                                                 }
10010                                                 if (!isset($tag['attribute']['height'])) {
10011                                                         $tag['attribute']['height'] = 0;
10012                                                 }
10013                                                 //if (!isset($tag['attribute']['align'])) {
10014                                                         // the only alignment supported is "bottom"
10015                                                         // further development is required for other modes.
10016                                                         $tag['attribute']['align'] = 'bottom';
10017                                                 //} 
10018                                                 switch($tag['attribute']['align']) {
10019                                                         case 'top': {
10020                                                                 $align = 'T';
10021                                                                 break;
10022                                                         }
10023                                                         case 'middle': {
10024                                                                 $align = 'M';
10025                                                                 break;
10026                                                         }
10027                                                         case 'bottom': {
10028                                                                 $align = 'B';
10029                                                                 break;
10030                                                         }
10031                                                         default: {
10032                                                                 $align = 'B';
10033                                                                 break;
10034                                                         }
10035                                                 }
10036                                                 $fileinfo = pathinfo($tag['attribute']['src']);
10037                                                 if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
10038                                                         $type = strtolower($fileinfo['extension']);
10039                                                 }
10040                                                 $prevy = $this->y;
10041                                                 if (($type == "eps") OR ($type == "ai")) {
10042                                                         $this->ImageEps($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', true, $align);
10043                                                 } else {
10044                                                         $this->Image($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', '', $align);
10045                                                 }
10046                                                 switch($align) {
10047                                                         case 'T': {
10048                                                                 $this->y = $prevy;
10049                                                                 break;
10050                                                         }
10051                                                         case 'M': {
10052                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
10053                                                                 break;
10054                                                         }
10055                                                         case 'B': {
10056                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
10057                                                                 break;
10058                                                         }
10059                                                 }
10060                                         }
10061                                         break;
10062                                 }
10063                                 case 'dl': {
10064                                         $this->listnum++;
10065                                         break;
10066                                 }
10067                                 case 'dt': {
10068                                         $this->addHTMLVertSpace(1, $cell);
10069                                         break;
10070                                 }
10071                                 case 'dd': {
10072                                         if ($this->rtl) {
10073                                                 $this->rMargin += $this->listindent;
10074                                         } else {
10075                                                 $this->lMargin += $this->listindent;
10076                                         }
10077                                         $this->addHTMLVertSpace(1, $cell);
10078                                         break;
10079                                 }
10080                                 case 'ul':
10081                                 case 'ol': {
10082                                         $this->listnum++;
10083                                         if ($tag['value'] == "ol") {
10084                                                 $this->listordered[$this->listnum] = true;
10085                                         } else {
10086                                                 $this->listordered[$this->listnum] = false;
10087                                         }
10088                                         $this->listcount[$this->listnum] = 0;
10089                                         if ($this->rtl) {
10090                                                 $this->rMargin += $this->listindent;
10091                                         } else {
10092                                                 $this->lMargin += $this->listindent;
10093                                         }
10094                                         break;
10095                                 }
10096                                 case 'li': {
10097                                         $this->Ln('', $cell);
10098                                         if ($tag['value'] == 'li') {
10099                                                 if ($this->listordered[$this->listnum]) {
10100                                                         if (isset($tag['attribute']['value'])) {
10101                                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
10102                                                         }
10103                                                         $this->listcount[$this->listnum]++;
10104                                                         if ($this->rtl) {
10105                                                                 $this->lispacer = ".".($this->listcount[$this->listnum]);
10106                                                         } else {
10107                                                                 $this->lispacer = ($this->listcount[$this->listnum]).".";
10108                                                         }
10109                                                 } else {
10110                                                         //unordered list symbol
10111                                                         $this->lispacer = "-";
10112                                                 }
10113                                         } else {
10114                                                 $this->lispacer = "";
10115                                         }
10116                                         break;
10117                                 }
10118                                 case 'blockquote': {
10119                                         if ($this->rtl) {
10120                                                 $this->rMargin += $this->listindent;
10121                                         } else {
10122                                                 $this->lMargin += $this->listindent;
10123                                         }
10124                                         $this->addHTMLVertSpace(2, $cell);
10125                                         break;
10126                                 }
10127                                 case 'br': {
10128                                         $this->Ln('', $cell);
10129                                         break;
10130                                 }
10131                                 case 'div': {
10132                                         $this->addHTMLVertSpace(2, $cell);
10133                                         break;
10134                                 }
10135                                 case 'p': {
10136                                         $this->addHTMLVertSpace(2, $cell);
10137                                         break;
10138                                 }
10139                                 case 'sup': {
10140                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
10141                                         break;
10142                                 }
10143                                 case 'sub': {
10144                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
10145                                         break;
10146                                 }
10147                                 case 'h1':
10148                                 case 'h2':
10149                                 case 'h3':
10150                                 case 'h4':
10151                                 case 'h5':
10152                                 case 'h6': {
10153                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k);
10154                                         break;
10155                                 }
10156                                 default: {
10157                                         break;
10158                                 }
10159                         }
10160                 }
10161
10162                 /**
10163                  * Process closing tags.
10164                  * @param array $dom html dom array
10165                  * @param int $key current element id
10166                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10167                  * @access protected
10168                  */
10169                 function closeHTMLTagHandler(&$dom, $key, $cell=false) {
10170                         $tag = $dom[$key];
10171                         $parent = $dom[($dom[$key]['parent'])];
10172                         //Closing tag
10173                         switch($tag['value']) {
10174                                 case 'td':
10175                                 case 'th': {
10176                                         break;
10177                                 }
10178                                 case 'tr': {
10179                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
10180                                         // update row-spanned cells
10181                                         if (isset($dom[$table_el]['rowspans'])) {
10182                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
10183                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
10184                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
10185                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $dom[($dom[$key]['parent'])]['endpage']) {
10186                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
10187                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[($dom[$key]['parent'])]['endpage']) {
10188                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
10189                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
10190                                                                 }
10191                                                         }
10192                                                 }
10193                                         }
10194                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
10195                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];
10196                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
10197                                                 $cellspacing = $this->pixelsToUnits($dom[$table_el]['attribute']['cellspacing']);
10198                                                 $this->y += $cellspacing;
10199                                         }                               
10200                                         $this->Ln(0, $cell);
10201                                         $this->x = $dom[($dom[$key]['parent'])]['startx'];
10202                                         break;
10203                                 }
10204                                 case 'table': {
10205                                         // draw borders
10206                                         $table_el = $parent;
10207                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
10208                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
10209                                                         $border = 1;
10210                                         } else {
10211                                                 $border = 0;
10212                                         }
10213                                         // for each row
10214                                         foreach ($table_el['trids'] as $j => $trkey) {
10215                                                 $parent = $dom[$trkey];
10216                                                 $restspace = $this->getPageHeight() - $this->y - $this->getBreakMargin();
10217                                                 // for each cell on the row
10218                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
10219                                                         if (isset($cellpos['rowspanid'])) {
10220                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
10221                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
10222                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
10223                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
10224                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
10225                                                         } else {
10226                                                                 $endy = $parent['endy'];
10227                                                                 $startpage = $parent['startpage'];
10228                                                                 $endpage = $parent['endpage'];
10229                                                         }
10230                                                         $this->setPage($startpage);
10231                                                         $this->y = $parent['starty'];
10232                                                         if ($endpage > $startpage) {
10233                                                                 // design borders around HTML cells.
10234                                                                 for ($page=$startpage; $page <= $endpage; $page++) {
10235                                                                         $this->setPage($page);
10236                                                                         if ($page == $startpage) {
10237                                                                                 $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin();
10238                                                                                 $ch = $restspace;
10239                                                                         } elseif ($page == $endpage) {
10240                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10241                                                                                 $ch = $endy - $this->tMargin;
10242                                                                         } else {
10243                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10244                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
10245                                                                         }
10246
10247                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10248                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
10249                                                                                 $fill = true;
10250                                                                         } else {
10251                                                                                 $fill = false;
10252                                                                         }
10253                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
10254                                                                         $this->x = $cellpos['startx'];
10255                                                                         // design a cell around the text
10256                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10257                                                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10258                                                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10259                                                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10260                                                                         $this->intmrk[$this->page] += strlen($ccode."\n");
10261                                                                 }
10262                                                         } else {
10263                                                                 $ch = $endy - $parent['starty'];
10264                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10265                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
10266                                                                         $fill = true;
10267                                                                 } else {
10268                                                                         $fill = false;
10269                                                                 }
10270                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
10271                                                                 $this->x = $cellpos['startx'];
10272                                                                 $this->y = $parent['starty'];
10273                                                                 // design a cell around the text
10274                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10275                                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10276                                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10277                                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10278                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
10279                                                         }
10280                                                 }
10281                                                 if (isset($table_el['attribute']['cellspacing'])) {
10282                                                         $cellspacing = $this->pixelsToUnits($table_el['attribute']['cellspacing']);
10283                                                         $this->y += $cellspacing;
10284                                                 }
10285                                                 $this->Ln(0, $cell);
10286                                                 $this->x = $parent['startx'];
10287                                         }
10288                                         if (isset($parent['cellpadding'])) {
10289                                                 $this->cMargin = $this->oldcMargin;
10290                                         }
10291                                         //set row height
10292                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10293                                         break;
10294                                 }
10295                                 case 'b': {
10296                                         $this->setStyle('b', false);
10297                                         break;
10298                                 }
10299                                 case 'i': {
10300                                         $this->setStyle('i', false);
10301                                         break;
10302                                 }
10303                                 case 'u': {
10304                                         $this->setStyle('u', false);
10305                                         break;
10306                                 }
10307                                 case 'del': {
10308                                         $this->setStyle('d', false);
10309                                         break;
10310                                 }
10311                                 case 'a': {
10312                                         $this->HREF = '';
10313                                         break;
10314                                 }
10315                                 case 'sup': {
10316                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
10317                                         break;
10318                                 }
10319                                 case 'sub': {
10320                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
10321                                         break;
10322                                 }
10323                                 case 'div': {
10324                                         $this->addHTMLVertSpace(1, $cell);
10325                                         break;
10326                                 }
10327                                 case 'blockquote': {
10328                                         if ($this->rtl) {
10329                                                 $this->rMargin -= $this->listindent;
10330                                         } else {
10331                                                 $this->lMargin -= $this->listindent;
10332                                         }
10333                                         $this->addHTMLVertSpace(2, $cell);
10334                                         break;
10335                                 }
10336                                 case 'p': {
10337                                         $this->addHTMLVertSpace(2, $cell);
10338                                         break;
10339                                 }
10340                                 case 'dl': {
10341                                         $this->listnum--;
10342                                         if ($this->listnum <= 0) {
10343                                                 $this->listnum = 0;
10344                                                 $this->addHTMLVertSpace(2, $cell);
10345                                         }
10346                                         break;
10347                                 }
10348                                 case 'dt': {
10349                                         $this->lispacer = "";
10350                                         break;
10351                                 }
10352                                 case 'dd': {
10353                                         $this->lispacer = "";
10354                                         if ($this->rtl) {
10355                                                 $this->rMargin -= $this->listindent;
10356                                         } else {
10357                                                 $this->lMargin -= $this->listindent;
10358                                         }
10359                                         break;
10360                                 }
10361                                 case 'ul':
10362                                 case 'ol': {
10363                                         $this->listnum--;
10364                                         $this->lispacer = "";
10365                                         if ($this->rtl) {
10366                                                 $this->rMargin -= $this->listindent;
10367                                         } else {
10368                                                 $this->lMargin -= $this->listindent;
10369                                         }
10370                                         if ($this->listnum <= 0) {
10371                                                 $this->listnum = 0;
10372                                                 $this->addHTMLVertSpace(2, $cell);
10373                                         }
10374                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10375                                         break;
10376                                 }
10377                                 case 'li': {
10378                                         $this->lispacer = "";
10379                                         break;
10380                                 }
10381                                 case 'h1':
10382                                 case 'h2':
10383                                 case 'h3':
10384                                 case 'h4':
10385                                 case 'h5':
10386                                 case 'h6': {
10387                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k);
10388                                         break;
10389                                 }
10390                                 default : {
10391                                         break;
10392                                 }
10393                         }
10394                         $this->tmprtl = false;
10395                 }
10396                 
10397                 /**
10398                  * Add vertical spaces if needed.
10399                  * @param int $n number of spaces to add
10400                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10401                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
10402                  * @access protected
10403                  */
10404                 function addHTMLVertSpace($n, $cell=false, $h='') {
10405                         if (is_string($h)) {
10406                                 $vsize = $n * $this->lasth;
10407                         } else {
10408                                 $vsize = $n * $h;
10409                         }
10410                         if ($vsize > $this->htmlvspace) {
10411                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
10412                                 $this->htmlvspace = $vsize;
10413             }
10414         }
10415                 
10416         } // END OF TCPDF CLASS
10417 }
10418 //============================================================+
10419 // END OF FILE
10420 //============================================================+
10421 ?>