Extended the Report Engine to better support own reports
[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   * 5. Header function renamed to Header1 (due to conflict with FrontReport Header)
145   * 6. Line 6190, SetLineWidth (cast of values to avoid problem in PHP 5.2.6
146   * -------------------------------------------------------------------------------
147   */
148 if (!defined("K_PATH_FONTS"))
149         define ("K_PATH_FONTS", '../reporting/fonts/');
150 define ("K_PATH_CACHE", '../reporting/fonts/');
151 define("K_CELL_HEIGHT_RATIO", 1.25);
152
153 //require_once(dirname(__FILE__).'/config/tcpdf_config.php');
154
155 // includes some support files
156
157 /**
158  * unicode data
159  */
160 // only included if unicode
161 //include_once(dirname(__FILE__)."/unicode_data2.php");
162
163 /**
164  * html colors table
165  */
166 require_once(dirname(__FILE__).'/htmlcolors.php');
167
168 /**
169  * barcode class
170  */
171 require_once(dirname(__FILE__)."/barcodes.php");
172
173 /**
174  * HTML entity decode functions
175  */
176 require_once(dirname(__FILE__)."/html_entity_decode_php4.php");
177
178 if (!class_exists('TCPDF')) {
179         /**
180          * define default PDF document producer
181          */
182         define('PDF_PRODUCER','TCPDF 4.0.027_PHP4 (http://www.tcpdf.org)');
183
184         /**
185         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
186         * 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>
187         * @name TCPDF
188         * @package com.tecnick.tcpdf
189         * @version 4.0.027_PHP4
190         * @author Nicola Asuni - info@tecnick.com
191         * @link http://www.tcpdf.org
192         * @license http://www.gnu.org/copyleft/lesser.html LGPL
193         */
194         class TCPDF {
195                 
196                 // protected or Protected properties
197
198                 /**
199                 * @var current page number
200                 * @access protected
201                 */
202                 var $page;
203
204                 /**
205                 * @var current object number
206                 * @access protected
207                 */
208                 var $n;
209
210                 /**
211                 * @var array of object offsets
212                 * @access protected
213                 */
214                 var $offsets;
215
216                 /**
217                 * @var buffer holding in-memory PDF
218                 * @access protected
219                 */
220                 var $buffer;
221
222                 /**
223                 * @var array containing pages
224                 * @access protected
225                 */
226                 var $pages = array();
227
228                 /**
229                 * @var current document state
230                 * @access protected
231                 */
232                 var $state;
233
234                 /**
235                 * @var compression flag
236                 * @access protected
237                 */
238                 var $compress;
239
240                 /**
241                 * @var current page orientation (P = Portrait, L = Landscape)
242                 * @access protected
243                 */
244                 var $CurOrientation;
245
246                 /**
247                 * @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>
248                 * @access protected
249                 */
250                 var $pagedim = array();
251
252                 /**
253                 * @var scale factor (number of points in user unit)
254                 * @access protected
255                 */
256                 var $k;
257
258                 /**
259                 * @var width of page format in points
260                 * @access protected
261                 */
262                 var $fwPt;
263
264                 /**
265                 * @var height of page format in points
266                 * @access protected
267                 */
268                 var $fhPt;
269
270                 /**
271                 * @var current width of page in points
272                 * @access protected
273                 */
274                 var $wPt;
275
276                 /**
277                 * @var current height of page in points
278                 * @access protected
279                 */
280                 var $hPt;
281
282                 /**
283                 * @var current width of page in user unit
284                 * @access protected
285                 */
286                 var $w;
287
288                 /**
289                 * @var current height of page in user unit
290                 * @access protected
291                 */
292                 var $h;
293
294                 /**
295                 * @var left margin
296                 * @access protected
297                 */
298                 var $lMargin;
299
300                 /**
301                 * @var top margin
302                 * @access protected
303                 */
304                 var $tMargin;
305
306                 /**
307                 * @var right margin
308                 * @access protected
309                 */
310                 var $rMargin;
311
312                 /**
313                 * @var page break margin
314                 * @access protected
315                 */
316                 var $bMargin;
317
318                 /**
319                 * @var cell internal padding
320                 * @access protected
321                 */
322                 var $cMargin;
323
324                 /**
325                 * @var cell internal padding (previous value)
326                 * @access protected
327                 */
328                 var $oldcMargin;
329
330                 /**
331                 * @var current horizontal position in user unit for cell positioning
332                 * @access protected
333                 */
334                 var $x;
335
336                 /**
337                 * @var current vertical position in user unit for cell positioning
338                 * @access protected
339                 */
340                 var $y;
341
342                 /**
343                 * @var height of last cell printed
344                 * @access protected
345                 */
346                 var $lasth;
347
348                 /**
349                 * @var line width in user unit
350                 * @access protected
351                 */
352                 var $LineWidth;
353
354                 /**
355                 * @var array of standard font names
356                 * @access protected
357                 */
358                 var $CoreFonts;
359
360                 /**
361                 * @var array of used fonts
362                 * @access protected
363                 */
364                 var $fonts = array();
365
366                 /**
367                 * @var array of font files
368                 * @access protected
369                 */
370                 var $FontFiles = array();
371
372                 /**
373                 * @var array of encoding differences
374                 * @access protected
375                 */
376                 var $diffs = array();
377
378                 /**
379                 * @var array of used images
380                 * @access protected
381                 */
382                 var $images = array();
383
384                 /**
385                 * @var array of Annotations in pages
386                 * @access protected
387                 */
388                 var $PageAnnots = array();
389
390                 /**
391                 * @var array of internal links
392                 * @access protected
393                 */
394                 var $links = array();
395
396                 /**
397                 * @var current font family
398                 * @access protected
399                 */
400                 var $FontFamily;
401
402                 /**
403                 * @var current font style
404                 * @access protected
405                 */
406                 var $FontStyle;
407
408                 /**
409                 * @var current font ascent (distance between font top and baseline)
410                 * @access protected
411                 * @since 2.8.000 (2007-03-29)
412                 */
413                 var $FontAscent;
414
415                 /**
416                 * @var current font descent (distance between font bottom and baseline)
417                 * @access protected
418                 * @since 2.8.000 (2007-03-29)
419                 */
420                 var $FontDescent;
421
422                 /**
423                 * @var underlining flag
424                 * @access protected
425                 */
426                 var $underline;
427
428                 /**
429                 * @var current font info
430                 * @access protected
431                 */
432                 var $CurrentFont;
433
434                 /**
435                 * @var current font size in points
436                 * @access protected
437                 */
438                 var $FontSizePt;
439
440                 /**
441                 * @var current font size in user unit
442                 * @access protected
443                 */
444                 var $FontSize;
445
446                 /**
447                 * @var commands for drawing color
448                 * @access protected
449                 */
450                 var $DrawColor;
451
452                 /**
453                 * @var commands for filling color
454                 * @access protected
455                 */
456                 var $FillColor;
457
458                 /**
459                 * @var commands for text color
460                 * @access protected
461                 */
462                 var $TextColor;
463
464                 /**
465                 * @var indicates whether fill and text colors are different
466                 * @access protected
467                 */
468                 var $ColorFlag;
469
470                 /**
471                 * @var word spacing
472                 * @access protected
473                 */
474                 var $ws;
475
476                 /**
477                 * @var automatic page breaking
478                 * @access protected
479                 */
480                 var $AutoPageBreak;
481
482                 /**
483                 * @var threshold used to trigger page breaks
484                 * @access protected
485                 */
486                 var $PageBreakTrigger;
487
488                 /**
489                 * @var flag set when processing footer
490                 * @access protected
491                 */
492                 var $InFooter;
493
494                 /**
495                 * @var zoom display mode
496                 * @access protected
497                 */
498                 var $ZoomMode;
499
500                 /**
501                 * @var layout display mode
502                 * @access protected
503                 */
504                 var $LayoutMode;
505
506                 /**
507                 * @var title
508                 * @access protected
509                 */
510                 var $title;
511
512                 /**
513                 * @var subject
514                 * @access protected
515                 */
516                 var $subject;
517
518                 /**
519                 * @var author
520                 * @access protected
521                 */
522                 var $author;
523
524                 /**
525                 * @var keywords
526                 * @access protected
527                 */
528                 var $keywords;
529
530                 /**
531                 * @var creator
532                 * @access protected
533                 */
534                 var $creator;
535
536                 /**
537                 * @var alias for total number of pages
538                 * @access protected
539                 */
540                 var $AliasNbPages;
541
542                 /**
543                 * @var right-bottom corner X coordinate of inserted image
544                 * @since 2002-07-31
545                 * @author Nicola Asuni
546                 * @access protected
547                 */
548                 var $img_rb_x;
549
550                 /**
551                 * @var right-bottom corner Y coordinate of inserted image
552                 * @since 2002-07-31
553                 * @author Nicola Asuni
554                 * @access protected
555                 */
556                 var $img_rb_y;
557
558                 /**
559                 * @var image scale factor
560                 * @since 2004-06-14
561                 * @author Nicola Asuni
562                 * @access protected
563                 */
564                 var $imgscale = 1;
565
566                 /**
567                 * @var boolean set to true when the input text is unicode (require unicode fonts)
568                 * @since 2005-01-02
569                 * @author Nicola Asuni
570                 * @access protected
571                 */
572                 var $isunicode = false;
573
574                 /**
575                 * @var PDF version
576                 * @since 1.5.3
577                 * @access protected
578                 */
579                 var $PDFVersion = "1.7";
580
581
582                 // ----------------------
583
584                 /**
585                  * @var Minimum distance between header and top page margin.
586                  * @access protected
587                  */
588                 var $header_margin;
589
590                 /**
591                  * @var Minimum distance between footer and bottom page margin.
592                  * @access protected
593                  */
594                 var $footer_margin;
595
596                 /**
597                  * @var original left margin value
598                  * @access protected
599                  * @since 1.53.0.TC013
600                  */
601                 var $original_lMargin;
602
603                 /**
604                  * @var original right margin value
605                  * @access protected
606                  * @since 1.53.0.TC013
607                  */
608                 var $original_rMargin;
609
610                 /**
611                  * @var Header font.
612                  * @access protected
613                  */
614                 var $header_font;
615
616                 /**
617                  * @var Footer font.
618                  * @access protected
619                  */
620                 var $footer_font;
621
622                 /**
623                  * @var Language templates.
624                  * @access protected
625                  */
626                 var $l;
627
628                 /**
629                  * @var Barcode to print on page footer (only if set).
630                  * @access protected
631                  */
632                 var $barcode = false;
633
634                 /**
635                  * @var If true prints header
636                  * @access protected
637                  */
638                 var $print_header = true;
639
640                 /**
641                  * @var If true prints footer.
642                  * @access protected
643                  */
644                 var $print_footer = true;
645
646                 /**
647                  * @var Header image logo.
648                  * @access protected
649                  */
650                 var $header_logo = "";
651
652                 /**
653                  * @var Header image logo width in mm.
654                  * @access protected
655                  */
656                 var $header_logo_width = 30;
657
658                 /**
659                  * @var String to print as title on document header.
660                  * @access protected
661                  */
662                 var $header_title = "";
663
664                 /**
665                  * @var String to print on document header.
666                  * @access protected
667                  */
668                 var $header_string = "";
669
670                 /**
671                  * @var Default number of columns for html table.
672                  * @access protected
673                  */
674                 var $default_table_columns = 4;
675
676
677                 // variables for html parser
678
679                 /**
680                  * @var HTML PARSER: store current link.
681                  * @access protected
682                  */
683                 var $HREF;
684
685                 /**
686                  * @var store available fonts list.
687                  * @access protected
688                  */
689                 var $fontlist = array();
690
691                 /**
692                  * @var current foreground color
693                  * @access protected
694                  */
695                 var $fgcolor;
696
697                 /**
698                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
699                  * @access protected
700                  */
701                 var $listordered = array();
702
703                 /**
704                  * @var HTML PARSER: array count list items on nested lists.
705                  * @access protected
706                  */
707                 var $listcount = array();
708
709                 /**
710                  * @var HTML PARSER: current list nesting level.
711                  * @access protected
712                  */
713                 var $listnum = 0;
714
715                 /**
716                  * @var HTML PARSER: indent amount for lists.
717                  * @access protected
718                  */
719                 var $listindent;
720
721                 /**
722                  * @var current background color
723                  * @access protected
724                  */
725                 var $bgcolor;
726
727                 /**
728                  * @var Store temporary font size in points.
729                  * @access protected
730                  */
731                 var $tempfontsize = 10;
732
733                 /**
734                  * @var Bold font style status.
735                  * @access protected
736                  */
737                 var $b;
738
739                 /**
740                  * @var Underlined font style status.
741                  * @access protected
742                  */
743                 var $u;
744
745                 /**
746                  * @var Italic font style status.
747                  * @access protected
748                  */
749                 var $i;
750
751                 /**
752                  * @var Line through font style status.
753                  * @access protected
754                  * @since 2.8.000 (2008-03-19)
755                  */
756                 var $d;
757
758                 /**
759                  * @var spacer for LI tags.
760                  * @access protected
761                  */
762                 var $lispacer = "";
763
764                 /**
765                  * @var default encoding
766                  * @access protected
767                  * @since 1.53.0.TC010
768                  */
769                 var $encoding = "UTF-8";
770
771                 /**
772                  * @var PHP internal encoding
773                  * @access protected
774                  * @since 1.53.0.TC016
775                  */
776                 var $internal_encoding;
777
778                 /**
779                  * @var indicates if the document language is Right-To-Left
780                  * @access protected
781                  * @since 2.0.000
782                  */
783                 var $rtl = false;
784
785                 /**
786                  * @var used to force RTL or LTR string inversion
787                  * @access protected
788                  * @since 2.0.000
789                  */
790                 var $tmprtl = false;
791
792                 // --- Variables used for document encryption:
793
794                 /**
795                  * Indicates whether document is protected
796                  * @access protected
797                  * @since 2.0.000 (2008-01-02)
798                  */
799                 var $encrypted;
800
801                 /**
802                  * U entry in pdf document
803                  * @access protected
804                  * @since 2.0.000 (2008-01-02)
805                  */
806                 var $Uvalue;
807
808                 /**
809                  * O entry in pdf document
810                  * @access protected
811                  * @since 2.0.000 (2008-01-02)
812                  */
813                 var $Ovalue;
814
815                 /**
816                  * P entry in pdf document
817                  * @access protected
818                  * @since 2.0.000 (2008-01-02)
819                  */
820                 var $Pvalue;
821
822                 /**
823                  * encryption object id
824                  * @access protected
825                  * @since 2.0.000 (2008-01-02)
826                  */
827                 var $enc_obj_id;
828
829                 /**
830                  * last RC4 key encrypted (cached for optimisation)
831                  * @access protected
832                  * @since 2.0.000 (2008-01-02)
833                  */
834                 var $last_rc4_key;
835
836                 /**
837                  * last RC4 computed key
838                  * @access protected
839                  * @since 2.0.000 (2008-01-02)
840                  */
841                 var $last_rc4_key_c;
842
843                 // --- bookmark ---
844
845                 /**
846                  * Outlines for bookmark
847                  * @access protected
848                  * @since 2.1.002 (2008-02-12)
849                  */
850                 var $outlines = array();
851
852                 /**
853                  * Outline root for bookmark
854                  * @access protected
855                  * @since 2.1.002 (2008-02-12)
856                  */
857                 var $OutlineRoot;
858
859
860                 // --- javascript and form ---
861
862                 /**
863                  * javascript code
864                  * @access protected
865                  * @since 2.1.002 (2008-02-12)
866                  */
867                 var $javascript = "";
868
869                 /**
870                  * javascript counter
871                  * @access protected
872                  * @since 2.1.002 (2008-02-12)
873                  */
874                 var $n_js;
875
876                 /**
877                  * line trough state
878                  * @access protected
879                  * @since 2.8.000 (2008-03-19)
880                  */
881                 var $linethrough;
882
883                 // --- Variables used for User's Rights ---
884                 // See PDF reference chapter 8.7 Digital Signatures
885
886                 /**
887                  * If true enables user's rights on PDF reader
888                  * @access protected
889                  * @since 2.9.000 (2008-03-26)
890                  */
891                 var $ur;
892
893                 /**
894                  * Names specifying additional document-wide usage rights for the document.
895                  * @access protected
896                  * @since 2.9.000 (2008-03-26)
897                  */
898                 var $ur_document;
899
900                 /**
901                  * Names specifying additional annotation-related usage rights for the document.
902                  * @access protected
903                  * @since 2.9.000 (2008-03-26)
904                  */
905                 var $ur_annots;
906
907                 /**
908                  * Names specifying additional form-field-related usage rights for the document.
909                  * @access protected
910                  * @since 2.9.000 (2008-03-26)
911                  */
912                 var $ur_form;
913
914                 /**
915                  * Names specifying additional signature-related usage rights for the document.
916                  * @access protected
917                  * @since 2.9.000 (2008-03-26)
918                  */
919                 var $ur_signature;
920
921                 /**
922                  * Dot Per Inch Document Resolution (do not change)
923                  * @access protected
924                  * @since 3.0.000 (2008-03-27)
925                  */
926                 var $dpi = 72;
927
928                 /**
929                  * Indicates whether a new page group was requested
930                  * @access protected
931                  * @since 3.0.000 (2008-03-27)
932                  */
933                 var $newpagegroup;
934
935                 /**
936                  * Contains the number of pages of the groups
937                  * @access protected
938                  * @since 3.0.000 (2008-03-27)
939                  */
940                 var $pagegroups;
941
942                 /**
943                  * Contains the alias of the current page group
944                  * @access protected
945                  * @since 3.0.000 (2008-03-27)
946                  */
947                 var $currpagegroup;
948
949                 /**
950                  * Restrict the rendering of some elements to screen or printout.
951                  * @access protected
952                  * @since 3.0.000 (2008-03-27)
953                  */
954                 var $visibility="all";
955
956                 /**
957                  * Print visibility.
958                  * @access protected
959                  * @since 3.0.000 (2008-03-27)
960                  */
961                 var $n_ocg_print;
962
963                 /**
964                  * View visibility.
965                  * @access protected
966                  * @since 3.0.000 (2008-03-27)
967                  */
968                 var $n_ocg_view;
969
970                 /**
971                  * Array of transparency objects and parameters.
972                  * @access protected
973                  * @since 3.0.000 (2008-03-27)
974                  */
975                 var $extgstates;
976
977                 /**
978                  * Set the default JPEG compression quality (1-100)
979                  * @access protected
980                  * @since 3.0.000 (2008-03-27)
981                  */
982                 var $jpeg_quality;
983
984                 /**
985                  * Default cell height ratio.
986                  * @access protected
987                  * @since 3.0.014 (2008-05-23)
988                  */
989                 var $cell_height_ratio = K_CELL_HEIGHT_RATIO;
990
991                 /**
992                  * PDF viewer preferences.
993                  * @access protected
994                  * @since 3.1.000 (2008-06-09)
995                  */
996                 var $viewer_preferences;
997
998                 /**
999                  * A name object specifying how the document should be displayed when opened.
1000                  * @access protected
1001                  * @since 3.1.000 (2008-06-09)
1002                  */
1003                 var $PageMode;
1004
1005                 /**
1006                  * Array for storing gradient information.
1007                  * @access protected
1008                  * @since 3.1.000 (2008-06-09)
1009                  */
1010                 var $gradients = array();
1011
1012                 /**
1013                  * Array used to store positions inside the pages buffer.
1014                  * keys are the page numbers
1015                  * @access protected
1016                  * @since 3.2.000 (2008-06-26)
1017                  */
1018                 var $intmrk = array();
1019
1020                 /**
1021                  * Array used to store footer positions of each page.
1022                  * @access protected
1023                  * @since 3.2.000 (2008-07-01)
1024                  */
1025                 var $footerpos = array();
1026
1027
1028                 /**
1029                  * Array used to store footer lenght of each page.
1030                  * @access protected
1031                  * @since 4.0.014 (2008-07-29)
1032                  */
1033                 var $footerlen = array();
1034
1035                 /**
1036                  * True if a newline is created.
1037                  * @access protected
1038                  * @since 3.2.000 (2008-07-01)
1039                  */
1040                 var $newline = true;
1041
1042                 /**
1043                  * End position of the latest inserted line
1044                  * @access protected
1045                  * @since 3.2.000 (2008-07-01)
1046                  */
1047                 var $endlinex = 0;
1048
1049                 /**
1050                  * PDF string for last line width
1051                  * @access protected
1052                  * @since 4.0.006 (2008-07-16)
1053                  */
1054                 var $linestyleWidth = "";
1055
1056                 /**
1057                  * PDF string for last line width
1058                  * @access protected
1059                  * @since 4.0.006 (2008-07-16)
1060                  */
1061                 var $linestyleCap = "0 J";
1062
1063                 /**
1064                  * PDF string for last line width
1065                  * @access protected
1066                  * @since 4.0.006 (2008-07-16)
1067                  */
1068                 var $linestyleJoin = "0 j";
1069
1070                 /**
1071                  * PDF string for last line width
1072                  * @access protected
1073                  * @since 4.0.006 (2008-07-16)
1074                  */
1075                 var $linestyleDash = "[] 0 d";
1076
1077                 /**
1078                  * True if marked-content sequence is open
1079                  * @access protected
1080                  * @since 4.0.013 (2008-07-28)
1081                  */
1082                 var $openMarkedContent = false;
1083
1084                 /**
1085                  * Count the latest inserted vertical spaces on HTML.
1086                  * @access protected
1087                  * @since 4.0.021 (2008-08-24)
1088                  */
1089                 var $htmlvspace = 0;
1090                 
1091                 /**
1092                  * Array of Spot colors
1093                  * @access protected
1094                  * @since 4.0.024 (2008-09-12)
1095                  */
1096                 var $spot_colors = array();
1097
1098                 //------------------------------------------------------------
1099                 // METHODS
1100                 //------------------------------------------------------------
1101
1102                 /**
1103                  * This is the class constructor.
1104                  * It allows to set up the page format, the orientation and
1105                  * the measure unit used in all the methods (except for the font sizes).
1106                  * @since 1.0
1107                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1108                  * @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.
1109                  * @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>
1110                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1111                  * @param String $encoding charset encoding; default is UTF-8
1112                  */
1113                 function TCPDF($orientation='P', $unit='mm', $format='A4', $uni=true, $encoding="UTF-8") {
1114                         if ($uni) // Fix for FrontAccounting
1115                         {
1116                                 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
1117                                 include_once(dirname(__FILE__)."/unicode_data2.php");
1118                         }
1119                         /* Set internal character encoding to ASCII */
1120                         if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) {
1121                                 $this->internal_encoding = mb_internal_encoding();
1122                                 mb_internal_encoding("ASCII");
1123                         }
1124                         // set language direction
1125                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1126                         $this->tmprtl = false;
1127                         //Some checks
1128                         $this->_dochecks();
1129                         //Initialization of properties
1130                         $this->isunicode = $uni;
1131                         $this->page = 0;
1132                         $this->pagedim = array();
1133                         $this->n = 2;
1134                         $this->buffer = '';
1135                         $this->pages = array();
1136                         $this->state = 0;
1137                         $this->fonts = array();
1138                         $this->FontFiles = array();
1139                         $this->diffs = array();
1140                         $this->images = array();
1141                         $this->links = array();
1142                         $this->gradients = array();
1143                         $this->InFooter = false;
1144                         $this->lasth = 0;
1145                         $this->FontFamily = 'helvetica';
1146                         $this->FontStyle = '';
1147                         $this->FontSizePt = 12;
1148                         $this->underline = false;
1149                         $this->linethrough = false;
1150                         $this->DrawColor = '0 G';
1151                         $this->FillColor = '0 g';
1152                         $this->TextColor = '0 g';
1153                         $this->ColorFlag = false;
1154                         $this->ws = 0;
1155                         // encryption values
1156                         $this->encrypted = false;
1157                         $this->last_rc4_key = '';
1158                         $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";
1159                         //Standard Unicode fonts
1160                         $this->CoreFonts = array(
1161                                 'courier'=>'Courier',
1162                                 'courierB'=>'Courier-Bold',
1163                                 'courierI'=>'Courier-Oblique',
1164                                 'courierBI'=>'Courier-BoldOblique',
1165                                 'helvetica'=>'Helvetica',
1166                                 'helveticaB'=>'Helvetica-Bold',
1167                                 'helveticaI'=>'Helvetica-Oblique',
1168                                 'helveticaBI'=>'Helvetica-BoldOblique',
1169                                 'times'=>'Times-Roman',
1170                                 'timesB'=>'Times-Bold',
1171                                 'timesI'=>'Times-Italic',
1172                                 'timesBI'=>'Times-BoldItalic',
1173                                 'symbol'=>'Symbol',
1174                                 'zapfdingbats'=>'ZapfDingbats'
1175                         );
1176                         //Set scale factor
1177                         $this->setPageUnit($unit);
1178                         // set page format and orientation
1179                         $this->setPageFormat($format, $orientation);
1180                         //Page margins (1 cm)
1181                         $margin = 28.35 / $this->k;
1182                         $this->SetMargins($margin,$margin);
1183                         //Interior cell margin (1 mm)
1184                         $this->cMargin = $margin / 10;
1185                         //Line width (0.2 mm)
1186                         $this->LineWidth = 0.57 / $this->k;
1187                         $this->linestyleWidth = sprintf('%.2f w', ($this->LineWidth * $this->k));
1188                         $this->linestyleCap = "0 J";
1189                         $this->linestyleJoin = "0 j";
1190                         $this->linestyleDash = "[] 0 d";
1191                         //Automatic page break
1192                         $this->SetAutoPageBreak(true, 2*$margin);
1193                         //Full width display mode
1194                         $this->SetDisplayMode('fullwidth');
1195                         //Compression
1196                         $this->SetCompression(true);
1197                         //Set default PDF version number
1198                         $this->PDFVersion = "1.7";
1199                         $this->encoding = $encoding;
1200                         $this->HREF = '';
1201                         $this->getFontsList();
1202                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1203                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1204                         $this->extgstates = array();
1205                         // user's rights
1206                         $this->ur = false;
1207                         $this->ur_document = "/FullSave";
1208                         $this->ur_annots = "/Create/Delete/Modify/Copy/Import/Export";
1209                         $this->ur_form = "/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate";
1210                         $this->ur_signature = "/Modify";
1211                         // set default JPEG quality
1212                         $this->jpeg_quality = 75;
1213                         // initialize some settings
1214                         $this->utf8Bidi(array(""));
1215                 }
1216
1217                 /**
1218                  * Default destructor.
1219                  * @since 1.53.0.TC016
1220                  */
1221                 function TCPDFDestruct() {
1222                         // restore internal encoding
1223                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1224                                 mb_internal_encoding($this->internal_encoding);
1225                         }
1226                 }
1227
1228                 /**
1229                 * Set the units of measure for the document.
1230                 * @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.
1231                 * @since 3.0.015 (2008-06-06)
1232                 */
1233                 function setPageUnit($unit) {
1234                 //Set scale factor
1235                         switch (strtolower($unit)) {
1236                                 // points
1237                                 case 'pt': {
1238                                         $this->k = 1;
1239                                         break;
1240                                 }
1241                                 // millimeters
1242                                 case 'mm': {
1243                                         $this->k = $this->dpi / 25.4;
1244                                         break;
1245                                 }
1246                                 // centimeters
1247                                 case 'cm': {
1248                                         $this->k = $this->dpi / 2.54;
1249                                         break;
1250                                 }
1251                                 // inches
1252                                 case 'in': {
1253                                         $this->k = $this->dpi;
1254                                         break;
1255                                 }
1256                                 // unsupported unit
1257                                 default : {
1258                                         $this->Error('Incorrect unit: '.$unit);
1259                                         break;
1260                                 }
1261                         }
1262                         if (isset($this->CurOrientation)) {
1263                                         $this->setPageOrientation($this->CurOrientation);
1264                         }
1265                 }
1266
1267                 /**
1268                 * Set the page format
1269                 * @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>
1270                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1271                 * @since 3.0.015 (2008-06-06)
1272                 */
1273                 function setPageFormat($format, $orientation="P") {
1274                         //Page format
1275                         if (is_string($format)) {
1276                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1277                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1278                                 switch (strtoupper($format)){
1279                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1280                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1281                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1282                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1283                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1284                                         case 'A3': {$format = array(841.89,1190.55); break;}
1285                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1286                                         case 'A5': {$format = array(419.53,595.28); break;}
1287                                         case 'A6': {$format = array(297.64,419.53); break;}
1288                                         case 'A7': {$format = array(209.76,297.64); break;}
1289                                         case 'A8': {$format = array(147.40,209.76); break;}
1290                                         case 'A9': {$format = array(104.88,147.40); break;}
1291                                         case 'A10': {$format = array(73.70,104.88); break;}
1292                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1293                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1294                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1295                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1296                                         case 'B4': {$format = array(708.66,1000.63); break;}
1297                                         case 'B5': {$format = array(498.90,708.66); break;}
1298                                         case 'B6': {$format = array(354.33,498.90); break;}
1299                                         case 'B7': {$format = array(249.45,354.33); break;}
1300                                         case 'B8': {$format = array(175.75,249.45); break;}
1301                                         case 'B9': {$format = array(124.72,175.75); break;}
1302                                         case 'B10': {$format = array(87.87,124.72); break;}
1303                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1304                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1305                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1306                                         case 'C3': {$format = array(918.43,1298.27); break;}
1307                                         case 'C4': {$format = array(649.13,918.43); break;}
1308                                         case 'C5': {$format = array(459.21,649.13); break;}
1309                                         case 'C6': {$format = array(323.15,459.21); break;}
1310                                         case 'C7': {$format = array(229.61,323.15); break;}
1311                                         case 'C8': {$format = array(161.57,229.61); break;}
1312                                         case 'C9': {$format = array(113.39,161.57); break;}
1313                                         case 'C10': {$format = array(79.37,113.39); break;}
1314                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1315                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1316                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1317                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1318                                         case 'RA4': {$format = array(609.45,864.57); break;}
1319                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1320                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1321                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1322                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1323                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1324                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1325                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1326                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1327                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1328                                 }
1329                                 $this->fwPt = $format[0];
1330                                 $this->fhPt = $format[1];
1331                         }
1332                         else {
1333                                 $this->fwPt = $format[0] * $this->k;
1334                                 $this->fhPt = $format[1] * $this->k;
1335                         }
1336                         $this->setPageOrientation($orientation);
1337                 }
1338
1339
1340                 /**
1341                 * Set page orientation.
1342                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1343                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1344                 * @param float $bottommargin bottom margin of the page.
1345                 * @since 3.0.015 (2008-06-06)
1346                 */
1347                 function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1348                         $orientation = strtoupper($orientation);
1349                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1350                                 $this->CurOrientation = 'P';
1351                                 $this->wPt = $this->fwPt;
1352                                 $this->hPt = $this->fhPt;
1353                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1354                                 $this->CurOrientation = 'L';
1355                                 $this->wPt = $this->fhPt;
1356                                 $this->hPt = $this->fwPt;
1357                         }
1358                         else {
1359                                 $this->Error('Incorrect orientation: '.$orientation);
1360                         }
1361                         $this->w = $this->wPt / $this->k;
1362                         $this->h = $this->hPt / $this->k;
1363                         if (empty($autopagebreak)) {
1364                                 if (isset($this->AutoPageBreak)) {
1365                                         $autopagebreak = $this->AutoPageBreak;
1366                                 } else {
1367                                         $autopagebreak = true;
1368                                 }
1369                         }
1370                         if (empty($bottommargin)) {
1371                                 if (isset($this->bMargin)) {
1372                                         $bottommargin = $this->bMargin;
1373                                 } else {
1374                                         // default value = 2 cm
1375                                         $bottommargin = 2 * 28.35 / $this->k;
1376                                 }
1377                         }
1378                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1379                         // store page dimensions
1380                         $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);
1381                 }
1382
1383                 /**
1384                  * Enable or disable Right-To-Left language mode
1385                  * @param Boolean $enable if true enable Right-To-Left language mode.
1386                  * @since 2.0.000 (2008-01-03)
1387                  */
1388                 function setRTL($enable) {
1389                         $this->rtl = $enable ? true : false;
1390                         $this->tmprtl = false;
1391                 }
1392
1393                 /**
1394                  * Return the RTL status
1395                  * @return boolean
1396                  * @since 4.0.012 (2008-07-24)
1397                  */
1398                 function getRTL() {
1399                         return $this->rtl;
1400                 }
1401
1402                 /**
1403                 * Force temporary RTL language direction
1404                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1405                 * @since 2.1.000 (2008-01-09)
1406                 */
1407                 function setTempRTL($mode) {
1408                         switch ($mode) {
1409                                 case false:
1410                                 case 'L':
1411                                 case 'R': {
1412                                         $this->tmprtl = $mode;
1413                                 }
1414                         }
1415                 }
1416
1417                 /**
1418                 * Set the last cell height.
1419                 * @param float $h cell height.
1420                 * @author Nicola Asuni
1421                 * @since 1.53.0.TC034
1422                 */
1423                 function setLastH($h) {
1424                         $this->lasth = $h;
1425                 }
1426
1427                 /**
1428                 * Get the last cell height.
1429                 * @return last cell height
1430                 * @since 4.0.017 (2008-08-05)
1431                 */
1432                 function getLastH() {
1433                         return $this->lasth;
1434                 }
1435
1436                 /**
1437                 * Set the image scale.
1438                 * @param float $scale image scale.
1439                 * @author Nicola Asuni
1440                 * @since 1.5.2
1441                 */
1442                 function setImageScale($scale) {
1443                         $this->imgscale = $scale;
1444                 }
1445
1446                 /**
1447                 * Returns the image scale.
1448                 * @return float image scale.
1449                 * @author Nicola Asuni
1450                 * @since 1.5.2
1451                 */
1452                 function getImageScale() {
1453                         return $this->imgscale;
1454                 }
1455
1456                 /**
1457                 * Returns the page width in units.
1458                 * @return int page width.
1459                 * @author Nicola Asuni
1460                 * @since 1.5.2
1461                 */
1462                 function getPageWidth() {
1463                         return $this->w;
1464                 }
1465
1466                 /**
1467                 * Returns the page height in units.
1468                 * @return int page height.
1469                 * @author Nicola Asuni
1470                 * @since 1.5.2
1471                 */
1472                 function getPageHeight() {
1473                         return $this->h;
1474                 }
1475
1476                 /**
1477                 * Returns the page break margin.
1478                 * @return int page break margin.
1479                 * @author Nicola Asuni
1480                 * @since 1.5.2
1481                 */
1482                 function getBreakMargin() {
1483                         return $this->bMargin;
1484                 }
1485
1486                 /**
1487                 * Returns the scale factor (number of points in user unit).
1488                 * @return int scale factor.
1489                 * @author Nicola Asuni
1490                 * @since 1.5.2
1491                 */
1492                 function getScaleFactor() {
1493                         return $this->k;
1494                 }
1495
1496                 /**
1497                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1498                 * @param float $left Left margin.
1499                 * @param float $top Top margin.
1500                 * @param float $right Right margin. Default value is the left one.
1501                 * @since 1.0
1502                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1503                 */
1504                 function SetMargins($left, $top, $right=-1) {
1505                         //Set left, top and right margins
1506                         $this->lMargin = $left;
1507                         $this->tMargin = $top;
1508                         if ($right == -1) {
1509                                 $right = $left;
1510                         }
1511                         $this->rMargin = $right;
1512                 }
1513
1514                 /**
1515                 * 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.
1516                 * @param float $margin The margin.
1517                 * @since 1.4
1518                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1519                 */
1520                 function SetLeftMargin($margin) {
1521                         //Set left margin
1522                         $this->lMargin=$margin;
1523                         if (($this->page > 0) AND ($this->x < $margin)) {
1524                                 $this->x = $margin;
1525                         }
1526                 }
1527
1528                 /**
1529                 * Defines the top margin. The method can be called before creating the first page.
1530                 * @param float $margin The margin.
1531                 * @since 1.5
1532                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1533                 */
1534                 function SetTopMargin($margin) {
1535                         //Set top margin
1536                         $this->tMargin=$margin;
1537                         if (($this->page > 0) AND ($this->y < $margin)) {
1538                                 $this->y = $margin;
1539                         }
1540                 }
1541
1542                 /**
1543                 * Defines the right margin. The method can be called before creating the first page.
1544                 * @param float $margin The margin.
1545                 * @since 1.5
1546                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1547                 */
1548                 function SetRightMargin($margin) {
1549                         $this->rMargin=$margin;
1550                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1551                                 $this->x = $this->w - $margin;
1552                         }
1553                 }
1554
1555                 /**
1556                 * Set the internal Cell padding.
1557                 * @param float $pad internal padding.
1558                 * @since 2.1.000 (2008-01-09)
1559                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1560                 */
1561                 function SetCellPadding($pad) {
1562                         $this->cMargin = $pad;
1563                 }
1564
1565                 /**
1566                 * 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.
1567                 * @param boolean $auto Boolean indicating if mode should be on or off.
1568                 * @param float $margin Distance from the bottom of the page.
1569                 * @since 1.0
1570                 * @see Cell(), MultiCell(), AcceptPageBreak()
1571                 */
1572                 function SetAutoPageBreak($auto, $margin=0) {
1573                         //Set auto page break mode and triggering margin
1574                         $this->AutoPageBreak = $auto;
1575                         $this->bMargin = $margin;
1576                         $this->PageBreakTrigger = $this->h - $margin;
1577                 }
1578
1579                 /**
1580                 * Defines the way the document is to be displayed by the viewer.
1581                 * @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>
1582                 * @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>
1583                 * @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>
1584                 * @since 1.2
1585                 */
1586                 function SetDisplayMode($zoom, $layout='SinglePage', $mode="UseNone") {
1587                         //Set display mode in viewer
1588                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1589                                 $this->ZoomMode = $zoom;
1590                         } else {
1591                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1592                         }
1593                         switch ($layout) {
1594                                 case "default":
1595                                 case "single":
1596                                 case "SinglePage": {
1597                                         $this->LayoutMode = "SinglePage";
1598                                         break;
1599                                 }
1600                                 case "continuous":
1601                                 case "OneColumn": {
1602                                         $this->LayoutMode = "OneColumn";
1603                                         break;
1604                                 }
1605                                 case "two":
1606                                 case "TwoColumnLeft": {
1607                                         $this->LayoutMode = "TwoColumnLeft";
1608                                         break;
1609                                 }
1610                                 case "TwoColumnRight": {
1611                                         $this->LayoutMode = "TwoColumnRight";
1612                                         break;
1613                                 }
1614                                 case "TwoPageLeft": {
1615                                         $this->LayoutMode = "TwoPageLeft";
1616                                         break;
1617                                 }
1618                                 case "TwoPageRight": {
1619                                         $this->LayoutMode = "TwoPageRight";
1620                                         break;
1621                                 }
1622                                 default: {
1623                                         $this->LayoutMode = "SinglePage";
1624                                 }
1625                         }
1626                         switch ($mode) {
1627                                 case "UseNone": {
1628                                         $this->PageMode = "UseNone";
1629                                         break;
1630                                 }
1631                                 case "UseOutlines": {
1632                                         $this->PageMode = "UseOutlines";
1633                                         break;
1634                                 }
1635                                 case "UseThumbs": {
1636                                         $this->PageMode = "UseThumbs";
1637                                         break;
1638                                 }
1639                                 case "FullScreen": {
1640                                         $this->PageMode = "FullScreen";
1641                                         break;
1642                                 }
1643                                 case "UseOC": {
1644                                         $this->PageMode = "UseOC";
1645                                         break;
1646                                 }
1647                                 case "": {
1648                                         $this->PageMode = "UseAttachments";
1649                                         break;
1650                                 }
1651                                 default: {
1652                                         $this->PageMode = "UseNone";
1653                                 }
1654                         }
1655                 }
1656
1657                 /**
1658                 * 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.
1659                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1660                 * @param boolean $compress Boolean indicating if compression must be enabled.
1661                 * @since 1.4
1662                 */
1663                 function SetCompression($compress) {
1664                         //Set page compression
1665                         if (function_exists('gzcompress')) {
1666                                 $this->compress = $compress;
1667                         } else {
1668                                 $this->compress = false;
1669                         }
1670                 }
1671
1672                 /**
1673                 * Defines the title of the document.
1674                 * @param string $title The title.
1675                 * @since 1.2
1676                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1677                 */
1678                 function SetTitle($title) {
1679                         //Title of document
1680                         $this->title = $title;
1681                 }
1682
1683                 /**
1684                 * Defines the subject of the document.
1685                 * @param string $subject The subject.
1686                 * @since 1.2
1687                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1688                 */
1689                 function SetSubject($subject) {
1690                         //Subject of document
1691                         $this->subject = $subject;
1692                 }
1693
1694                 /**
1695                 * Defines the author of the document.
1696                 * @param string $author The name of the author.
1697                 * @since 1.2
1698                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
1699                 */
1700                 function SetAuthor($author) {
1701                         //Author of document
1702                         $this->author = $author;
1703                 }
1704
1705                 /**
1706                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
1707                 * @param string $keywords The list of keywords.
1708                 * @since 1.2
1709                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
1710                 */
1711                 function SetKeywords($keywords) {
1712                         //Keywords of document
1713                         $this->keywords = $keywords;
1714                 }
1715
1716                 /**
1717                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
1718                 * @param string $creator The name of the creator.
1719                 * @since 1.2
1720                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
1721                 */
1722                 function SetCreator($creator) {
1723                         //Creator of document
1724                         $this->creator = $creator;
1725                 }
1726
1727                 /**
1728                 * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
1729                 * @param string $alias The alias. Default value: {nb}.
1730                 * @since 1.4
1731                 * @see getAliasNbPages(), PageNo(), Footer()
1732                 */
1733                 function AliasNbPages($alias='{nb}') {
1734                         //Define an alias for total number of pages
1735                         $this->AliasNbPages = $alias;
1736                 }
1737                 
1738                 /**
1739                  * Returns the string alias used for the total number of pages.
1740          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
1741                  * @return string
1742                  * @since 4.0.018 (2008-08-08)
1743                  * @see AliasNbPages(), PageNo(), Footer()
1744                 */
1745                 function getAliasNbPages() {
1746                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
1747                                 return "{".$this->AliasNbPages."}";
1748             }
1749                         return $this->AliasNbPages;
1750                 }
1751
1752                 /**
1753                 * 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.
1754                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
1755                 * @param string $msg The error message
1756                 * @since 1.0
1757                 */
1758                 function Error($msg) {
1759                         //Fatal error
1760                         display_error('<strong>TCPDF error: </strong>'.$msg);
1761                         exit;
1762                 }
1763
1764                 /**
1765                 * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
1766                 * Note: no page is created by this method
1767                 * @since 1.0
1768                 * @see AddPage(), Close()
1769                 */
1770                 function Open() {
1771                         //Begin document
1772                         $this->state = 1;
1773                 }
1774
1775                 /**
1776                 * 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.
1777                 * @since 1.0
1778                 * @see Open(), Output()
1779                 */
1780                 function Close() {
1781                         //Terminate document
1782                         if ($this->state == 3) {
1783                                 return;
1784                         }
1785                         if ($this->page == 0) {
1786                                 $this->AddPage();
1787                         }
1788                         //Page footer
1789                         $this->setFooter();
1790                         //Close page
1791                         $this->_endpage();
1792                         //Close document
1793                         $this->_enddoc();
1794                 }
1795
1796                 /**
1797                 * Move pointer at the specified document page and update page dimensions.
1798                 * @param int $pnum page number
1799                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
1800                 * @since 2.1.000 (2008-01-07)
1801                 * @see getPage(), lastpage(), getNumPages()
1802                 */
1803                 function setPage($pnum, $resetmargins=false) {
1804                         if (($pnum > 0) AND ($pnum <= count($this->pages))) {
1805                                 $this->page = $pnum;
1806                                 $this->wPt = $this->pagedim[$this->page]['w'];
1807                                 $this->hPt = $this->pagedim[$this->page]['h'];
1808                                 $this->w = $this->wPt / $this->k;
1809                                 $this->h = $this->hPt / $this->k;
1810                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
1811                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
1812                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
1813                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
1814                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
1815                                 if ($resetmargins) {
1816                                         $this->lMargin = $this->pagedim[$this->page]['lm'];
1817                                         $this->rMargin = $this->pagedim[$this->page]['rm'];
1818                                         $this->SetY($this->tMargin);
1819                                 }
1820                         } else {
1821                                 $this->Error('Wrong page number on setPage() function.');
1822                         }
1823                 }
1824
1825                 /**
1826                 * Reset pointer to the last document page.
1827                 * @since 2.0.000 (2008-01-04)
1828                 * @see setPage(), getPage(), getNumPages()
1829                 */
1830                 function lastPage() {
1831                         $this->setPage($this->getNumPages());
1832                 }
1833
1834                 /**
1835                 * Get current document page number.
1836                 * @return int page number
1837                 * @since 2.1.000 (2008-01-07)
1838                 * @see setPage(), lastpage(), getNumPages()
1839                 */
1840                 function getPage() {
1841                         return $this->page;
1842                 }
1843
1844
1845                 /**
1846                 * Get the total number of insered pages.
1847                 * @return int number of pages
1848                 * @since 2.1.000 (2008-01-07)
1849                 * @see setPage(), getPage(), lastpage()
1850                 */
1851                 function getNumPages() {
1852                         return count($this->pages);
1853                 }
1854
1855                 /**
1856                 * 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.
1857                 * 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.
1858                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
1859                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1860                 * @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>
1861                 * @since 1.0
1862                 * @see TCPDF(), Header(), Footer(), SetMargins()
1863                 */
1864                 function AddPage($orientation='', $format='') {
1865                         if (!isset($this->original_lMargin)) {
1866                                 $this->original_lMargin = $this->lMargin;
1867                         }
1868                         if (!isset($this->original_rMargin)) {
1869                                 $this->original_rMargin = $this->rMargin;
1870                         }
1871                         if (count($this->pages) > $this->page) {
1872                                 // this page has been already added
1873                                 $this->setPage(($this->page + 1));
1874                                 $this->SetY($this->tMargin);
1875                                 return;
1876                         }
1877                         //Start a new page
1878                         if ($this->state == 0) {
1879                                 $this->Open();
1880                         }
1881                         // save current settings
1882                         $font_family = $this->FontFamily;
1883                         $font_style = $this->FontStyle.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
1884                         $font_size = $this->FontSizePt;
1885                         $prev_rMargin = $this->rMargin;
1886                         $prev_lMargin = $this->lMargin;
1887                         $prev_cMargin = $this->cMargin;
1888                         $prev_linestyleWidth = $this->linestyleWidth;
1889                         $prev_linestyleCap = $this->linestyleCap;
1890                         $prev_linestyleJoin = $this->linestyleJoin;
1891                         $prev_linestyleDash = $this->linestyleDash;
1892                         $prev_DrawColor = $this->DrawColor;
1893                         $prev_FillColor = $this->FillColor;
1894                         $prev_TextColor = $this->TextColor;
1895                         $prev_ColorFlag = $this->ColorFlag;
1896                         if ($this->page > 0) {
1897                                 //Page footer
1898                                 $this->setFooter();
1899                                 //Close page
1900                                 $this->_endpage();
1901                         }
1902                         //Start new page
1903                         $this->_beginpage($orientation, $format);
1904                         // restore graphic styles
1905                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1906                         if (!empty($font_family)) {
1907                                 $this->SetFont($font_family, $font_style, $font_size);
1908                         }
1909                         //Page header
1910                         $this->setHeader();
1911                         // restore graphic styles
1912                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1913                         if (!empty($font_family)) {
1914                                 $this->SetFont($font_family, $font_style, $font_size);
1915                         }
1916                         // restore settings
1917                         $this->FontFamily = $font_family;
1918                         $this->FontStyle = $font_style;
1919                         $this->FontSizePt = $font_size;
1920                         $this->rMargin = $prev_rMargin;
1921                         $this->lMargin = $prev_lMargin;
1922                         $this->cMargin = $prev_cMargin;
1923                         $this->linestyleWidth = $prev_linestyleWidth;
1924                         $this->linestyleCap = $prev_linestyleCap;
1925                         $this->linestyleJoin = $prev_linestyleJoin;
1926                         $this->linestyleDash = $prev_linestyleDash;
1927                         $this->DrawColor = $prev_DrawColor;
1928                         $this->FillColor = $prev_FillColor;
1929                         $this->TextColor = $prev_TextColor;
1930                         $this->ColorFlag = $prev_ColorFlag;
1931                         // mark this point
1932                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1933                 }
1934
1935                 /**
1936                  * Set start-writing mark on current page for multicell borders and fills.
1937                  * This function must be called after calling Image() function for a background image.
1938                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
1939                  * @access public
1940                  * @since 4.0.016 (2008-07-30)
1941                  */
1942                 function setPageMark() {
1943                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1944                 }
1945
1946                 /**
1947                  * Set header data.
1948                  * @param string $ln header image logo
1949                  * @param string $lw header image logo width in mm
1950                  * @param string $ht string to print as title on document header
1951                  * @param string $hs string to print on document header
1952                 */
1953                 function setHeaderData($ln="", $lw=0, $ht="", $hs="") {
1954                         $this->header_logo = $ln;
1955                         $this->header_logo_width = $lw;
1956                         $this->header_title = $ht;
1957                         $this->header_string = $hs;
1958                 }
1959
1960                 /**
1961                  * Returns header data:
1962                  * <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>
1963                  * @return array()
1964                  * @since 4.0.012 (2008-07-24)
1965                  */
1966                 function getHeaderData() {
1967                         $ret = array();
1968                         $ret['logo'] = $this->header_logo;
1969                         $ret['logo_width'] = $this->header_logo_width;
1970                         $ret['title'] = $this->header_title;
1971                         $ret['string'] = $this->header_string;
1972                         return $ret;
1973                 }
1974
1975                 /**
1976                  * Set header margin.
1977                  * (minimum distance between header and top page margin)
1978                  * @param int $hm distance in user units
1979                 */
1980                 function setHeaderMargin($hm=10) {
1981                         $this->header_margin = $hm;
1982                 }
1983
1984                 /**
1985                  * Returns header margin in user units.
1986                  * @return float
1987                  * @since 4.0.012 (2008-07-24)
1988                 */
1989                 function getHeaderMargin() {
1990                         return $this->header_margin;
1991                 }
1992
1993                 /**
1994                  * Set footer margin.
1995                  * (minimum distance between footer and bottom page margin)
1996                  * @param int $fm distance in user units
1997                 */
1998                 function setFooterMargin($fm=10) {
1999                         $this->footer_margin = $fm;
2000                 }
2001
2002                 /**
2003                  * Returns footer margin in user units.
2004                  * @return float
2005                  * @since 4.0.012 (2008-07-24)
2006                 */
2007                 function getFooterMargin() {
2008                         return $this->footer_margin;
2009                 }
2010                 /**
2011                  * Set a flag to print page header.
2012                  * @param boolean $val set to true to print the page header (default), false otherwise.
2013                  */
2014                 function setPrintHeader($val=true) {
2015                         $this->print_header = $val;
2016                 }
2017
2018                 /**
2019                  * Set a flag to print page footer.
2020                  * @param boolean $value set to true to print the page footer (default), false otherwise.
2021                  */
2022                 function setPrintFooter($val=true) {
2023                         $this->print_footer = $val;
2024                 }
2025
2026                 /**
2027                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2028                  * @return float
2029                  */
2030                 function getImageRBX() {
2031                         return $this->img_rb_x;
2032                 }
2033
2034                 /**
2035                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2036                  * @return float
2037                  */
2038                 function getImageRBY() {
2039                         return $this->img_rb_y;
2040                 }
2041
2042                 /**
2043                  * This method is used to render the page header.
2044                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2045                  */
2046                 function Header1() {
2047                         $ormargins = $this->getOriginalMargins();
2048                         $headerfont = $this->getHeaderFont();
2049                         $headerdata = $this->getHeaderData();
2050                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2051                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2052                                 $imgy = $this->getImageRBY();
2053                         } else {
2054                                 $imgy = $this->GetY();
2055                         }
2056                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2057                         // set starting margin for text data cell
2058                         if ($this->getRTL()) {
2059                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2060                         } else {
2061                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2062                         }
2063                         $this->SetTextColor(0, 0, 0);
2064                         // header title
2065                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2066                         $this->SetX($header_x);
2067                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '');
2068                         // header string
2069                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2070                         $this->SetX($header_x);
2071                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, 0, 0, true, 0);
2072                         // print an ending header line
2073                         $this->SetLineStyle(array("width" => 0.85 / $this->getScaleFactor(), "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2074                         $this->SetY(1 + max($imgy, $this->GetY()));
2075                         if ($this->getRTL()) {
2076                                 $this->SetX($ormargins['right']);
2077                         } else {
2078                                 $this->SetX($ormargins['left']);
2079                         }
2080                         $this->Cell(0, 0, '', 'T', 0, 'C');
2081                 }
2082
2083                 /**
2084                  * This method is used to render the page footer.
2085                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2086                  */
2087                 function Footer() {
2088                         $cur_y = $this->GetY();
2089                         $ormargins = $this->getOriginalMargins();
2090                         $this->SetTextColor(0, 0, 0);
2091                         //set style for cell border
2092                         $line_width = 0.85 / $this->getScaleFactor();
2093                         $this->SetLineStyle(array("width" => $line_width, "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2094                         //print document barcode
2095                         $barcode = $this->getBarcode();
2096                         if (!empty($barcode)) {
2097                                 $this->Ln();
2098                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2099                                 $this->write1DBarcode($barcode, "C128B", $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2100                         }
2101                         $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / '.$this->getAliasNbPages();
2102                         $this->SetY($cur_y);
2103                         //Print page number
2104                         if ($this->getRTL()) {
2105                                 $this->SetX($ormargins['right']);
2106                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2107                         } else {
2108                                 $this->SetX($ormargins['left']);
2109                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2110                         }
2111                 }
2112
2113                 /**
2114                  * This method is used to render the page header.
2115                  * @access protected
2116                  * @since 4.0.012 (2008-07-24)
2117                  */
2118                 function setHeader() {
2119                         if ($this->print_header) {
2120                                 $lasth = $this->lasth;
2121                                 $this->_out("q");
2122                                 $this->rMargin = $this->original_rMargin;
2123                                 $this->lMargin = $this->original_lMargin;
2124                                 //set current position
2125                                 if ($this->rtl) {
2126                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2127                                 } else {
2128                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2129                                 }
2130                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2131                                 $this->Header1();
2132                                 //restore position
2133                                 if ($this->rtl) {
2134                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2135                                 } else {
2136                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2137                                 }
2138                                 $this->_out("Q");
2139                                 $this->lasth = $lasth;
2140                         }
2141                 }
2142
2143                 /**
2144                  * This method is used to render the page footer.
2145                  * @access protected
2146                  * @since 4.0.012 (2008-07-24)
2147                  */
2148                 function setFooter() {
2149                         //Page footer
2150                         $this->InFooter = true;
2151                         // mark this point
2152                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
2153                         if ($this->print_footer) {
2154                                 $lasth = $this->lasth;
2155                                 $this->_out("q");
2156                                 $this->rMargin = $this->original_rMargin;
2157                                 $this->lMargin = $this->original_lMargin;
2158                                 //set current position
2159                                 $footer_y = $this->h - $this->footer_margin;
2160                                 if ($this->rtl) {
2161                                         $this->SetXY($this->original_rMargin, $footer_y);
2162                                 } else {
2163                                         $this->SetXY($this->original_lMargin, $footer_y);
2164                                 }
2165                                 $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]);
2166                                 $this->Footer();
2167                                 //restore position
2168                                 if ($this->rtl) {
2169                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2170                                 } else {
2171                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2172                                 }
2173                                 $this->_out("Q");
2174                                 $this->lasth = $lasth;
2175                         }
2176                         $this->footerlen[$this->page] = strlen($this->pages[$this->page]) - $this->footerpos[$this->page];
2177                         $this->InFooter = false;
2178                 }
2179
2180                 /**
2181                 * Returns the current page number.
2182                 * @return int page number
2183                 * @since 1.0
2184                 * @see AliasNbPages(), getAliasNbPages()
2185                 */
2186                 function PageNo() {
2187                         return $this->page;
2188                 }
2189
2190                 /**
2191                 * Defines a new spot color. 
2192                 * It can be expressed in RGB components or gray scale. 
2193                 * The method can be called before the first page is created and the value is retained from page to page.
2194                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2195                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2196                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2197                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2198                 * @since 4.0.024 (2008-09-12)
2199                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2200                 */
2201                 function AddSpotColor($name, $c, $m, $y, $k) {
2202                         if (!isset($this->spot_colors[$name])) {
2203                                 $i = 1 + count($this->spot_colors);
2204                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2205                         }
2206                 }
2207
2208                 /**
2209                 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
2210                 * It can be expressed in RGB components or gray scale.
2211                 * The method can be called before the first page is created and the value is retained from page to page.
2212                 * @param array $color array of colors
2213                 * @since 3.1.000 (2008-06-11)
2214                 * @see SetDrawColor()
2215                 */
2216                 function SetDrawColorArray($color) {
2217                         if (isset($color)) {
2218                                 $color = array_values($color);
2219                                 $r = isset($color[0]) ? $color[0] : -1;
2220                                 $g = isset($color[1]) ? $color[1] : -1;
2221                                 $b = isset($color[2]) ? $color[2] : -1;
2222                                 $k = isset($color[3]) ? $color[3] : -1;
2223                                 if ($r >= 0) {
2224                                         $this->SetDrawColor($r, $g, $b, $k);
2225                                 }
2226                         }
2227                 }
2228
2229                 /**
2230                 * 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.
2231                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2232                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2233                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2234                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2235                 * @since 1.3
2236                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2237                 */
2238                 function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2239                         // set default values
2240                         if (!is_numeric($col1)) {
2241                                 $col1 = 0;
2242                         }
2243                         if (!is_numeric($col2)) {
2244                                 $col2 = -1;
2245                         }
2246                         if (!is_numeric($col3)) {
2247                                 $col3 = -1;
2248                         }
2249                         if (!is_numeric($col4)) {
2250                                 $col4 = -1;
2251                         }
2252                         //Set color for all stroking operations
2253                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2254                                 // Grey scale
2255                                 $this->DrawColor = sprintf('%.3f G', $col1/255);
2256                         } elseif ($col4 == -1) {
2257                                 // RGB
2258                                 $this->DrawColor = sprintf('%.3f %.3f %.3f RG', $col1/255, $col2/255, $col3/255);
2259                         } else {
2260                                 // CMYK
2261                                 $this->DrawColor = sprintf('%.3f %.3f %.3f %.3f K', $col1/100, $col2/100, $col3/100, $col4/100);
2262                         }
2263                         if ($this->page > 0) {
2264                                 $this->_out($this->DrawColor);
2265                         }
2266                 }
2267                 
2268                 /**
2269                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2270                 * @param string $name name of the spot color
2271                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2272                 * @since 4.0.024 (2008-09-12)
2273                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2274                 */
2275                 function SetDrawSpotColor($name, $tint=100) {
2276                         if (!isset($this->spot_colors[$name])) {
2277                                 $this->Error('Undefined spot color: '.$name);
2278                         }
2279                         $this->DrawColor = sprintf('/CS%d CS %.3f SCN', $this->spot_colors[$name]['i'], $tint/100);
2280                         if ($this->page > 0) {
2281                                 $this->_out($this->DrawColor);
2282                         }
2283                 }
2284
2285                 /**
2286                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
2287                 * It can be expressed in RGB components or gray scale.
2288                 * The method can be called before the first page is created and the value is retained from page to page.
2289                 * @param array $color array of colors
2290                 * @since 3.1.000 (2008-6-11)
2291                 * @see SetFillColor()
2292                 */
2293                 function SetFillColorArray($color) {
2294                         if (isset($color)) {
2295                                 $color = array_values($color);
2296                                 $r = isset($color[0]) ? $color[0] : -1;
2297                                 $g = isset($color[1]) ? $color[1] : -1;
2298                                 $b = isset($color[2]) ? $color[2] : -1;
2299                                 $k = isset($color[3]) ? $color[3] : -1;
2300                                 if ($r >= 0) {
2301                                         $this->SetFillColor($r, $g, $b, $k);
2302                                 }
2303                         }
2304                 }
2305
2306                 /**
2307                 * 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.
2308                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2309                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2310                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2311                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2312                 * @since 1.3
2313                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2314                 */
2315                 function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2316                         // set default values
2317                         if (!is_numeric($col1)) {
2318                                 $col1 = 0;
2319                         }
2320                         if (!is_numeric($col2)) {
2321                                 $col2 = -1;
2322                         }
2323                         if (!is_numeric($col3)) {
2324                                 $col3 = -1;
2325                         }
2326                         if (!is_numeric($col4)) {
2327                                 $col4 = -1;
2328                         }
2329                         //Set color for all filling operations
2330                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2331                                 // Grey scale
2332                                 $this->FillColor = sprintf('%.3f g', $col1/255);
2333                                 $this->bgcolor = array('G' => $col1);
2334                         } elseif ($col4 == -1) {
2335                                 // RGB
2336                                 $this->FillColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2337                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2338                         } else {
2339                                 // CMYK
2340                                 $this->FillColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2341                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2342                         }
2343                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2344                         if ($this->page > 0) {
2345                                 $this->_out($this->FillColor);
2346                         }
2347                 }
2348                 
2349                 /**
2350                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2351                 * @param string $name name of the spot color
2352                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2353                 * @since 4.0.024 (2008-09-12)
2354                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2355                 */
2356                 function SetFillSpotColor($name, $tint=100) {
2357                         if (!isset($this->spot_colors[$name])) {
2358                                 $this->Error('Undefined spot color: '.$name);
2359                         }
2360                         $this->FillColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2361                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2362                         if ($this->page > 0) {
2363                                 $this->_out($this->FillColor);
2364                         }
2365                 }
2366
2367                 /**
2368                 * Defines the color used for text. It can be expressed in RGB components or gray scale.
2369                 * The method can be called before the first page is created and the value is retained from page to page.
2370                 * @param array $color array of colors
2371                 * @since 3.1.000 (2008-6-11)
2372                 * @see SetFillColor()
2373                 */
2374                 function SetTextColorArray($color) {
2375                         if (isset($color)) {
2376                                 $color = array_values($color);
2377                                 $r = isset($color[0]) ? $color[0] : -1;
2378                                 $g = isset($color[1]) ? $color[1] : -1;
2379                                 $b = isset($color[2]) ? $color[2] : -1;
2380                                 $k = isset($color[3]) ? $color[3] : -1;
2381                                 if ($r >= 0) {
2382                                         $this->SetTextColor($r, $g, $b, $k);
2383                                 }
2384                         }
2385                 }
2386
2387                 /**
2388                 * 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.
2389                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2390                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2391                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2392                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2393                 * @since 1.3
2394                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2395                 */
2396                 function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2397                         // set default values
2398                         if (!is_numeric($col1)) {
2399                                 $col1 = 0;
2400                         }
2401                         if (!is_numeric($col2)) {
2402                                 $col2 = -1;
2403                         }
2404                         if (!is_numeric($col3)) {
2405                                 $col3 = -1;
2406                         }
2407                         if (!is_numeric($col4)) {
2408                                 $col4 = -1;
2409                         }
2410                         //Set color for text
2411                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2412                                 // Grey scale
2413                                 $this->TextColor = sprintf('%.3f g', $col1/255);
2414                                 $this->fgcolor = array('G' => $col1);
2415                         } elseif ($col4 == -1) {
2416                                 // RGB
2417                                 $this->TextColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2418                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2419                         } else {
2420                                 // CMYK
2421                                 $this->TextColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2422                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2423                         }
2424                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2425                 }
2426
2427                 /**
2428                 * Defines the spot color used for text.
2429                 * @param string $name name of the spot color
2430                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2431                 * @since 4.0.024 (2008-09-12)
2432                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2433                 */
2434                 function SetTextSpotColor($name, $tint=100) {
2435                         if (!isset($this->spot_colors[$name])) {
2436                                 $this->Error('Undefined spot color: '.$name);
2437                         }
2438                         $this->TextColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2439                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2440                         if ($this->page > 0) {
2441                                 $this->_out($this->TextColor);
2442                         }
2443                 }
2444
2445                 /**
2446                 * Returns the length of a string in user unit. A font must be selected.<br>
2447                 * @param string $s The string whose length is to be computed
2448                 * @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.
2449                 * @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.
2450                 * @param float $fontsize Font size in points. The default value is the current size.
2451                 * @return int string length
2452                 * @author Nicola Asuni
2453                 * @since 1.2
2454                 */
2455                 function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2456                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $this->tmprtl), $fontname, $fontstyle, $fontsize);
2457                 }
2458
2459                 /**
2460                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2461                 * @param string $arr The array of chars whose total length is to be computed
2462                 * @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.
2463                 * @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.
2464                 * @param float $fontsize Font size in points. The default value is the current size.
2465                 * @return int string length
2466                 * @author Nicola Asuni
2467                 * @since 2.4.000 (2008-03-06)
2468                 */
2469                 function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2470                         // store current values
2471                         if (!empty($fontname)) {
2472                                 $prev_FontFamily = $this->FontFamily;
2473                                 $prev_FontStyle = $this->FontStyle;
2474                                 $prev_FontSizePt = $this->FontSizePt;
2475                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2476                         }
2477                         $w = 0;
2478                         foreach($sa as $char) {
2479                                 $w += $this->GetCharWidth($char);
2480                         }
2481                         // restore previous values
2482                         if (!empty($fontname)) {
2483                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2484                         }
2485                         return $w;
2486                 }
2487
2488                 /**
2489                 * Returns the length of the char in user unit for the current font.<br>
2490                 * @param int $char The char code whose length is to be returned
2491                 * @return int char width
2492                 * @author Nicola Asuni
2493                 * @since 2.4.000 (2008-03-06)
2494                 */
2495                 function GetCharWidth($char) {
2496                         $cw = &$this->CurrentFont['cw'];
2497                         if (isset($cw[$char])) {
2498                                 $w = $cw[$char];
2499                                 /*
2500                         } elseif (isset($cw[ord($char)])) {
2501                                 $w = $cw[ord($char)];
2502                         } elseif (isset($cw[chr($char)])) {
2503                                 $w = $cw[chr($char)];
2504                                 */
2505                         } elseif (isset($this->CurrentFont['dw'])) {
2506                                 $w = $this->CurrentFont['dw'];
2507                         } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
2508                                 $w = $this->CurrentFont['desc']['MissingWidth']; // set default size
2509                         } else {
2510                                 $w = 500; // default width
2511                         }
2512                         return ($w * $this->FontSize / 1000);
2513                 }
2514
2515                 /**
2516                 * Returns the numbero of characters in a string.
2517                 * @param string $s The input string.
2518                 * @return int number of characters
2519                 * @since 2.0.0001 (2008-01-07)
2520                 */
2521                 function GetNumChars($s) {
2522                         if ($this->isunicode) {
2523                                 return count($this->UTF8StringToArray($s));
2524                         }
2525                         return strlen($s);
2526                 }
2527
2528                 /**
2529                 * Fill the list of available fonts ($this->fontlist).
2530                 * @access protected
2531                 * @since 4.0.013 (2008-07-28)
2532                 */
2533                 function getFontsList() {
2534                         $fontsdir = opendir($this->_getfontpath());
2535                         while (($file = readdir($fontsdir)) !== false) {
2536                                 if (substr($file, -4) == ".php") {
2537                                                 array_push($this->fontlist, strtolower(basename($file, ".php")));
2538                                 }
2539                         }
2540                         closedir($fontsdir);
2541                 }
2542
2543                 /**
2544                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2545                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
2546                 * 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.
2547                 * Changed to support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
2548                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2549                 * @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>
2550                 * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space.
2551                 * @return array containing the font data, or false in case of error.
2552                 * @since 1.5
2553                 * @see SetFont()
2554                 */
2555                 function AddFont($family, $style='', $file='') {
2556                         if (empty($family)) {
2557                                 if (!empty($this->FontFamily)) {
2558                                         $family = $this->FontFamily;
2559                                 } else {
2560                                         $this->Error('Empty font family');
2561                                 }
2562                         }
2563                         $family = strtolower($family);
2564                         if ((!$this->isunicode) AND ($family == 'arial')) {
2565                                 $family = 'helvetica';
2566                         }
2567                         if (($family == "symbol") OR ($family == "zapfdingbats")) {
2568                                 $style = '';
2569                         }
2570                         $style = strtoupper($style);
2571                         // underline
2572                         if (strpos($style,'U') !== false) {
2573                                 $this->underline = true;
2574                                 $style = str_replace('U', '', $style);
2575                         } else {
2576                                 $this->underline = false;
2577                         }
2578                         //line through (deleted)
2579                         if (strpos($style,'D') !== false) {
2580                                 $this->linethrough = true;
2581                                 $style = str_replace('D', '', $style);
2582                         } else {
2583                                 $this->linethrough = false;
2584                         }
2585                         if ($style == 'IB') {
2586                                 $style = 'BI';
2587                         }
2588                         $fontkey = $family.$style;
2589                         $fontdata = array("fontkey" => $fontkey, "family" => $family, "style" => $style);
2590                         // check if the font has been already added
2591                         if (isset($this->fonts[$fontkey])) {
2592                                 return $fontdata;
2593                         }
2594                         if ($file == '') {
2595                                 $file = str_replace(' ', '', $family).strtolower($style).'.php';
2596                         }
2597                         if (!file_exists($this->_getfontpath().$file)) {
2598                                 // try to load the basic file without styles
2599                                 $file = str_replace(' ', '', $family).'.php';
2600                         }
2601                         if (isset($type)) {
2602                                 unset($type);
2603                         }
2604                         if (isset($cw)) {
2605                                 unset($cw);
2606                         }
2607                         @include($this->_getfontpath().$file);
2608                         if ((!isset($type)) OR (!isset($cw))) {
2609                                 $this->Error("Could not include font definition file: ".$file);
2610                         }
2611                         $i = count($this->fonts) + 1;
2612                         // register CID font (all styles at once)
2613                         if ($type == 'cidfont0') {
2614                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
2615                                 foreach ($styles as $skey => $qual) {
2616                                         $sname = $name.$qual;
2617                                         $sfontkey = $family.$skey;
2618                                         $this->fonts[$sfontkey] = array('i' => $i, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc);
2619                                         $i = count($this->fonts) + 1;
2620                                 }
2621                                 $file = '';
2622                         } elseif ($type == 'core') {
2623                                 $def_width = $cw[ord('?')];
2624                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $def_width);
2625                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
2626                                 if (!isset($file)) {
2627                                         $file = '';
2628                                 }
2629                                 if (!isset($enc)) {
2630                                         $enc = '';
2631                                 }
2632                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'file' => $file, 'enc' => $enc, 'desc' => $desc);
2633                         } elseif ($type == 'TrueTypeUnicode') {
2634                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg);
2635                         } else {
2636                                 $this->Error('Unknow font type');
2637                         }
2638                         if (isset($diff) AND (!empty($diff))) {
2639                                 //Search existing encodings
2640                                 $d = 0;
2641                                 $nb = count($this->diffs);
2642                                 for($i=1; $i <= $nb; $i++) {
2643                                         if ($this->diffs[$i] == $diff) {
2644                                                 $d = $i;
2645                                                 break;
2646                                         }
2647                                 }
2648                                 if ($d == 0) {
2649                                         $d = $nb + 1;
2650                                         $this->diffs[$d] = $diff;
2651                                 }
2652                                 $this->fonts[$fontkey]['diff'] = $d;
2653                         }
2654                         if (!empty($file)) {
2655                                 if ((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) {
2656                                         $this->FontFiles[$file] = array('length1' => $originalsize);
2657                                 } elseif ($type != 'core') {
2658                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2);
2659                                 }
2660                         }
2661                         return $fontdata;
2662                 }
2663
2664                 /**
2665                 * Sets the font used to print character strings.
2666                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
2667                 * The method can be called before the first page is created and the font is retained from page to page.
2668                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
2669                 * 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 />
2670                 * @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.
2671                 * @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.
2672                 * @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
2673                 * @since 1.0
2674                 * @see AddFont(), SetFontSize()
2675                 */
2676                 function SetFont($family, $style='', $size=0) {
2677                         //Select a font; size given in points
2678                         if ($size == 0) {
2679                                 $size = $this->FontSizePt;
2680                         }
2681                         // try to add font (if not already added)
2682                         $fontdata =  $this->AddFont($family, $style);
2683                         $this->FontFamily = $fontdata['family'];
2684                         $this->FontStyle = $fontdata['style'];
2685                         $this->CurrentFont = &$this->fonts[$fontdata['fontkey']];
2686                         $this->SetFontSize($size);
2687                 }
2688
2689                 /**
2690                 * Defines the size of the current font.
2691                 * @param float $size The size (in points)
2692                 * @since 1.0
2693                 * @see SetFont()
2694                 */
2695                 function SetFontSize($size) {
2696                         //Set font size in points
2697                         $this->FontSizePt = $size;
2698                         $this->FontSize = $size / $this->k;
2699                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
2700                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
2701                         } else {
2702                                 $this->FontAscent = 0.8 * $this->FontSize;
2703                         }
2704                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
2705                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
2706                         } else {
2707                                 $this->FontDescent = 0.2 * $this->FontSize;
2708                         }
2709                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
2710                                 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2711                         }
2712                 }
2713
2714                 /**
2715                 * 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 />
2716                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
2717                 * @since 1.5
2718                 * @see Cell(), Write(), Image(), Link(), SetLink()
2719                 */
2720                 function AddLink() {
2721                         //Create a new internal link
2722                         $n = count($this->links) + 1;
2723                         $this->links[$n] = array(0, 0);
2724                         return $n;
2725                 }
2726
2727                 /**
2728                 * Defines the page and position a link points to.
2729                 * @param int $link The link identifier returned by AddLink()
2730                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
2731                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
2732                 * @since 1.5
2733                 * @see AddLink()
2734                 */
2735                 function SetLink($link, $y=0, $page=-1) {
2736                         if ($y == -1) {
2737                                 $y = $this->y;
2738                         }
2739                         if ($page == -1) {
2740                                 $page = $this->page;
2741                         }
2742                         $this->links[$link] = array($page, $y);
2743                 }
2744
2745                 /**
2746                 * Puts a link on a rectangular area of the page.
2747                 * 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.
2748                 * @param float $x Abscissa of the upper-left corner of the rectangle
2749                 * @param float $y Ordinate of the upper-left corner of the rectangle
2750                 * @param float $w Width of the rectangle
2751                 * @param float $h Height of the rectangle
2752                 * @param mixed $link URL or identifier returned by AddLink()
2753                 * @since 1.5
2754                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
2755                 */
2756                 function Link($x, $y, $w, $h, $link) {
2757                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'));
2758                 }
2759                 
2760                 /**
2761                 * Puts a text annotation on a rectangular area of the page.
2762                 * !!!! THIS FUNCTION IS NOT YET FULLY IMPLEMENTED !!!!
2763                 * @param float $x Abscissa of the upper-left corner of the rectangle
2764                 * @param float $y Ordinate of the upper-left corner of the rectangle
2765                 * @param float $w Width of the rectangle
2766                 * @param float $h Height of the rectangle
2767                 * @param string $text annotation text
2768                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
2769                 * @since 4.0.018 (2008-08-06)
2770                 */
2771                 function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text')) {
2772                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt);
2773                 }
2774
2775                 /**
2776                 * 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.
2777                 * @param float $x Abscissa of the origin
2778                 * @param float $y Ordinate of the origin
2779                 * @param string $txt String to print
2780                 * @param int $stroke outline size in points (0 = disable)
2781                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
2782                 * @since 1.0
2783                 * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
2784                 */
2785                 function Text($x, $y, $txt, $stroke=0, $clip=false) {
2786                         //Output a string
2787                         if ($this->rtl) {
2788                                 // bidirectional algorithm (some chars may be changed affecting the line length)
2789                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $this->tmprtl);
2790                                 $l = $this->GetArrStringWidth($s);
2791                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
2792                         } else {
2793                                 $xr = $x;
2794                         }
2795                         $opt = "";
2796                         if (($stroke > 0) AND (!$clip)) {
2797                                 $opt .= "1 Tr ".intval($stroke)." w ";
2798                         } elseif (($stroke > 0) AND $clip) {
2799                                 $opt .= "5 Tr ".intval($stroke)." w ";
2800                         } elseif ($clip) {
2801                                 $opt .= "7 Tr ";
2802                         }
2803                         $s = sprintf('BT %.2f %.2f Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
2804                         if ($this->underline AND ($txt!='')) {
2805                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
2806                         }
2807                         if ($this->linethrough AND ($txt!='')) {
2808                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt);
2809                         }
2810                         if ($this->ColorFlag AND (!$clip)) {
2811                                 $s='q '.$this->TextColor.' '.$s.' Q';
2812                         }
2813                         $this->_out($s);
2814                 }
2815
2816                 /**
2817                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
2818                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
2819                 * This method is called automatically and should not be called directly by the application.
2820                 * @return boolean
2821                 * @since 1.4
2822                 * @see SetAutoPageBreak()
2823                 */
2824                 function AcceptPageBreak() {
2825                         return $this->AutoPageBreak;
2826                 }
2827
2828                 /**
2829                 * Add page if needed.
2830                 * @param float $h Cell height. Default value: 0.
2831                 * @since 3.2.000 (2008-07-01)
2832                 * @access protected
2833                 */
2834                 function checkPageBreak($h) {
2835                         if ((($this->y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
2836                                 $rs = "";
2837                                 //Automatic page break
2838                                 $x = $this->x;
2839                                 $ws = $this->ws;
2840                                 if ($ws > 0) {
2841                                         $this->ws = 0;
2842                                         $rs .= '0 Tw';
2843                                 }
2844                                 $this->AddPage($this->CurOrientation);
2845                                 if ($ws > 0) {
2846                                         $this->ws = $ws;
2847                                         $rs .= sprintf('%.3f Tw', $ws * $k);
2848                                 }
2849                                 $this->_out($rs);
2850                                 $this->y = $this->tMargin;
2851                                 $this->x = $x;
2852                         }
2853                 }
2854
2855                 /**
2856                 * 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 />
2857                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2858                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2859                 * @param float $h Cell height. Default value: 0.
2860                 * @param string $txt String to print. Default value: empty string.
2861                 * @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>
2862                 * @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>
2863                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2864                 * @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>
2865                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2866                 * @param mixed $link URL or identifier returned by AddLink().
2867                 * @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>
2868                 * @since 1.0
2869                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
2870                 */
2871                 function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2872                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
2873                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2874                         if ($h < $min_cell_height) {
2875                                 $h = $min_cell_height;
2876                         }
2877                         $this->checkPageBreak($h);
2878                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch));
2879                 }
2880
2881                 /**
2882                 * 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 />
2883                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2884                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2885                 * @param float $h Cell height. Default value: 0.
2886                 * @param string $txt String to print. Default value: empty string.
2887                 * @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>
2888                 * @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.
2889                 * @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>
2890                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2891                 * @param mixed $link URL or identifier returned by AddLink().
2892                 * @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>
2893                 * @since 1.0
2894                 * @see Cell()
2895                 */
2896                 function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2897                         $rs = ""; //string to be returned
2898                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2899                         if ($h < $min_cell_height) {
2900                                 $h = $min_cell_height;
2901                         }
2902                         $k = $this->k;
2903                         if (empty($w) OR ($w <= 0)) {
2904                                 if ($this->rtl) {
2905                                         $w = $this->x - $this->lMargin;
2906                                 } else {
2907                                         $w = $this->w - $this->rMargin - $this->x;
2908                                 }
2909                         }
2910                         $s = '';
2911                         if (($fill == 1) OR ($border == 1)) {
2912                                 if ($fill == 1) {
2913                                         $op = ($border == 1) ? 'B' : 'f';
2914                                 } else {
2915                                         $op = 'S';
2916                                 }
2917                                 if ($this->rtl) {
2918                                         $xk = (($this->x  - $w) * $k);
2919                                 } else {
2920                                         $xk = ($this->x * $k);
2921                                 }
2922                                 $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
2923                         }
2924
2925                         if (is_string($border)) {
2926                                 $x = $this->x;
2927                                 $y = $this->y;
2928                                 if (strpos($border,'L') !== false) {
2929                                         if ($this->rtl) {
2930                                                 $xk = ($x - $w) * $k;
2931                                         } else {
2932                                                 $xk = $x * $k;
2933                                         }
2934                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h)) * $k));
2935                                 }
2936                                 if (strpos($border,'T') !== false) {
2937                                         if ($this->rtl) {
2938                                                 $xk = ($x - $w) * $k;
2939                                                 $xwk = $x * $k;
2940                                         } else {
2941                                                 $xk = $x * $k;
2942                                                 $xwk = ($x + $w) * $k;
2943                                         }
2944                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
2945                                 }
2946                                 if (strpos($border,'R') !== false) {
2947                                         if ($this->rtl) {
2948                                                 $xk = $x * $k;
2949                                         } else {
2950                                                 $xk = ($x + $w) * $k;
2951                                         }
2952                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h))* $k));
2953                                 }
2954                                 if (strpos($border,'B') !== false) {
2955                                         if ($this->rtl) {
2956                                                 $xk = ($x - $w) * $k;
2957                                                 $xwk = $x * $k;
2958                                         } else {
2959                                                 $xk = $x * $k;
2960                                                 $xwk = ($x + $w) * $k;
2961                                         }
2962                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
2963                                 }
2964                         }
2965                         if ($txt != '') {
2966                                 // text lenght
2967                                 $width = $this->GetStringWidth($txt);
2968                                 // ratio between cell lenght and text lenght
2969                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
2970
2971                                 // stretch text if required
2972                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
2973                                         if ($stretch > 2) {
2974                                                 // spacing
2975                                                 //Calculate character spacing in points
2976                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
2977                                                 //Set character spacing
2978                                                 $rs .= sprintf('BT %.2f Tc ET ', $char_space);
2979                                         } else {
2980                                                 // scaling
2981                                                 //Calculate horizontal scaling
2982                                                 $horiz_scale = $ratio * 100.0;
2983                                                 //Set horizontal scaling
2984                                                 $rs .= sprintf('BT %.2f Tz ET ', $horiz_scale);
2985                                         }
2986                                         $align = '';
2987                                         $width = $w - (2 * $this->cMargin);
2988                                 } else {
2989                                         $stretch == 0;
2990                                 }
2991                                 if ($align == 'L') {
2992                                         if ($this->rtl) {
2993                                                 $dx = $w - $width - $this->cMargin;
2994                                         } else {
2995                                                 $dx = $this->cMargin;
2996                                         }
2997                                 } elseif ($align == 'R') {
2998                                         if ($this->rtl) {
2999                                                 $dx = $this->cMargin;
3000                                         } else {
3001                                                 $dx = $w - $width - $this->cMargin;
3002                                         }
3003                                 } elseif ($align == 'C') {
3004                                         $dx = ($w - $width) / 2;
3005                                 } elseif ($align == 'J') {
3006                                         if ($this->rtl) {
3007                                                 $dx = $w - $width - $this->cMargin;
3008                                         } else {
3009                                                 $dx = $this->cMargin;
3010                                         }
3011                                 } else {
3012                                         $dx = $this->cMargin;
3013                                 }
3014                                 if ($this->ColorFlag) {
3015                                         $s .= 'q '.$this->TextColor.' ';
3016                                 }
3017                                 $txt2 = $this->_escapetext($txt);
3018                                 if ($this->rtl) {
3019                                         $xdk = ($this->x - $dx - $width) * $k;
3020                                 } else {
3021                                         $xdk = ($this->x + $dx) * $k;
3022                                 }
3023                                 // Justification
3024                                 if ($align == 'J') {
3025                                         // count number of spaces
3026                                         $ns = substr_count($txt, ' ');
3027                                         //if ($this->isunicode) {
3028                                         if (($this->CurrentFont['type'] == "TrueTypeUnicode") OR ($this->CurrentFont['type'] == "cidfont0")) {
3029                                                 // get string width without spaces
3030                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3031                                                 // calculate average space width
3032                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3033                                                 // set word position to be used with TJ operator
3034                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3035                                         } else {
3036                                                 // get string width
3037                                                 $width = $this->GetStringWidth($txt);
3038                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3039                                                 $rs .= sprintf('BT %.3f Tw ET ', $spacewidth);
3040                                         }
3041                                 }
3042                                 // calculate approximate position of the font base line
3043                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3044                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3045                                 // print text
3046                                 $s .= sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3047                                 if ($this->rtl) {
3048                                         $xdx = $this->x - $dx - $width;
3049                                 } else {
3050                                         $xdx = $this->x + $dx;
3051                                 }
3052                                 if ($this->underline)  {
3053                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3054                                 }
3055                                 if ($this->linethrough) {
3056                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3057                                 }
3058                                 if ($this->ColorFlag) {
3059                                         $s .= ' Q';
3060                                 }
3061                                 if ($link) {
3062                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link);
3063                                 }
3064                         }
3065                         // output cell
3066                         if ($s) {
3067                                 // output cell
3068                                 $rs .= $s;
3069                                 // reset text stretching
3070                                 if ($stretch > 2) {
3071                                         //Reset character horizontal spacing
3072                                         $rs .= ' BT 0 Tc ET';
3073                                 } elseif ($stretch > 0) {
3074                                         //Reset character horizontal scaling
3075                                         $rs .= ' BT 100 Tz ET';
3076                                 }
3077                         }
3078                         // reset word spacing
3079                         if ((!$this->isunicode) AND ($align == 'J')) {
3080                                 $rs .= ' BT 0 Tw ET';
3081                         }
3082                         $this->lasth = $h;
3083                         if ($ln > 0) {
3084                                 //Go to the beginning of the next line
3085                                 $this->y += $h;
3086                                 if ($ln == 1) {
3087                                         if ($this->rtl) {
3088                                                 $this->x = $this->w - $this->rMargin;
3089                                         } else {
3090                                                 $this->x = $this->lMargin;
3091                                         }
3092                                 }
3093                         } else {
3094                                 // go left or right by case
3095                                 if ($this->rtl) {
3096                                         $this->x -= $w;
3097                                 } else {
3098                                         $this->x += $w;
3099                                 }
3100                         }
3101                         $gstyles = $this->linestyleWidth." ".$this->linestyleCap." ".$this->linestyleJoin." ".$this->linestyleDash." ".$this->DrawColor." ".$this->FillColor."\n";
3102                         $rs = $gstyles.$rs;
3103                         return $rs;
3104                 }
3105
3106                 /**
3107                 * This method allows printing text with line breaks.
3108                 * 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 />
3109                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3110                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3111                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3112                 * @param string $txt String to print
3113                 * @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>
3114                 * @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>
3115                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3116                 * @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>
3117                 * @param int $x x position in user units
3118                 * @param int $y y position in user units
3119                 * @param boolean $reseth if true reset the last cell height (default true).
3120                 * @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>
3121                 * @param boolean $ishtml se to true if $txt is HTML content (default = false).
3122                 * @return int Return the number of cells or 1 for html mode.
3123                 * @since 1.3
3124                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3125                 */
3126                 function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false) {
3127                         if ((empty($this->lasth))OR ($reseth)) {
3128                                 //set row height
3129                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3130                         }
3131                         if (!empty($y)) {
3132                                 $this->SetY($y);
3133                         } else {
3134                                 $y = $this->GetY();
3135                         }
3136                         // check for page break
3137                         $this->checkPageBreak($h);
3138                         $y = $this->GetY();
3139                         // get current page number
3140                         $startpage = $this->page;
3141                         if (!empty($x)) {
3142                                 $this->SetX($x);
3143                         } else {
3144                                 $x = $this->GetX();
3145                         }
3146                         if (empty($w) OR ($w <= 0)) {
3147                                 if ($this->rtl) {
3148                                         $w = $this->x - $this->lMargin;
3149                                 } else {
3150                                         $w = $this->w - $this->rMargin - $this->x;
3151                                 }
3152                         }
3153                         // store original margin values
3154                         $lMargin = $this->lMargin;
3155                         $rMargin = $this->rMargin;
3156                         if ($this->rtl) {
3157                                 $this->SetRightMargin($this->w - $this->x);
3158                                 $this->SetLeftMargin($this->x - $w);
3159                         } else {
3160                                 $this->SetLeftMargin($this->x);
3161                                 $this->SetRightMargin($this->w - $this->x - $w);
3162                         }
3163                         // calculate remaining vertical space on first page ($startpage)
3164                         $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
3165                         // Adjust internal padding
3166                         if ($this->cMargin < ($this->LineWidth / 2)) {
3167                                 $this->cMargin = ($this->LineWidth / 2);
3168                         }
3169                         // Add top space if needed
3170                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3171                                 $this->y += $this->LineWidth / 2;
3172                         }
3173                         // add top padding
3174                         $this->y += $this->cMargin;
3175                         if ($ishtml) {
3176                                 // Write HTML text
3177                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3178                                 $nl = 1;
3179                         } else {
3180                                 // Write text
3181                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false);
3182                         }
3183                         // add bottom padding
3184                         $this->y += $this->cMargin;
3185                         // Add bottom space if needed
3186                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3187                                 $this->y += $this->LineWidth / 2;
3188                         }
3189                         // Get end-of-text Y position
3190                         $currentY = $this->GetY();
3191                         // get latest page number
3192                         $endpage = $this->page;
3193                         // check if a new page has been created
3194                         if ($endpage > $startpage) {
3195                                 // design borders around HTML cells.
3196                                 for ($page=$startpage; $page <= $endpage; $page++) {
3197                                         $this->setPage($page);
3198                                         if ($page == $startpage) {
3199                                                 $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
3200                                                 $h = $restspace;
3201                                         } elseif ($page == $endpage) {
3202                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3203                                                 $h = $currentY - $this->tMargin;
3204                                         } else {
3205                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3206                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3207                                         }
3208                                         $this->SetX($x);
3209                                         $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3210                                         if ($border OR $fill) {
3211                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3212                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3213                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3214                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3215                                         }
3216                                 }
3217                         } else {
3218                                 $h = max($h, ($currentY - $y));
3219                                 // put cursor at the beginning of text
3220                                 $this->SetY($y);
3221                                 $this->SetX($x);
3222                                 $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3223                                 if ($border OR $fill) {
3224                                         // design a cell around the text
3225                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3226                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3227                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3228                                         $this->intmrk[$this->page] += strlen($ccode."\n");
3229                                 }
3230                         }
3231                         // Get end-of-cell Y position
3232                         $currentY = $this->GetY();
3233                         // restore original margin values
3234                         $this->SetLeftMargin($lMargin);
3235                         $this->SetRightMargin($rMargin);
3236                         if ($ln > 0) {
3237                                 //Go to the beginning of the next line
3238                                 $this->SetY($currentY);
3239                                 if ($ln == 2) {
3240                                         $this->SetX($x + $w);
3241                                 }
3242                         } else {
3243                                 // go left or right by case
3244                                 $this->setPage($startpage);
3245                                 $this->y = $y;
3246                                 $this->SetX($x + $w);
3247                         }
3248                         return $nl;
3249                 }
3250
3251                 /**
3252                 * This method prints text from the current position.<br />
3253                 * @param float $h Line height
3254                 * @param string $txt String to print
3255                 * @param mixed $link URL or identifier returned by AddLink()
3256                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3257                 * @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>
3258                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3259                 * @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>
3260                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3261                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3262                 * @since 1.5
3263                 */
3264                 function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false) {
3265                         // remove carriage returns
3266                         $s = str_replace("\r", '', $txt);
3267                         // check if string contains arabic text
3268                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3269                                 $arabic = true;
3270                         } else {
3271                                 $arabic = false;
3272                         }
3273                         // get array of chars
3274                         $chars = $this->UTF8StringToArray($s);
3275                         // get the number of characters
3276                         $nb = count($chars);
3277                         // handle single space character
3278                         if (($nb == 1) AND preg_match("/[\s]/u", $s)) {
3279                                 if ($this->rtl) {
3280                                         $this->x -= $this->GetStringWidth($s);
3281                                 } else {
3282                                         $this->x += $this->GetStringWidth($s);
3283                                 }
3284                                 return;
3285                         }
3286                         // store current position
3287                         $prevx = $this->x;
3288                         $prevy = $this->y;
3289                         // calculate remaining line width ($w)
3290                         if ($this->rtl) {
3291                                 $w = $this->x - $this->lMargin;
3292                         } else {
3293                                 $w = $this->w - $this->rMargin - $this->x;
3294                         }
3295                         // max column width
3296                         $wmax = $w - (2 * $this->cMargin);
3297                         $i = 0; // character position
3298                         $j = 0; // current starting position
3299                         $sep = -1; // position of the last blank space
3300                         $l = 0; // current string lenght
3301                         $nl = 0; //number of lines
3302                         $linebreak = false;
3303                         // for each character
3304                         while ($i < $nb) {
3305                                 //Get the current character
3306                                 $c = $chars[$i];
3307                                 if ($c == 10) { // 10 = "\n" = new line
3308                                         //Explicit line break
3309                                         if ($align == "J") {
3310                                                 if ($this->rtl) {
3311                                                         $talign = "R";
3312                                                 } else {
3313                                                         $talign = "L";
3314                                                 }
3315                                         } else {
3316                                                 $talign = $align;
3317                                         }
3318                                         if ($firstline) {
3319                                                 $startx = $this->x;
3320                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3321                                                 if ($this->rtl) {
3322                                                         $this->endlinex = $startx - $linew;
3323                                                 } else {
3324                                                         $this->endlinex = $startx + $linew;
3325                                                 }
3326                                                 $w = $linew;
3327                                                 $tmpcmargin = $this->cMargin;
3328                                                 $this->cMargin = 0;
3329                                         }
3330                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $talign, $fill, $link, $stretch);
3331                                         if ($firstline) {
3332                                                 $this->cMargin = $tmpcmargin;
3333                                                 return ($this->UTF8ArrSubString($chars, $i));
3334                                         }
3335                                         $nl++;
3336                                         $j = $i + 1;
3337                                         $l = 0;
3338                                         $sep = -1;
3339                                         $w = $this->getRemainingWidth();
3340                                         $wmax = $w - (2 * $this->cMargin);
3341                                 } else {
3342                                         if (preg_match("/[\s]/u", $this->unichr($c))) {
3343                                                 // update last blank space position
3344                                                 $sep = $i;
3345                                         }
3346                                         // update string length
3347                                         if (($this->isunicode) AND ($arabic)) {
3348                                                 // with bidirectional algorithm some chars may be changed affecting the line length
3349                                                 // *** very slow ***
3350                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), $this->tmprtl));
3351                                         } else {
3352                                                 $l += $this->GetCharWidth($c);
3353                                         }
3354                                         if ($l > $wmax) {
3355                                                 // we have reached the end of column
3356                                                 if ($sep == -1) {
3357                                                         // check if the line was already started
3358                                                         if (($this->rtl AND ($this->x < ($this->w - $this->rMargin)))
3359                                                                 OR ((!$this->rtl) AND ($this->x > $this->lMargin))) {
3360                                                                 // print a void cell and go to next line
3361                                                                 $this->Cell($w, $h, "", 0, 1);
3362                                                                 $linebreak = true;
3363                                                                 if ($firstline) {
3364                                                                         return ($this->UTF8ArrSubString($chars, $j));
3365                                                                 }
3366                                                         } else {
3367                                                                 // truncate the word because do not fit on column
3368                                                                 if ($firstline) {
3369                                                                         $startx = $this->x;
3370                                                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3371                                                                         if ($this->rtl) {
3372                                                                                 $this->endlinex = $startx - $linew;
3373                                                                         } else {
3374                                                                                 $this->endlinex = $startx + $linew;
3375                                                                         }
3376                                                                         $w = $linew;
3377                                                                         $tmpcmargin = $this->cMargin;
3378                                                                         $this->cMargin = 0;
3379                                                                 }
3380                                                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $align, $fill, $link, $stretch);
3381                                                                 if ($firstline) {
3382                                                                         $this->cMargin = $tmpcmargin;
3383                                                                         return ($this->UTF8ArrSubString($chars, $i));
3384                                                                 }
3385                                                                 $j = $i;
3386                                                                 $i--;
3387                                                         }
3388                                                 } else {
3389                                                         // word wrapping
3390                                                         if ($firstline) {
3391                                                                 $startx = $this->x;
3392                                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $sep), $this->tmprtl));
3393                                                                 if ($this->rtl) {
3394                                                                         $this->endlinex = $startx - $linew;
3395                                                                 } else {
3396                                                                         $this->endlinex = $startx + $linew;
3397                                                                 }
3398                                                                 $w = $linew;
3399                                                                 $tmpcmargin = $this->cMargin;
3400                                                                 $this->cMargin = 0;
3401                                                         }
3402                                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $sep), 0, 1, $align, $fill, $link, $stretch);
3403                                                         if ($firstline) {
3404                                                                 $this->cMargin = $tmpcmargin;
3405                                                                 return ($this->UTF8ArrSubString($chars, $sep));
3406                                                         }
3407                                                         $i = $sep;
3408                                                         $sep = -1;
3409                                                         $j = ($i+1);
3410                                                 }
3411                                                 $w = $this->getRemainingWidth();
3412                                                 $wmax = $w - (2 * $this->cMargin);
3413                                                 if ($linebreak) {
3414                                                         $linebreak = false;
3415                                                 } else {
3416                                                         $nl++;
3417                                                         $l = 0;
3418                                                 }
3419                                         }
3420                                 }
3421                                 $i++;
3422                         } // end while i < nb
3423                         // print last substring (if any)
3424                         if ($l > 0) {
3425                                 switch ($align) {
3426                                         case "J":
3427                                         case "C": {
3428                                                 $w = $w;
3429                                                 break;
3430                                         }
3431                                         case "L": {
3432                                                 if ($this->rtl) {
3433                                                         $w = $w;
3434                                                 } else {
3435                                                         $w = $l;
3436                                                 }
3437                                                 break;
3438                                         }
3439                                         case "R": {
3440                                                 if ($this->rtl) {
3441                                                         $w = $l;
3442                                                 } else {
3443                                                         $w = $w;
3444                                                 }
3445                                                 break;
3446                                         }
3447                                         default: {
3448                                                 $w = $l;
3449                                                 break;
3450                                         }
3451                                 }
3452                                 if ($firstline) {
3453                                         $startx = $this->x;
3454                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $nb), $this->tmprtl));
3455                                         if ($this->rtl) {
3456                                                 $this->endlinex = $startx - $linew;
3457                                         } else {
3458                                                 $this->endlinex = $startx + $linew;
3459                                         }
3460                                         $w = $linew;
3461                                         $tmpcmargin = $this->cMargin;
3462                                         $this->cMargin = 0;
3463                                 }
3464                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $nb), 0, $ln, $align, $fill, $link, $stretch);
3465                                 if ($firstline) {
3466                                         $this->cMargin = $tmpcmargin;
3467                                         return ($this->UTF8ArrSubString($chars, $nb));
3468                                 }
3469                                 $nl++;
3470                         }
3471                         if ($firstline) {
3472                                 return "";
3473                         }
3474                         return $nl;
3475                 }
3476
3477                 /**
3478                 * Returns the remaining width between the current position and margins.
3479                 * @return int Return the remaining width
3480                 * @access protected
3481                 */
3482                 function getRemainingWidth() {
3483                         if ($this->rtl) {
3484                                 return ($this->x - $this->lMargin);
3485                         } else {
3486                                 return ($this->w - $this->rMargin - $this->x);
3487                         }
3488                 }
3489
3490          /**
3491                 * Extract a slice of the $strarr array and return it as string.
3492                 * @param string $strarr The input array of characters.
3493                 * @param int $start the starting element of $strarr.
3494                 * @param int $end first element that will not be returned.
3495                 * @return Return part of a string
3496                 */
3497                 function UTF8ArrSubString($strarr, $start='', $end='') {
3498                         if (strlen($start) == 0) {
3499                                 $start = 0;
3500                         }
3501                         if (strlen($end) == 0) {
3502                                 $end = count($strarr);
3503                         }
3504                         $string = "";
3505                         for ($i=$start; $i < $end; $i++) {
3506                                 $string .= $this->unichr($strarr[$i]);
3507                         }
3508                         return $string;
3509                 }
3510
3511                 /**
3512                 * Returns the unicode caracter specified by UTF-8 code
3513                 * @param int $c UTF-8 code
3514                 * @return Returns the specified character.
3515                 * @author Miguel Perez, Nicola Asuni
3516                 * @since 2.3.000 (2008-03-05)
3517                 */
3518                 function unichr($c) {
3519                         if (!$this->isunicode) {
3520                                 return chr($c);
3521                         } elseif ($c <= 0x7F) {
3522                                 // one byte
3523                                 return chr($c);
3524                         } elseif ($c <= 0x7FF) {
3525                                 // two bytes
3526                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
3527                         } elseif ($c <= 0xFFFF) {
3528                                 // three bytes
3529                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3530                         } elseif ($c <= 0x10FFFF) {
3531                                 // four bytes
3532                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3533                         } else {
3534                                 return "";
3535                         }
3536                 }
3537
3538                 /**
3539                 * Puts an image in the page.
3540                 * The upper-left corner must be given.
3541                 * The dimensions can be specified in different ways:<ul>
3542                 * <li>explicit width and height (expressed in user unit)</li>
3543                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
3544                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
3545                 * 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;
3546                 * The format can be specified explicitly or inferred from the file extension.<br />
3547                 * It is possible to put a link on the image.<br />
3548                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
3549                 * @param string $file Name of the file containing the image.
3550                 * @param float $x Abscissa of the upper-left corner.
3551                 * @param float $y Ordinate of the upper-left corner.
3552                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
3553                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
3554                 * @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.
3555                 * @param mixed $link URL or identifier returned by AddLink().
3556                 * @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>
3557                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
3558                 * @param int $dpi dot-per-inch resolution used on resize
3559                 * @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>
3560                 * @since 1.1
3561                 * @see AddLink()
3562                 */
3563                 function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
3564                         // get image size
3565                         list($pixw, $pixh) = getimagesize($file);
3566                         // calculate image width and height on document
3567                         if (($w <= 0) AND ($h <= 0)) {
3568                                 // convert image size to document unit
3569                                 $w = $pixw / ($this->imgscale * $this->k);
3570                                 $h = $pixh / ($this->imgscale * $this->k);
3571                         } elseif ($w <= 0) {
3572                                 $w = $h * $pixw / $pixh;
3573                         } elseif ($h <= 0) {
3574                                 $h = $w * $pixh / $pixw;
3575                         }
3576                         // calculate new minimum dimensions in pixels
3577                         $neww = round($w * $this->k * $dpi / $this->dpi);
3578                         $newh = round($h * $this->k * $dpi / $this->dpi);
3579                         // check if resize is necessary (resize is used only to reduce the image)
3580                         if (($neww * $newh) >= ($pixw * $pixh)) {
3581                                 $resize = false;
3582                         }
3583                         // check if image has been already added on document
3584                         if (!isset($this->images[$file])) {
3585                                 //First use of image, get info
3586                                 if ($type == '') {
3587                                         $fileinfo = pathinfo($file);
3588                                         if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
3589                                                 $type = $fileinfo['extension'];
3590                                         } else {
3591                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
3592                                         }
3593                                 }
3594                                 $type = strtolower($type);
3595                                 if ($type == "jpg") {
3596                                         $type = "jpeg";
3597                                 }
3598                                 $mqr = get_magic_quotes_runtime();
3599                                 set_magic_quotes_runtime(0);
3600                                 // Specific image handlers
3601                                 $mtd = '_parse'.$type;
3602                                 // GD image handler function
3603                                 $gdfunction = "imagecreatefrom".$type;
3604                                 $info = false;
3605                                 if ((method_exists($this,$mtd)) AND (!($resize AND function_exists($gdfunction)))) {
3606                                         $info = $this->$mtd($file);
3607                                 }
3608                                 if (!$info) {
3609                                         if (function_exists($gdfunction)) {
3610                                                 $img = $gdfunction($file);
3611                                                 if ($resize) {
3612                                                         $imgr = imagecreatetruecolor($neww, $newh);
3613                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
3614                                                         $info = $this->_toJPEG($imgr);
3615                                                 } else {
3616                                                         $info = $this->_toJPEG($img);
3617                                                 }
3618                                         } else {
3619                                                 return;
3620                                         }
3621                                 }
3622                                 if ($info === false) {
3623                                         //If false, we cannot process image
3624                                         return;
3625                                 }
3626                                 set_magic_quotes_runtime($mqr);
3627                                 $info['i'] = count($this->images) + 1;
3628                                 // add image to document
3629                                 $this->images[$file] = $info;
3630                         } else {
3631                                 $info = $this->images[$file];
3632                         }
3633                         // Check whether we need a new page first as this does not fit
3634                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
3635                                 // Automatic page break
3636                                 $this->AddPage($this->CurOrientation);
3637                                 // Reset coordinates to top fo next page
3638                                 //$x = $this->GetX();
3639                                 $y = $this->GetY() + $this->cMargin;
3640                         }
3641                         // 2007-10-19 Warren Sherliker: End Edit
3642                         // set bottomcoordinates
3643                         $this->img_rb_y = $y + $h;
3644                         // set alignment
3645                         if ($this->rtl) {
3646                                 if ($palign == 'L') {
3647                                         $ximg = $this->lMargin;
3648                                         // set right side coordinate
3649                                         $this->img_rb_x = $ximg + $w;
3650                                 } elseif ($palign == 'C') {
3651                                         $ximg = ($this->w - $x - $w) / 2;
3652                                         // set right side coordinate
3653                                         $this->img_rb_x = $ximg + $w;
3654                                 } else {
3655                                         $ximg = $this->w - $x - $w;
3656                                         // set left side coordinate
3657                                         $this->img_rb_x = $ximg;
3658                                 }
3659                         } else {
3660                                 if ($palign == 'R') {
3661                                         $ximg = $this->w - $this->rMargin - $w;
3662                                         // set left side coordinate
3663                                         $this->img_rb_x = $ximg;
3664                                 } elseif ($palign == 'C') {
3665                                         $ximg = ($this->w - $x - $w) / 2;
3666                                         // set right side coordinate
3667                                         $this->img_rb_x = $ximg + $w;
3668                                 } else {
3669                                         $ximg = $x;
3670                                         // set right side coordinate
3671                                         $this->img_rb_x = $ximg + $w;
3672                                 }
3673                         }
3674                         $xkimg = $ximg * $this->k;
3675                         $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']));
3676                         if ($link) {
3677                                 $this->Link($ximg, $y, $w, $h, $link);
3678                         }
3679                         // set pointer to align the successive text/objects
3680                         switch($align) {
3681                                 case 'T': {
3682                                         $this->y = $y;
3683                                         $this->x = $this->img_rb_x;
3684                                         break;
3685                                 }
3686                                 case 'M': {
3687                                         $this->y = $y + round($h/2);
3688                                         $this->x = $this->img_rb_x;
3689                                         break;
3690                                 }
3691                                 case 'B': {
3692                                         $this->y = $this->img_rb_y;
3693                                         $this->x = $this->img_rb_x;
3694                                         break;
3695                                 }
3696                                 case 'N': {
3697                                         $this->SetY($this->img_rb_y);
3698                                         break;
3699                                 }
3700                                 default:{
3701                                         break;
3702                                 }
3703                         }
3704                         $this->endlinex = $this->img_rb_x;
3705                 }
3706
3707                 /**
3708                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
3709                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
3710                 * @param string $file Image file name.
3711                 * @param image $image Image object.
3712                 * return image JPEG image object.
3713                 * @access protected
3714                 */
3715                 function _toJPEG($image) {
3716                         $tempname = tempnam(K_PATH_CACHE,'jpg');
3717                         imagejpeg($image, $tempname, $this->jpeg_quality);
3718                         imagedestroy($image);
3719                         $retvars = $this->_parsejpeg($tempname);
3720                         // tidy up by removing temporary image
3721                         unlink($tempname);
3722                         return $retvars;
3723                 }
3724
3725                 /**
3726                 * Extract info from a JPEG file without using the GD library.
3727                 * @param string $file image file to parse
3728                 * @return array structure containing the image data
3729                 * @access protected
3730                 */
3731                 function _parsejpeg($file) {
3732                         $a = getimagesize($file);
3733                         if (empty($a)) {
3734                                 $this->Error('Missing or incorrect image file: '.$file);
3735                         }
3736                         if ($a[2] != 2) {
3737                                 $this->Error('Not a JPEG file: '.$file);
3738                         }
3739                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
3740                                 $colspace = 'DeviceRGB';
3741                         } elseif ($a['channels'] == 4) {
3742                                 $colspace = 'DeviceCMYK';
3743                         }       else {
3744                                 $colspace = 'DeviceGray';
3745                         }
3746                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
3747                         $data = file_get_contents($file);
3748                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
3749                 }
3750
3751                 /**
3752                 * Extract info from a PNG file without using the GD library.
3753                 * @param string $file image file to parse
3754                 * @return array structure containing the image data
3755                 * @access protected
3756                 */
3757                 function _parsepng($file) {
3758                         $f = fopen($file,'rb');
3759                         if (empty($f)) {
3760                                 $this->Error('Can\'t open image file: '.$file);
3761                         }
3762                         //Check signature
3763                         if (fread($f,8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3764                                 $this->Error('Not a PNG file: '.$file);
3765                         }
3766                         //Read header chunk
3767                         fread($f,4);
3768                         if (fread($f,4) != 'IHDR') {
3769                                 $this->Error('Incorrect PNG file: '.$file);
3770                         }
3771                         $w = $this->_freadint($f);
3772                         $h = $this->_freadint($f);
3773                         $bpc = ord(fread($f,1));
3774                         if ($bpc > 8) {
3775                                 //$this->Error('16-bit depth not supported: '.$file);
3776                                 return false;
3777                         }
3778                         $ct = ord(fread($f,1));
3779                         if ($ct == 0) {
3780                                 $colspace = 'DeviceGray';
3781                         } elseif ($ct == 2) {
3782                                 $colspace = 'DeviceRGB';
3783                         } elseif ($ct == 3) {
3784                                 $colspace = 'Indexed';
3785                         } else {
3786                                 //$this->Error('Alpha channel not supported: '.$file);
3787                                 return false;
3788                         }
3789                         if (ord(fread($f,1)) != 0) {
3790                                 //$this->Error('Unknown compression method: '.$file);
3791                                 return false;
3792                         }
3793                         if (ord(fread($f,1)) != 0) {
3794                                 //$this->Error('Unknown filter method: '.$file);
3795                                 return false;
3796                         }
3797                         if (ord(fread($f,1)) != 0) {
3798                                 //$this->Error('Interlacing not supported: '.$file);
3799                                 return false;
3800                         }
3801                         fread($f,4);
3802                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3803                         //Scan chunks looking for palette, transparency and image data
3804                         $pal = '';
3805                         $trns = '';
3806                         $data = '';
3807                         do {
3808                                 $n = $this->_freadint($f);
3809                                 $type = fread($f,4);
3810                                 if ($type == 'PLTE') {
3811                                         //Read palette
3812                                         $pal = fread($f,$n);
3813                                         fread($f,4);
3814                                 } elseif ($type == 'tRNS') {
3815                                         //Read transparency info
3816                                         $t = fread($f,$n);
3817                                         if ($ct == 0) {
3818                                                 $trns = array(ord(substr($t,1,1)));
3819                                         }
3820                                         elseif ($ct == 2) {
3821                                                 $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
3822                                         } else {
3823                                                 $pos = strpos($t,chr(0));
3824                                                 if ($pos !== false) {
3825                                                         $trns = array($pos);
3826                                                 }
3827                                         }
3828                                         fread($f, 4);
3829                                 } elseif ($type == 'IDAT') {
3830                                         //Read image data block
3831                                         $data .= fread($f,$n);
3832                                         fread($f, 4);
3833                                 } elseif ($type == 'IEND') {
3834                                         break;
3835                                 } else {
3836                                         fread($f, $n+4);
3837                                 }
3838                         }
3839                         while ($n);
3840                         if (($colspace == 'Indexed') AND (empty($pal))) {
3841                                 //$this->Error('Missing palette in '.$file);
3842                                 return false;
3843                         }
3844                         fclose($f);
3845                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
3846                 }
3847
3848                 /**
3849                 * Performs a line break.
3850                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
3851                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
3852                 * @param boolean $cell if true add a cMargin to the x coordinate
3853                 * @since 1.0
3854                 * @see Cell()
3855                 */
3856                 function Ln($h='', $cell=false) {
3857                         //Line feed; default value is last cell height
3858                         if ($cell) {
3859                                 $cellmargin = $this->cMargin;
3860                         } else {
3861                                 $cellmargin = 0;
3862                         }
3863                         if ($this->rtl) {
3864                                 $this->x = $this->w - $this->rMargin - $cellmargin;
3865                         } else {
3866                                 $this->x = $this->lMargin + $cellmargin;
3867                         }
3868                         if (is_string($h)) {
3869                                 $this->y += $this->lasth;
3870                         } else {
3871                                 $this->y += $h;
3872                         }
3873                         $this->newline = true;
3874                 }
3875
3876                 /**
3877                 * Returns the relative X value of current position.
3878                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
3879                 * @return float
3880                 * @since 1.2
3881                 * @see SetX(), GetY(), SetY()
3882                 */
3883                 function GetX() {
3884                         //Get x position
3885                         if ($this->rtl) {
3886                                 return ($this->w - $this->x);
3887                         } else {
3888                                 return $this->x;
3889                         }
3890                 }
3891
3892                 /**
3893                 * Returns the absolute X value of current position.
3894                 * @return float
3895                 * @since 1.2
3896                 * @see SetX(), GetY(), SetY()
3897                 */
3898                 function GetAbsX() {
3899                         return $this->x;
3900                 }
3901
3902                 /**
3903                 * Returns the ordinate of the current position.
3904                 * @return float
3905                 * @since 1.0
3906                 * @see SetY(), GetX(), SetX()
3907                 */
3908                 function GetY() {
3909                         //Get y position
3910                         return $this->y;
3911                 }
3912
3913                 /**
3914                 * Defines the abscissa of the current position.
3915                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
3916                 * @param float $x The value of the abscissa.
3917                 * @since 1.2
3918                 * @see GetX(), GetY(), SetY(), SetXY()
3919                 */
3920                 function SetX($x) {
3921                         //Set x position
3922                         if ($this->rtl) {
3923                                 if ($x >= 0) {
3924                                         $this->x = $this->w - $x;
3925                                 } else {
3926                                         $this->x = abs($x);
3927                                 }
3928                         } else {
3929                                 if ($x >= 0) {
3930                                         $this->x = $x;
3931                                 } else {
3932                                         $this->x = $this->w + $x;
3933                                 }
3934                         }
3935                 }
3936
3937                 /**
3938                 * Moves the current abscissa back to the left margin and sets the ordinate.
3939                 * If the passed value is negative, it is relative to the bottom of the page.
3940                 * @param float $y The value of the ordinate.
3941                 * @since 1.0
3942                 * @see GetX(), GetY(), SetY(), SetXY()
3943                 */
3944                 function SetY($y) {
3945                         //Set y position and reset x
3946                         if ($this->rtl) {
3947                                 $this->x = $this->w - $this->rMargin;
3948                         } else {
3949                                 $this->x = $this->lMargin;
3950                         }
3951                         if ($y >= 0) {
3952                                 $this->y = $y;
3953                         } else {
3954                                 $this->y = $this->h + $y;
3955                         }
3956                 }
3957
3958                 /**
3959                 * Defines the abscissa and ordinate of the current position.
3960                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
3961                 * @param float $x The value of the abscissa
3962                 * @param float $y The value of the ordinate
3963                 * @since 1.2
3964                 * @see SetX(), SetY()
3965                 */
3966                 function SetXY($x, $y) {
3967                         //Set x and y positions
3968                         $this->SetY($y);
3969                         $this->SetX($x);
3970                 }
3971
3972                 /**
3973                 * Send the document to a given destination: string, local file or browser.
3974                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
3975                 * The method first calls Close() if necessary to terminate the document.
3976                 * @param string $name The name of the file when saved.
3977                 * @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>
3978                 * @since 1.0
3979                 * @see Close()
3980                 */
3981                 function Output($name='doc.pdf', $dest='I') {
3982                         //Output PDF to some destination
3983                         //Finish document if necessary
3984                         if ($this->state < 3) {
3985                                 $this->Close();
3986                         }
3987                         //Normalize parameters
3988                         if (is_bool($dest)) {
3989                                 $dest = $dest ? 'D' : 'F';
3990                         }
3991                         $dest = strtoupper($dest);
3992                         if ($dest != 'F') {
3993                                 $name = str_replace("+", "%20", urlencode($name));
3994                                 $name = preg_replace('/[\r\n]+\s*/', '' , $name);
3995                         }
3996                         switch($dest) {
3997                                 case 'I': {
3998                                         //Send to standard output
3999                                         if (ob_get_contents()) {
4000                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4001                                         }
4002                                         if (php_sapi_name() != 'cli') {
4003                                                 //We send to a browser
4004                                                 header('Content-Type: application/pdf');
4005                                                 if (headers_sent()) {
4006                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
4007                                                 }
4008                                                 header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4009                                                 header("Pragma: public");
4010                                                 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4011                                                 header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");      
4012                                                 header('Content-Length: '.strlen($this->buffer));
4013                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
4014                                         }
4015                                         echo $this->buffer;
4016                                         break;
4017                                 }
4018                                 case 'D': {
4019                                         //Download file
4020                                         if (ob_get_contents()) {
4021                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4022                                         }
4023                                         header('Content-Description: File Transfer');
4024                                         if (headers_sent()) {
4025                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
4026                                         }
4027                                         header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4028                                         header("Pragma: public");
4029                                         header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4030                                         header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
4031                                         // force download dialog
4032                                         header("Content-Type: application/force-download");
4033                                         header("Content-Type: application/octet-stream", false);
4034                                         header("Content-Type: application/download", false);
4035                                         // use the Content-Disposition header to supply a recommended filename
4036                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
4037                                         header("Content-Transfer-Encoding: binary");
4038                                         header("Content-Length: ".strlen($this->buffer));
4039                                         echo $this->buffer;
4040                                         break;
4041                                 }
4042                                 case 'F': {
4043                                         //Save to local file
4044                                         $f = fopen($name, 'wb');
4045                                         if (!$f) {
4046                                                 $this->Error('Unable to create output file: '.$name);
4047                                         }
4048                                         fwrite($f, $this->buffer,strlen($this->buffer));
4049                                         fclose($f);
4050                                         break;
4051                                 }
4052                                 case 'S': {
4053                                         //Return as a string
4054                                         return $this->buffer;
4055                                 }
4056                                 default: {
4057                                         $this->Error('Incorrect output destination: '.$dest);
4058                                 }
4059                         }
4060                         return '';
4061                 }
4062
4063                 /**
4064                 * Check for locale-related bug
4065                 * @access protected
4066                 */
4067                 function _dochecks() {
4068                         //Check for locale-related bug
4069                         if (1.1 == 1) {
4070                                 $this->Error('Don\'t alter the locale before including class file');
4071                         }
4072                         //Check for decimal separator
4073                         if (sprintf('%.1f', 1.0) != '1.0') {
4074                                 setlocale(LC_NUMERIC, 'C');
4075                         }
4076                 }
4077
4078                 /**
4079                 * Return fonts path
4080                 * @return string
4081                 * @access protected
4082                 */
4083                 function _getfontpath() {
4084                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
4085                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
4086                         }
4087                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
4088                 }
4089
4090                 /**
4091                 * Output pages.
4092                 * @access protected
4093                 */
4094                 function _putpages() {
4095                         $nb = count($this->pages);
4096                         if (!empty($this->pagegroups)) {
4097                                 // do page number replacement
4098                                 foreach ($this->pagegroups as $k => $v) {
4099                                         $vu = $this->UTF8ToUTF16BE($v, false);
4100                                         $alias_a = $this->_escape($k);
4101                                         $alias_au = $this->_escape("{".$k."}");
4102                                         if ($this->isunicode) {
4103                                                 $alias_b = $this->_escape($this->UTF8ToLatin1($k));
4104                                                 $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$k."}"));
4105                                                 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
4106                                                 $alias_cu = $this->_escape($this->utf8StrRev("{".$k."}", false, $this->tmprtl));
4107                                         }
4108                                         for ($n = 1; $n <= $nb; $n++) {
4109                                                 $this->pages[$n] = str_replace($alias_au, $vu, $this->pages[$n]);
4110                                                 if ($this->isunicode) {
4111                                                         $this->pages[$n] = str_replace($alias_bu, $vu, $this->pages[$n]);
4112                                                         $this->pages[$n] = str_replace($alias_cu, $vu, $this->pages[$n]);
4113                                                         $this->pages[$n] = str_replace($alias_b, $v, $this->pages[$n]);
4114                                                         $this->pages[$n] = str_replace($alias_c, $v, $this->pages[$n]);
4115                                                 }
4116                                                 $this->pages[$n] = str_replace($alias_a, $v, $this->pages[$n]);
4117                                         }
4118                                 }
4119                         }
4120                         if (!empty($this->AliasNbPages)) {
4121                                 $nbu = $this->UTF8ToUTF16BE($nb, false); // replacement for unicode font
4122                                 $alias_a = $this->_escape($this->AliasNbPages);
4123                                 $alias_au = $this->_escape("{".$this->AliasNbPages."}");
4124                                 if ($this->isunicode) {
4125                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
4126                                         $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$this->AliasNbPages."}"));
4127                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
4128                                         $alias_cu = $this->_escape($this->utf8StrRev("{".$this->AliasNbPages."}", false, $this->tmprtl));
4129                                 }
4130                                 //Replace number of pages
4131                                 for($n = 1; $n <= $nb; $n++) {
4132                                         $this->pages[$n] = str_replace($alias_au, $nbu, $this->pages[$n]);
4133                                         if ($this->isunicode) {
4134                                                 $this->pages[$n] = str_replace($alias_bu, $nbu, $this->pages[$n]);
4135                                                 $this->pages[$n] = str_replace($alias_cu, $nbu, $this->pages[$n]);
4136                                                 $this->pages[$n] = str_replace($alias_b, $nb, $this->pages[$n]);
4137                                                 $this->pages[$n] = str_replace($alias_c, $nb, $this->pages[$n]);
4138                                         }
4139                                         $this->pages[$n] = str_replace($alias_a, $nb, $this->pages[$n]);
4140                                 }
4141                         }
4142                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4143                         for($n=1; $n <= $nb; $n++) {
4144                                 //Page
4145                                 $this->_newobj();
4146                                 $this->_out('<</Type /Page');
4147                                 $this->_out('/Parent 1 0 R');
4148                                 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
4149                                 $this->_out('/Resources 2 0 R');
4150                                 $this->_putannots($n);
4151                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
4152                                 $this->_out('endobj');
4153                                 //Page content
4154                                 $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
4155                                 $this->_newobj();
4156                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
4157                                 $this->_putstream($p);
4158                                 $this->_out('endobj');
4159                         }
4160                         //Pages root
4161                         $this->offsets[1] = strlen($this->buffer);
4162                         $this->_out('1 0 obj');
4163                         $this->_out('<</Type /Pages');
4164                         $kids='/Kids [';
4165                         for($i=0; $i < $nb; $i++) {
4166                                 $kids .= (3+2*$i).' 0 R ';
4167                         }
4168                         $this->_out($kids.']');
4169                         $this->_out('/Count '.$nb);
4170                         //$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
4171                         $this->_out('>>');
4172                         $this->_out('endobj');
4173                 }
4174
4175                 /**
4176                 * Output Page Annotations.
4177                 * See section 8.4 of PDF reference.
4178                 * @param int $n page number
4179                 * @access protected
4180                 * @author Nicola Asuni
4181                 * @since 4.0.018 (2008-08-06)
4182                 */
4183                 function _putannots($n) {
4184                         if (isset($this->PageAnnots[$n])) {
4185                                 $annots = '/Annots [';
4186                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
4187                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
4188                                         $a = $pl['x'] * $this->k;
4189                                         $b = $this->hPt - $pl['y'] * $this->k;
4190                                         $c = $pl['w'] * $this->k;
4191                                         $d = $pl['h'] * $this->k;
4192                                         $rect = sprintf('%.2f %.2f %.2f %.2f', $a, $b, $a+$c, $b-$d);
4193                                         $annots .= '<</Type /Annot';
4194                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
4195                                         $annots .= ' /Rect ['.$rect.']';
4196                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
4197                                         //$annots .= ' /P ';
4198                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
4199                                         $annots .= ' /M '.$this->_datestring('D:'.date('YmdHis'));
4200                                         if (isset($pl['opt']['f'])) {
4201                                                 $val = 0;
4202                                                 if (is_array($pl['opt']['f'])) {
4203                                                         foreach ($pl['opt']['f'] as $f) {
4204                                                                 switch (strtolower($f)) {
4205                                                                         case 'invisible': {
4206                                                                                 $val += 1 << 0;
4207                                                                                 break;
4208                                                                         }
4209                                                                         case 'hidden': {
4210                                                                                 $val += 1 << 1;
4211                                                                                 break;
4212                                                                         }
4213                                                                         case 'print': {
4214                                                                                 $val += 1 << 2;
4215                                                                                 break;
4216                                                                         }
4217                                                                         case 'nozoom': {
4218                                                                                 $val += 1 << 3;
4219                                                                                 break;
4220                                                                         }
4221                                                                         case 'norotate': {
4222                                                                                 $val += 1 << 4;
4223                                                                                 break;
4224                                                                         }
4225                                                                         case 'noview': {
4226                                                                                 $val += 1 << 5;
4227                                                                                 break;
4228                                                                         }
4229                                                                         case 'readonly': {
4230                                                                                 $val += 1 << 6;
4231                                                                                 break;
4232                                                                         }
4233                                                                         case 'locked': {
4234                                                                                 $val += 1 << 8;
4235                                                                                 break;
4236                                                                         }
4237                                                                         case 'togglenoview': {
4238                                                                                 $val += 1 << 9;
4239                                                                                 break;
4240                                                                         }
4241                                                                         case 'lockedcontents': {
4242                                                                                 $val += 1 << 10;
4243                                                                                 break;
4244                                                                         }
4245                                                                         default: {
4246                                                                                 break;
4247                                                                         }
4248                                                                 }
4249                                                         }
4250                                                 }
4251                                                 $annots .= ' /F '.intval($val);
4252                                         }
4253                                         //$annots .= ' /AP ';
4254                                         //$annots .= ' /AS ';
4255                                         $annots .= ' /Border [';
4256                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
4257                                                 $annots .= intval($pl['opt']['border'][0]).' ';
4258                                                 $annots .= intval($pl['opt']['border'][1]).' ';
4259                                                 $annots .= intval($pl['opt']['border'][2]);
4260                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
4261                                                         $annots .= ' [';
4262                                                         foreach ($pl['opt']['border'][3] as $dash) {
4263                                                                 $annots .= intval($dash).' ';
4264                                                         }
4265                                                         $annots .= ']';
4266                                                 }
4267                                         } else {
4268                                                 $annots .= '0 0 0';
4269                                         }
4270                                         $annots .= ']';
4271                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
4272                                                 $annots .= ' /BS <<Type /Border';
4273                                                 if (isset($pl['opt']['bs']['w'])) {
4274                                                         $annots .= ' /W '.sprintf("%.4f", floatval($pl['opt']['bs']['w']));
4275                                                 }
4276                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
4277                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $markups)) {
4278                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4279                                                 }
4280                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
4281                                                         $annots .= ' /D [';
4282                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
4283                                                                 $cord = floatval($cord);
4284                                                                 $annots .= sprintf(" %.4f", $cord);
4285                                                         }
4286                                                         $annots .= ']';
4287                                                 }
4288                                                 $annots .= '>>';
4289                                         }
4290                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
4291                                                 $annots .= ' /BE <<';
4292                                                 $bstyles = array('S', 'C');
4293                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
4294                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4295                                                 } else {
4296                                                         $annots .= ' /S /S';
4297                                                 }
4298                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
4299                                                         $annots .= ' /I '.sprintf(" %.4f", $pl['opt']['be']['i']);
4300                                                 }
4301                                                 $annots .= '>>';
4302                                         }
4303                                         $annots .= ' /C [';
4304                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
4305                                                 foreach ($pl['opt']['c'] as $col) {
4306                                                         $col = intval($col);
4307                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
4308                                                         $annots .= sprintf(" %.4f", $color);
4309                                                 }
4310                                         }
4311                                         $annots .= ']';
4312                                         //$annots .= ' /StructParent ';
4313                                         //$annots .= ' /OC ';
4314                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
4315                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
4316                                                 // this is a markup type
4317                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
4318                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
4319                                                 }
4320                                                 //$annots .= ' /Popup ';
4321                                                 if (isset($pl['opt']['ca'])) {
4322                                                         $annots .= ' /CA '.sprintf("%.4f", floatval($pl['opt']['ca']));
4323                                                 }
4324                                                 if (isset($pl['opt']['rc'])) {
4325                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4326                                                 }
4327                                                 $annots .= ' /CreationDate '.$this->_datestring('D:'.date('YmdHis'));
4328                                                 //$annots .= ' /IRT ';
4329                                                 if (isset($pl['opt']['subj'])) {
4330                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
4331                                                 }
4332                                                 //$annots .= ' /RT ';
4333                                                 //$annots .= ' /IT ';
4334                                                 //$annots .= ' /ExData ';
4335                                         }
4336                                         switch (strtolower($pl['opt']['subtype'])) {
4337                                                 case 'text': {
4338                                                         if (isset($pl['opt']['open'])) {
4339                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
4340                                                         }
4341                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
4342                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
4343                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
4344                                                         } else {
4345                                                                 $annots .= ' /Name /Note';
4346                                                         }
4347                                                         $statemodels = array('Marked', 'Review');
4348                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
4349                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4350                                                         } else {
4351                                                                 $pl['opt']['statemodel'] = 'Marked';
4352                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4353                                                         }
4354                                                         if ($pl['opt']['statemodel'] == 'Marked') {
4355                                                                 $states = array('Accepted', 'Unmarked');
4356                                                         } else {
4357                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
4358                                                         }
4359                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
4360                                                                 $annots .= ' /State /'.$pl['opt']['state'];
4361                                                         } else {
4362                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
4363                                                                         $annots .= ' /State /Unmarked';
4364                                                                 } else {
4365                                                                         $annots .= ' /State /None';
4366                                                                 }
4367                                                         }
4368                                                         break;
4369                                                 }
4370                                                 case 'link': {
4371                                                         $annots .= ' /A <</S /URI /URI '.$this->_uristring($pl['txt']).'>>';
4372                                                         $hmodes = array('N', 'I', 'O', 'P');
4373                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
4374                                                                 $annots .= ' /H /'.$pl['opt']['h'];
4375                                                         } else {
4376                                                                 $annots .= ' /H /I';
4377                                                         }
4378                                                         //$annots .= ' /Dest ';
4379                                                         //$annots .= ' /PA ';
4380                                                         //$annots .= ' /Quadpoints ';
4381                                                         break;
4382                                                 }
4383                                                 case 'freetext': {
4384                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
4385                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
4386                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
4387                                                         }
4388                                                         if (isset($pl['opt']['rc'])) {
4389                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4390                                                         }
4391                                                         if (isset($pl['opt']['ds'])) {
4392                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
4393                                                         }
4394                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
4395                                                                 $annots .= ' /CL [';
4396                                                                 foreach ($pl['opt']['cl'] as $cl) {
4397                                                                         $annots .= sprintf("%.4f ", $cl * $this->k);
4398                                                                 }
4399                                                                 $annots .= ']';
4400                                                         }
4401                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
4402                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
4403                                                                 $annots .= ' /IT '.$pl['opt']['it'];
4404                                                         }
4405                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
4406                                                                 $l = $pl['opt']['rd'][0] * $this->k;
4407                                                                 $r = $pl['opt']['rd'][1] * $this->k;
4408                                                                 $t = $pl['opt']['rd'][2] * $this->k;
4409                                                                 $b = $pl['opt']['rd'][3] * $this->k;
4410                                                                 $annots .= ' /RD ['.sprintf('%.2f %.2f %.2f %.2f', $l, $r, $t, $b).']';
4411                                                         }
4412                                                         //$annots .= ' /LE ';
4413                                                         break;
4414                                                 }
4415                                                 // ... to be completed ...
4416                                                 case 'line': {
4417                                                         break;
4418                                                 }
4419                                                 case 'square': {
4420                                                         break;
4421                                                 }
4422                                                 case 'circle': {
4423                                                         break;
4424                                                 }
4425                                                 case 'polygon': {
4426                                                         break;
4427                                                 }
4428                                                 case 'polyline': {
4429                                                         break;
4430                                                 }
4431                                                 case 'highlight': {
4432                                                         break;
4433                                                 }
4434                                                 case 'underline': {
4435                                                         break;
4436                                                 }
4437                                                 case 'squiggly': {
4438                                                         break;
4439                                                 }
4440                                                 case 'strikeout': {
4441                                                         break;
4442                                                 }
4443                                                 case 'stamp': {
4444                                                         break;
4445                                                 }
4446                                                 case 'caret': {
4447                                                         break;
4448                                                 }
4449                                                 case 'ink': {
4450                                                         break;
4451                                                 }
4452                                                 case 'popup': {
4453                                                         break;
4454                                                 }
4455                                                 case 'fileattachment': {
4456                                                         break;
4457                                                 }
4458                                                 case 'sound': {
4459                                                         break;
4460                                                 }
4461                                                 case 'movie': {
4462                                                         break;
4463                                                 }
4464                                                 case 'widget': {
4465                                                         break;
4466                                                 }
4467                                                 case 'screen': {
4468                                                         break;
4469                                                 }
4470                                                 case 'printermark': {
4471                                                         break;
4472                                                 }
4473                                                 case 'trapnet': {
4474                                                         break;
4475                                                 }
4476                                                 case 'watermark': {
4477                                                         break;
4478                                                 }
4479                                                 case '3d': {
4480                                                         break;
4481                                                 }
4482                                                 default: {
4483                                                         break;
4484                                                 }
4485                                         }
4486                                         
4487                                 $annots .= '>>';
4488                                 }
4489                                 $this->_out($annots.']');
4490                         }
4491                 }
4492
4493                 /**
4494                 * Output fonts.
4495                 * _putfonts
4496                 * @access protected
4497                 */
4498                 function _putfonts() {
4499                         $nf = $this->n;
4500                         foreach($this->diffs as $diff) {
4501                                 //Encodings
4502                                 $this->_newobj();
4503                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
4504                                 $this->_out('endobj');
4505                         }
4506                         $mqr = get_magic_quotes_runtime();
4507                         set_magic_quotes_runtime(0);
4508                         foreach($this->FontFiles as $file => $info) {
4509                                 //Font file embedding
4510                                 $this->_newobj();
4511                                 $this->FontFiles[$file]['n'] = $this->n;
4512                                 $font = file_get_contents($this->_getfontpath().strtolower($file));
4513                                 $compressed = (substr($file,-2)=='.z');
4514                                 if ((!$compressed) AND (isset($info['length2']))) {
4515                                         $header = (ord($font{0}) == 128);
4516                                         if ($header) {
4517                                                 //Strip first binary header
4518                                                 $font = substr($font,6);
4519                                         }
4520                                         if ($header AND (ord($font{$info['length1']}) == 128)) {
4521                                                 //Strip second binary header
4522                                                 $font = substr($font, 0, $info['length1']).substr($font, $info['length1']+6);
4523                                         }
4524                                 }
4525                                 $this->_out('<</Length '.strlen($font));
4526                                 if ($compressed) {
4527                                         $this->_out('/Filter /FlateDecode');
4528                                 }
4529                                 $this->_out('/Length1 '.$info['length1']);
4530                                 if (isset($info['length2'])) {
4531                                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
4532                                 }
4533                                 $this->_out('>>');
4534                                 $this->_putstream($font);
4535                                 $this->_out('endobj');
4536                         }
4537                         set_magic_quotes_runtime($mqr);
4538                         foreach($this->fonts as $k => $font) {
4539                                 //Font objects
4540                                 $this->fonts[$k]['n'] = $this->n + 1;
4541                                 $type = $font['type'];
4542                                 $name = $font['name'];
4543                                 if ($type == 'core') {
4544                                         //Standard font
4545                                         $this->_newobj();
4546                                         $this->_out('<</Type /Font');
4547                                         $this->_out('/BaseFont /'.$name);
4548                                         $this->_out('/Subtype /Type1');
4549                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
4550                                                 $this->_out('/Encoding /WinAnsiEncoding');
4551                                         }
4552                                         $this->_out('>>');
4553                                         $this->_out('endobj');
4554                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
4555                                         //Additional Type1 or TrueType font
4556                                         $this->_newobj();
4557                                         $this->_out('<</Type /Font');
4558                                         $this->_out('/BaseFont /'.$name);
4559                                         $this->_out('/Subtype /'.$type);
4560                                         $this->_out('/FirstChar 32 /LastChar 255');
4561                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
4562                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
4563                                         if ($font['enc']) {
4564                                                 if (isset($font['diff'])) {
4565                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
4566                                                 } else {
4567                                                         $this->_out('/Encoding /WinAnsiEncoding');
4568                                                 }
4569                                         }
4570                                         $this->_out('>>');
4571                                         $this->_out('endobj');
4572                                         //Widths
4573                                         $this->_newobj();
4574                                         $cw = &$font['cw'];
4575                                         $s = '[';
4576                                         for($i=32; $i <= 255; $i++) {
4577                                                 //$s .= $cw[chr($i)].' ';
4578                                                 $s .= $cw[$i].' ';
4579                                         }
4580                                         $this->_out($s.']');
4581                                         $this->_out('endobj');
4582                                         //Descriptor
4583                                         $this->_newobj();
4584                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4585                                         foreach($font['desc'] as $k => $v) {
4586                                                 $s .= ' /'.$k.' '.$v;
4587                                         }
4588                                         $file = $font['file'];
4589                                         if ($file) {
4590                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
4591                                         }
4592                                         $this->_out($s.'>>');
4593                                         $this->_out('endobj');
4594                                 } else {
4595                                         //Allow for additional types
4596                                         $mtd = '_put'.strtolower($type);
4597                                         if (!method_exists($this, $mtd)) {
4598                                                 $this->Error('Unsupported font type: '.$type);
4599                                         }
4600                                         $this->$mtd($font);
4601                                 }
4602                         }
4603                 }
4604
4605                 /**
4606                  * Output CID-0 fonts.
4607                  * @param array $font font data
4608                  * @access protected
4609                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
4610                  * @since 3.2.000 (2008-06-23)
4611                  */
4612                 function _putcidfont0($font) {
4613                         if (isset($font['cidinfo']['uni2cid'])) {
4614                                 // convert unicode to cid.
4615                                 $uni2cid = $font['cidinfo']['uni2cid'];
4616                                 $cw = array();
4617                                 foreach ($font['cw'] as $uni => $width) {
4618                                         if (isset($uni2cid[$uni])) {
4619                                                 $cw[($uni2cid[$uni] + 31)] = $width;
4620                                         } elseif ($uni <= 255) {
4621                                                 $cw[$uni] = $width;
4622                                         } // else unknown character
4623                                 }
4624                                 ksort($cw);
4625                                 $font = array_merge($font, array('cw'=>$cw));
4626                         }
4627                         $longname = $name = $font['name'];
4628                         $enc = $font['enc'];
4629                         if ($enc) {
4630                                 $longname .= "-$enc";
4631                         }
4632                         $this->_newobj();
4633                         $this->_out('<</Type /Font');
4634                         $this->_out('/BaseFont /'.$longname);
4635                         $this->_out('/Subtype /Type0');
4636                         if ($enc) {
4637                                 $this->_out('/Encoding /'.$enc);
4638                         }
4639                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4640                         $this->_out('>>');
4641                         $this->_out('endobj');
4642                         $this->_newobj();
4643                         $this->_out('<</Type /Font');
4644                         $this->_out('/BaseFont /'.$name);
4645                         $this->_out('/Subtype /CIDFontType0');
4646                         $cidinfo = '/Registry ('.$font['cidinfo']['Registry'].') ';
4647                         $cidinfo .= '/Ordering ('.$font['cidinfo']['Ordering'].') ';
4648                         $cidinfo .= '/Supplement '.$font['cidinfo']['Supplement'];
4649                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
4650                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
4651                         $codes = array_keys($font['cw']);
4652                         $first = current($codes);
4653                         $last = end($codes);
4654                         $this->_out('/DW '.$font['dw']);
4655                         $w = '/W [';
4656                         $ranges = array();
4657                         $currange = 0;
4658                         for($i = $first; $i <= $last; $i++) {
4659                                 if (isset($font['cw'][$i]) AND (!$currange)) {
4660                                         $currange = $i - 31;
4661                                 } elseif (!isset($font['cw'][$i])) {
4662                                         $currange = 0;
4663                                 }
4664                                 if ($currange) {
4665                                         $ranges[$currange][] = $font['cw'][$i];
4666                                 }
4667                         }
4668                         foreach($ranges as $k => $ws) {
4669                                 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
4670                         }
4671                         $w .= ' ]';
4672                         $this->_out($w);
4673                         $this->_out('>>');
4674                         $this->_out('endobj');
4675                         $this->_newobj();
4676                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4677                         foreach($font['desc'] as $k => $v) {
4678                                 $s .= ' /'.$k.' '.$v;
4679                         }
4680                         $this->_out($s.'>>');
4681                         $this->_out('endobj');
4682                 }
4683
4684                 /**
4685                  * Output images.
4686                  * @access protected
4687                  */
4688                 function _putimages() {
4689                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4690                         reset($this->images);
4691                         while (list($file, $info) = each($this->images)) {
4692                                 $this->_newobj();
4693                                 $this->images[$file]['n'] = $this->n;
4694                                 $this->_out('<</Type /XObject');
4695                                 $this->_out('/Subtype /Image');
4696                                 $this->_out('/Width '.$info['w']);
4697                                 $this->_out('/Height '.$info['h']);
4698                                 if (isset($info["masked"])) {
4699                                         $this->_out('/SMask '.($this->n-1).' 0 R');
4700                                 }
4701                                 if ($info['cs'] == 'Indexed') {
4702                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]');
4703                                 } else {
4704                                         $this->_out('/ColorSpace /'.$info['cs']);
4705                                         if ($info['cs'] == 'DeviceCMYK') {
4706                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
4707                                         }
4708                                 }
4709                                 $this->_out('/BitsPerComponent '.$info['bpc']);
4710                                 if (isset($info['f'])) {
4711                                         $this->_out('/Filter /'.$info['f']);
4712                                 }
4713                                 if (isset($info['parms'])) {
4714                                         $this->_out($info['parms']);
4715                                 }
4716                                 if (isset($info['trns']) and is_array($info['trns'])) {
4717                                         $trns='';
4718                                         for($i=0; $i < count($info['trns']); $i++) {
4719                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
4720                                         }
4721                                         $this->_out('/Mask ['.$trns.']');
4722                                 }
4723                                 $this->_out('/Length '.strlen($info['data']).'>>');
4724                                 $this->_putstream($info['data']);
4725                                 unset($this->images[$file]['data']);
4726                                 $this->_out('endobj');
4727                                 //Palette
4728                                 if ($info['cs'] == 'Indexed') {
4729                                         $this->_newobj();
4730                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
4731                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
4732                                         $this->_putstream($pal);
4733                                         $this->_out('endobj');
4734                                 }
4735                         }
4736                 }
4737
4738                 /**
4739                 * Output Spot Colors Resources.
4740                 * @access protected
4741                 * @since 4.0.024 (2008-09-12)
4742                 */
4743                 function _putspotcolors() {
4744                         foreach ($this->spot_colors as $name => $color) {
4745                                 $this->_newobj();
4746                                 $this->spot_colors[$name]['n'] = $this->n;
4747                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
4748                                 $this->_out('/DeviceCMYK <<');
4749                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
4750                                 $this->_out(sprintf('/C1 [%.4f %.4f %.4f %.4f] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
4751                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
4752                                 $this->_out('endobj');
4753                         }
4754                 }
4755
4756                 /**
4757                 * Output object dictionary for images.
4758                 * @access protected
4759                 */
4760                 function _putxobjectdict() {
4761                         foreach($this->images as $image) {
4762                                 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
4763                         }
4764                 }
4765
4766                 /**
4767                 * Output Resources Dictionary.
4768                 * @access protected
4769                 */
4770                 function _putresourcedict(){
4771                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
4772                         $this->_out('/Font <<');
4773                         foreach($this->fonts as $font) {
4774                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
4775                         }
4776                         $this->_out('>>');
4777                         $this->_out('/XObject <<');
4778                         $this->_putxobjectdict();
4779                         $this->_out('>>');
4780                         // visibility
4781                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
4782                         // transparency
4783                         $this->_out('/ExtGState <<');
4784                         foreach($this->extgstates as $k => $extgstate) {
4785                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
4786                         }
4787                         $this->_out('>>');
4788                         // gradients
4789                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
4790                                 $this->_out('/Shading <<');
4791                                 foreach($this->gradients as $id => $grad) {
4792                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
4793                                 }
4794                                 $this->_out('>>');
4795                         }
4796                         // spot colors
4797                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
4798                                 $this->_out('/ColorSpace <<');
4799                                 foreach ($this->spot_colors as $color) {
4800                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
4801                                 }
4802                                 $this->_out('>>');
4803                         }
4804                 }
4805
4806                 /**
4807                 * Output Resources.
4808                 * @access protected
4809                 */
4810                 function _putresources() {
4811                         $this->_putextgstates();
4812                         $this->_putocg();
4813                         $this->_putfonts();
4814                         $this->_putimages();
4815                         $this->_putspotcolors();
4816                         $this->_putshaders();
4817                         //Resource dictionary
4818                         $this->offsets[2] = strlen($this->buffer);
4819                         $this->_out('2 0 obj');
4820                         $this->_out('<<');
4821                         $this->_putresourcedict();
4822                         $this->_out('>>');
4823                         $this->_out('endobj');
4824                         $this->_putjavascript();
4825                         $this->_putbookmarks();
4826                         // encryption
4827                         if ($this->encrypted) {
4828                                 $this->_newobj();
4829                                 $this->enc_obj_id = $this->n;
4830                                 $this->_out('<<');
4831                                 $this->_putencryption();
4832                                 $this->_out('>>');
4833                                 $this->_out('endobj');
4834                         }
4835                 }
4836
4837                 /**
4838                 * Adds some Metadata information
4839                 * (see Chapter 10.2 of PDF Reference)
4840                 * @access protected
4841                 */
4842                 function _putinfo() {
4843                         if (!empty($this->title)) {
4844                                 $this->_out('/Title '.$this->_textstring($this->title));
4845                         }
4846                         if (!empty($this->author)) {
4847                                 $this->_out('/Author '.$this->_textstring($this->author));
4848                         }
4849                         if (!empty($this->subject)) {
4850                                 $this->_out('/Subject '.$this->_textstring($this->subject));
4851                         }
4852                         if (!empty($this->keywords)) {
4853                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
4854                         }
4855                         if (!empty($this->creator)) {
4856                                 $this->_out('/Creator '.$this->_textstring($this->creator));
4857                         }
4858                         if (defined('PDF_PRODUCER')) {
4859                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
4860                         }
4861                         $this->_out('/CreationDate '.$this->_datestring('D:'.date('YmdHis')));
4862                         $this->_out('/ModDate '.$this->_datestring('D:'.date('YmdHis')));
4863                 }
4864
4865                 /**
4866                 * Format a date string for meta information
4867                 * @param string $s date string to escape.
4868                 * @return string escaped string.
4869                 * @access protected
4870                 */
4871                 function _datestring($s) {
4872                         if ($this->encrypted) {
4873                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4874                         }
4875                         return '('. $this->_escape($s).')';
4876                 }
4877
4878                 /**
4879                 * Output Catalog.
4880                 * @access protected
4881                 */
4882                 function _putcatalog() {
4883                         $this->_out('/Type /Catalog');
4884                         $this->_out('/Pages 1 0 R');
4885
4886                         if ($this->ZoomMode == 'fullpage') {
4887                                 $this->_out('/OpenAction [3 0 R /Fit]');
4888                         } elseif ($this->ZoomMode == 'fullwidth') {
4889                                 $this->_out('/OpenAction [3 0 R /FitH null]');
4890                         } elseif ($this->ZoomMode == 'real') {
4891                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
4892                         } elseif (!is_string($this->ZoomMode)) {
4893                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
4894                         }
4895                         if (isset($this->LayoutMode) AND (!empty($this->LayoutMode))) {
4896                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
4897                         }
4898                         if (isset($this->PageMode) AND (!empty($this->PageMode))) {
4899                                 $this->_out('/PageMode /'.$this->PageMode);
4900                         }
4901                         if (isset($this->l['a_meta_language'])) {
4902                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
4903                         }
4904                         if (!empty($this->javascript)) {
4905                                 $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
4906                         }
4907                         if (count($this->outlines) > 0) {
4908                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
4909                                 $this->_out('/PageMode /UseOutlines');
4910                         }
4911                         $this->_putviewerpreferences();
4912                         $p = $this->n_ocg_print.' 0 R';
4913                         $v = $this->n_ocg_view.' 0 R';
4914                         $as = "<</Event /Print /OCGs [".$p." ".$v."] /Category [/Print]>> <</Event /View /OCGs [".$p." ".$v."] /Category [/View]>>";
4915                         $this->_out("/OCProperties <</OCGs [".$p." ".$v."] /D <</ON [".$p."] /OFF [".$v."] /AS [".$as."]>>>>");
4916                         $this->_putuserrights();
4917                 }
4918
4919                 /**
4920                 * Output viewer preferences.
4921                 * @author Nicola asuni
4922                 * @since 3.1.000 (2008-06-09)
4923                 * @access protected
4924                 */
4925                 function _putviewerpreferences() {
4926                         $this->_out('/ViewerPreferences<<');
4927                         if ($this->rtl) {
4928                                 $this->_out('/Direction /R2L');
4929                         } else {
4930                                 $this->_out('/Direction /L2R');
4931                         }
4932                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
4933                                 $this->_out('/HideToolbar true');
4934                         }
4935                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
4936                                 $this->_out('/HideMenubar true');
4937                         }
4938                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
4939                                 $this->_out('/HideWindowUI true');
4940                         }
4941                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
4942                                 $this->_out('/FitWindow true');
4943                         }
4944                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
4945                                 $this->_out('/CenterWindow true');
4946                         }
4947                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
4948                                 $this->_out('/DisplayDocTitle true');
4949                         }
4950                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
4951                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
4952                         }
4953                         if (isset($this->viewer_preferences['ViewArea'])) {
4954                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
4955                         }
4956                         if (isset($this->viewer_preferences['ViewClip'])) {
4957                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
4958                         }
4959                         if (isset($this->viewer_preferences['PrintArea'])) {
4960                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
4961                         }
4962                         if (isset($this->viewer_preferences['PrintClip'])) {
4963                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
4964                         }
4965                         if (isset($this->viewer_preferences['PrintScaling'])) {
4966                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
4967                         }
4968                         if (isset($this->viewer_preferences['Duplex']) AND (!empty($this->viewer_preferences['Duplex']))) {
4969                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
4970                         }
4971                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
4972                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
4973                                         $this->_out('/PickTrayByPDFSize true');
4974                                 } else {
4975                                         $this->_out('/PickTrayByPDFSize false');
4976                                 }
4977                         }
4978                         if (isset($this->viewer_preferences['PrintPageRange'])) {
4979                                 $PrintPageRangeNum = "";
4980                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
4981                                         $PrintPageRangeNum .= " ".($v-1)."";
4982                                 }
4983                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
4984                         }
4985                         if (isset($this->viewer_preferences['NumCopies'])) {
4986                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
4987                         }
4988                         $this->_out('>>');
4989                 }
4990
4991                 /**
4992                 * Output trailer.
4993                 * @access protected
4994                 */
4995                 function _puttrailer() {
4996                         $this->_out('/Size '.($this->n + 1));
4997                         $this->_out('/Root '.$this->n.' 0 R');
4998                         $this->_out('/Info '.($this->n - 1).' 0 R');
4999                         if ($this->encrypted) {
5000                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
5001                                 $this->_out('/ID [()()]');
5002                         }
5003                 }
5004
5005                 /**
5006                 * Output PDF header.
5007                 * @access protected
5008                 */
5009                 function _putheader() {
5010                         $this->buffer = '%PDF-'.$this->PDFVersion."\n".$this->buffer;
5011 //                      $this->_out('%PDF-'.$this->PDFVersion);
5012                 }
5013
5014                 /**
5015                 * Output end of document (EOF).
5016                 * @access protected
5017                 */
5018                 function _enddoc() {
5019                         $this->_putheader();
5020                         $this->_putpages();
5021                         $this->_putresources();
5022                         //Info
5023                         $this->_newobj();
5024                         $this->_out('<<');
5025                         $this->_putinfo();
5026                         $this->_out('>>');
5027                         $this->_out('endobj');
5028                         //Catalog
5029                         $this->_newobj();
5030                         $this->_out('<<');
5031                         $this->_putcatalog();
5032                         $this->_out('>>');
5033                         $this->_out('endobj');
5034                         //Cross-ref
5035                         $o = strlen($this->buffer);
5036                         $this->_out('xref');
5037                         $this->_out('0 '.($this->n + 1));
5038                         $this->_out('0000000000 65535 f ');
5039                         for($i=1; $i <= $this->n; $i++) {
5040                                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
5041                         }
5042                         //Trailer
5043                         $this->_out('trailer');
5044                         $this->_out('<<');
5045                         $this->_puttrailer();
5046                         $this->_out('>>');
5047                         $this->_out('startxref');
5048                         $this->_out($o);
5049                         $this->_out('%%EOF');
5050                         $this->state = 3;
5051                 }
5052
5053                 /**
5054                 * Initialize a new page.
5055                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
5056                 * @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>
5057                 * @access protected
5058                 */
5059                 function _beginpage($orientation='', $format='') {
5060                         $this->page++;
5061                         $this->pages[$this->page] = ""; // this mark should be removed before output
5062                         $this->state = 2;
5063                         if (empty($orientation)) {
5064                                 if (isset($this->CurOrientation)) {
5065                                         $orientation = $this->CurOrientation;
5066                                 } else {
5067                                         $orientation = 'P';
5068                                 }
5069                         }
5070                         if (!empty($format)) {
5071                                 $this->setPageFormat($format, $orientation);
5072                         } else {
5073                                 $this->setPageOrientation($orientation);
5074                         }
5075                         if ($this->rtl) {
5076                                 $this->x = $this->w - $this->rMargin;
5077                         } else {
5078                                 $this->x = $this->lMargin;
5079                         }
5080                         $this->y = $this->tMargin;
5081                         if ($this->newpagegroup){
5082                                 // start a new group
5083                                 $n = sizeof($this->pagegroups) + 1;
5084                                 $alias = "{nb".$n."}";
5085                                 $this->pagegroups[$alias] = 1;
5086                                 $this->currpagegroup = $alias;
5087                                 $this->newpagegroup = false;
5088                         } elseif ($this->currpagegroup) {
5089                                 $this->pagegroups[$this->currpagegroup]++;
5090                         }
5091                 }
5092
5093                 /**
5094                 * Mark end of page.
5095                 * @access protected
5096                 */
5097                 function _endpage() {
5098                         $this->setVisibility("all");
5099                         $this->state = 1;
5100                 }
5101
5102                 /**
5103                 * Begin a new object.
5104                 * @access protected
5105                 */
5106                 function _newobj() {
5107                         $this->n++;
5108                         $this->offsets[$this->n] = strlen($this->buffer);
5109                         $this->_out($this->n.' 0 obj');
5110                 }
5111
5112                 /**
5113                 * Underline text.
5114                 * @param int $x X coordinate
5115                 * @param int $y Y coordinate
5116                 * @param string $txt text to underline
5117                 * @access protected
5118                 */
5119                 function _dounderline($x, $y, $txt) {
5120                         $up = $this->CurrentFont['up'];
5121                         $ut = $this->CurrentFont['ut'];
5122                         $w = $this->GetStringWidth($txt);
5123                         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);
5124                 }
5125
5126                 /**
5127                 * Line through text.
5128                 * @param int $x X coordinate
5129                 * @param int $y Y coordinate
5130                 * @param string $txt text to underline
5131                 * @access protected
5132                 */
5133                 function _dolinethrough($x, $y, $txt) {
5134                         $up = $this->CurrentFont['up'];
5135                         $ut = $this->CurrentFont['ut'];
5136                         $w = $this->GetStringWidth($txt);
5137                         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);
5138                 }
5139
5140                 /**
5141                 * Read a 4-byte integer from file.
5142                 * @param string $f file name.
5143                 * @return 4-byte integer
5144                 * @access protected
5145                 */
5146                 function _freadint($f) {
5147                         $a = unpack('Ni', fread($f,4));
5148                         return $a['i'];
5149                 }
5150
5151                 /**
5152                 * Format a text string for meta information
5153                 * @param string $s string to escape.
5154                 * @return string escaped string.
5155                 * @access protected
5156                 */
5157                 function _textstring($s) {
5158                         if ($this->isunicode) {
5159                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5160                                         $s = $this->UTF8ToLatin1($s);
5161                                 } else {
5162                                         //Convert string to UTF-16BE
5163                                         $s = $this->UTF8ToUTF16BE($s, true);
5164                                 }
5165                         }
5166                         if ($this->encrypted) {
5167                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5168                         }
5169                         return '('. $this->_escape($s).')';
5170                 }
5171
5172                 /**
5173                 * Format an URI string
5174                 * @param string $s string to escape.
5175                 * @return string escaped string.
5176                 * @access protected
5177                 */
5178                 function _uristring($s) {
5179                         if ($this->encrypted) {
5180                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5181                         }
5182                         return '('.$this->_escape($s).')';
5183                 }
5184
5185                 /**
5186                 * Format a text string
5187                 * @param string $s string to escape.
5188                 * @return string escaped string.
5189                 * @access protected
5190                 */
5191                 function _escapetext($s) {
5192                         if ($this->isunicode) {
5193                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5194                                         $s = $this->UTF8ToLatin1($s);
5195                                 } else {
5196                                         //Convert string to UTF-16BE and reverse RTL language
5197                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
5198                                 }
5199                         }
5200                         return $this->_escape($s);
5201                 }
5202
5203                 /**
5204                 * Add "\" before "\", "(" and ")"
5205                 * @param string $s string to escape.
5206                 * @return string escaped string.
5207                 * @access protected
5208                 */
5209                 function _escape($s) {
5210                         // the chr(13) substitution fixes the Bugs item #1421290.
5211                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
5212                 }
5213
5214                 /**
5215                 * Output a stream.
5216                 * @param string $s string to output.
5217                 * @access protected
5218                 */
5219                 function _putstream($s) {
5220                         if ($this->encrypted) {
5221                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5222                         }
5223                         $this->_out('stream');
5224                         $this->_out($s);
5225                         $this->_out('endstream');
5226                 }
5227
5228                 /**
5229                 * Output a string to the document.
5230                 * @param string $s string to output.
5231                 * @access protected
5232                 */
5233                 function _out($s) {
5234                         if ($this->state == 2) {
5235                                 if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
5236                                         // puts data before page footer
5237                                         $page = substr($this->pages[$this->page], 0, -$this->footerlen[$this->page]);
5238                                         $footer = substr($this->pages[$this->page], -$this->footerlen[$this->page]);
5239                                         $this->pages[$this->page] = $page." ".$s."\n".$footer;
5240                                 } else {
5241                                         $this->pages[$this->page] .= $s."\n";
5242                                 }
5243                         } else {
5244                                 $this->buffer .= $s."\n";
5245                         }
5246                 }
5247
5248                 /**
5249                 * Adds unicode fonts.<br>
5250                 * Based on PDF Reference 1.3 (section 5)
5251                 * @access protected
5252                 * @author Nicola Asuni
5253                 * @since 1.52.0.TC005 (2005-01-05)
5254                 */
5255                 function _puttruetypeunicode($font) {
5256                         // Type0 Font
5257                         // A composite font composed of other fonts, organized hierarchically
5258                         $this->_newobj();
5259                         $this->_out('<</Type /Font');
5260                         $this->_out('/Subtype /Type0');
5261                         $this->_out('/BaseFont /'.$font['name'].'');
5262                         $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.
5263                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5264                         $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
5265                         $this->_out('>>');
5266                         $this->_out('endobj');
5267                         // CIDFontType2
5268                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5269                         $this->_newobj();
5270                         $this->_out('<</Type /Font');
5271                         $this->_out('/Subtype /CIDFontType2');
5272                         $this->_out('/BaseFont /'.$font['name'].'');
5273                         $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
5274                         $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
5275                         if (isset($font['desc']['MissingWidth'])){
5276                                 $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth
5277                         }
5278                         $w = "";
5279                         foreach ($font['cw'] as $cid => $width) {
5280                                 $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID
5281                         }
5282                         $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont
5283                         $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
5284                         $this->_out('>>');
5285                         $this->_out('endobj');
5286                         // ToUnicode
5287                         // is a stream object that contains the definition of the CMap
5288                         // (PDF Reference 1.3 chap. 5.9)
5289                         $this->_newobj();
5290                         $this->_out('<</Length 345>>');
5291                         $this->_out('stream');
5292                         $this->_out('/CIDInit /ProcSet findresource begin');
5293                         $this->_out('12 dict begin');
5294                         $this->_out('begincmap');
5295                         $this->_out('/CIDSystemInfo');
5296                         $this->_out('<</Registry (Adobe)');
5297                         $this->_out('/Ordering (UCS)');
5298                         $this->_out('/Supplement 0');
5299                         $this->_out('>> def');
5300                         $this->_out('/CMapName /Adobe-Identity-UCS def');
5301                         $this->_out('/CMapType 2 def');
5302                         $this->_out('1 begincodespacerange');
5303                         $this->_out('<0000> <FFFF>');
5304                         $this->_out('endcodespacerange');
5305                         $this->_out('1 beginbfrange');
5306                         $this->_out('<0000> <FFFF> <0000>');
5307                         $this->_out('endbfrange');
5308                         $this->_out('endcmap');
5309                         $this->_out('CMapName currentdict /CMap defineresource pop');
5310                         $this->_out('end');
5311                         $this->_out('end');
5312                         $this->_out('endstream');
5313                         $this->_out('endobj');
5314                         // CIDSystemInfo dictionary
5315                         // A dictionary containing entries that define the character collection of the CIDFont.
5316                         $this->_newobj();
5317                         $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections
5318                         $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry
5319                         $this->_out('/Supplement 0'); // The supplement number of the character collection.
5320                         $this->_out('>>');
5321                         $this->_out('endobj');
5322                         // Font descriptor
5323                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
5324                         $this->_newobj();
5325                         $this->_out('<</Type /FontDescriptor');
5326                         $this->_out('/FontName /'.$font['name']);
5327                         foreach ($font['desc'] as $key => $value) {
5328                                 $this->_out('/'.$key.' '.$value);
5329                         }
5330                         if ($font['file']) {
5331                                 // A stream containing a TrueType font program
5332                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
5333                         }
5334                         $this->_out('>>');
5335                         $this->_out('endobj');
5336                         // Embed CIDToGIDMap
5337                         // A specification of the mapping from CIDs to glyph indices
5338                         $this->_newobj();
5339                         $ctgfile = $this->_getfontpath().strtolower($font['ctg']);
5340                         if (!file_exists($ctgfile)) {
5341                                 $this->Error('Font file not found: '.$ctgfile);
5342                         }
5343                         $size = filesize($ctgfile);
5344                         $this->_out('<</Length '.$size.'');
5345                         if (substr($ctgfile, -2) == '.z') { // check file extension
5346                                 /* Decompresses data encoded using the public-domain
5347                                 zlib/deflate compression method, reproducing the
5348                                 original text or binary data */
5349                                 $this->_out('/Filter /FlateDecode');
5350                         }
5351                         $this->_out('>>');
5352                         $this->_putstream(file_get_contents($ctgfile));
5353                         $this->_out('endobj');
5354                 }
5355
5356                  /**
5357                  * Converts UTF-8 strings to codepoints array.<br>
5358                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
5359                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
5360                  * <pre>
5361                  *        Char. number range  |        UTF-8 octet sequence
5362                  *       (hexadecimal)    |              (binary)
5363                  *    --------------------+-----------------------------------------------
5364                  *    0000 0000-0000 007F | 0xxxxxxx
5365                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
5366                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
5367                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5368                  *    ---------------------------------------------------------------------
5369                  *
5370                  *   ABFN notation:
5371                  *   ---------------------------------------------------------------------
5372                  *   UTF8-octets = *( UTF8-char )
5373                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
5374                  *   UTF8-1      = %x00-7F
5375                  *   UTF8-2      = %xC2-DF UTF8-tail
5376                  *
5377                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
5378                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
5379                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
5380                  *                 %xF4 %x80-8F 2( UTF8-tail )
5381                  *   UTF8-tail   = %x80-BF
5382                  *   ---------------------------------------------------------------------
5383                  * </pre>
5384                  * @param string $str string to process.
5385                  * @return array containing codepoints (UTF-8 characters values)
5386                  * @access protected
5387                  * @author Nicola Asuni
5388                  * @since 1.53.0.TC005 (2005-01-05)
5389                  */
5390                 function UTF8StringToArray($str) {
5391                         if (!$this->isunicode) {
5392                                 // split string into array of equivalent codes
5393                                 $strarr = array();
5394                                 $strlen = strlen($str);
5395                                 for($i=0; $i < $strlen; $i++) {
5396                                         $strarr[] = ord($str{$i});
5397                                 }
5398                                 return $strarr;
5399                         }
5400                         $unicode = array(); // array containing unicode values
5401                         $bytes  = array(); // array containing single character byte sequences
5402                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
5403                         $str .= ""; // force $str to be a string
5404                         $length = strlen($str);
5405                         for($i = 0; $i < $length; $i++) {
5406                                 $char = ord($str{$i}); // get one string character at time
5407                                 if (count($bytes) == 0) { // get starting octect
5408                                         if ($char <= 0x7F) {
5409                                                 $unicode[] = $char; // use the character "as is" because is ASCII
5410                                                 $numbytes = 1;
5411                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
5412                                                 $bytes[] = ($char - 0xC0) << 0x06;
5413                                                 $numbytes = 2;
5414                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
5415                                                 $bytes[] = ($char - 0xE0) << 0x0C;
5416                                                 $numbytes = 3;
5417                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
5418                                                 $bytes[] = ($char - 0xF0) << 0x12;
5419                                                 $numbytes = 4;
5420                                         } else {
5421                                                 // use replacement character for other invalid sequences
5422                                                 $unicode[] = 0xFFFD;
5423                                                 $bytes = array();
5424                                                 $numbytes = 1;
5425                                         }
5426                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
5427                                         $bytes[] = $char - 0x80;
5428                                         if (count($bytes) == $numbytes) {
5429                                                 // compose UTF-8 bytes to a single unicode value
5430                                                 $char = $bytes[0];
5431                                                 for($j = 1; $j < $numbytes; $j++) {
5432                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
5433                                                 }
5434                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
5435                                                         /* The definition of UTF-8 prohibits encoding character numbers between
5436                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
5437                                                         encoding form (as surrogate pairs) and do not directly represent
5438                                                         characters. */
5439                                                         $unicode[] = 0xFFFD; // use replacement character
5440                                                 } else {
5441                                                         $unicode[] = $char; // add char to array
5442                                                 }
5443                                                 // reset data for next char
5444                                                 $bytes = array();
5445                                                 $numbytes = 1;
5446                                         }
5447                                 } else {
5448                                         // use replacement character for other invalid sequences
5449                                         $unicode[] = 0xFFFD;
5450                                         $bytes = array();
5451                                         $numbytes = 1;
5452                                 }
5453                         }
5454                         return $unicode;
5455                 }
5456
5457                 /**
5458                  * Converts UTF-8 strings to UTF16-BE.<br>
5459                  * @param string $str string to process.
5460                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5461                  * @return string
5462                  * @access protected
5463                  * @author Nicola Asuni
5464                  * @since 1.53.0.TC005 (2005-01-05)
5465                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
5466                  */
5467                 function UTF8ToUTF16BE($str, $setbom=true) {
5468                         if (!$this->isunicode) {
5469                                 return $str; // string is not in unicode
5470                         }
5471                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5472                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
5473                 }
5474
5475                 /**
5476                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
5477                  * @param string $str string to process.
5478                  * @return string
5479                  * @author Andrew Whitehead, Nicola Asuni
5480                  * @access protected
5481                  * @since 3.2.000 (2008-06-23)
5482                  */
5483                 function UTF8ToLatin1($str) {
5484                         if (!$this->isunicode) {
5485                                 return $str; // string is not in unicode
5486                         }
5487                         $outstr = ""; // string to be returned
5488                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5489                         foreach ($unicode as $char) {
5490                                 if ($char == 0xFFFD) {
5491                                         // skip
5492                                 } elseif ($char == 0x2022) {
5493                                         // fix for middot
5494                                         $outstr .= chr(183);
5495                                 } elseif ($char < 256) {
5496                                         $outstr .= chr($char);
5497                                 } else {
5498                                         $outstr .= '?';
5499                                 }
5500                         }
5501                         return $outstr;
5502                 }
5503
5504                 /**
5505                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
5506                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
5507                  * <pre>
5508                  *   Encoding UTF-16:
5509                  *
5510                  *   Encoding of a single character from an ISO 10646 character value to
5511                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
5512                  *    than 0x10FFFF.
5513                  *
5514                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
5515                  *       terminate.
5516                  *
5517                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
5518                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
5519                  *       represented in 20 bits.
5520                  *
5521                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
5522                  *       0xDC00, respectively. These integers each have 10 bits free to
5523                  *       encode the character value, for a total of 20 bits.
5524                  *
5525                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
5526                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
5527                  *       bits of W2. Terminate.
5528                  *
5529                  *    Graphically, steps 2 through 4 look like:
5530                  *    U' = yyyyyyyyyyxxxxxxxxxx
5531                  *    W1 = 110110yyyyyyyyyy
5532                  *    W2 = 110111xxxxxxxxxx
5533                  * </pre>
5534                  * @param array $unicode array containing UTF-8 unicode values
5535                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5536                  * @return string
5537                  * @access protected
5538                  * @author Nicola Asuni
5539                  * @since 2.1.000 (2008-01-08)
5540                  * @see UTF8ToUTF16BE()
5541                  */
5542                 function arrUTF8ToUTF16BE($unicode, $setbom=true) {
5543                         $outstr = ""; // string to be returned
5544                         if ($setbom) {
5545                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
5546                         }
5547                         foreach($unicode as $char) {
5548                                 if ($char == 0xFFFD) {
5549                                         $outstr .= "\xFF\xFD"; // replacement character
5550                                 } elseif ($char < 0x10000) {
5551                                         $outstr .= chr($char >> 0x08);
5552                                         $outstr .= chr($char & 0xFF);
5553                                 } else {
5554                                         $char -= 0x10000;
5555                                         $w1 = 0xD800 | ($char >> 0x10);
5556                                         $w2 = 0xDC00 | ($char & 0x3FF);
5557                                         $outstr .= chr($w1 >> 0x08);
5558                                         $outstr .= chr($w1 & 0xFF);
5559                                         $outstr .= chr($w2 >> 0x08);
5560                                         $outstr .= chr($w2 & 0xFF);
5561                                 }
5562                         }
5563                         return $outstr;
5564                 }
5565                 // ====================================================
5566
5567                 /**
5568                  * Set header font.
5569                  * @param array $font font
5570                  * @since 1.1
5571                  */
5572                 function setHeaderFont($font) {
5573                         $this->header_font = $font;
5574                 }
5575
5576                 /**
5577                  * Get header font.
5578                  * @return array()
5579                  * @since 4.0.012 (2008-07-24)
5580                  */
5581                 function getHeaderFont() {
5582                         return $this->header_font;
5583                 }
5584
5585                 /**
5586                  * Set footer font.
5587                  * @param array $font font
5588                  * @since 1.1
5589                  */
5590                 function setFooterFont($font) {
5591                         $this->footer_font = $font;
5592                 }
5593
5594                 /**
5595                  * Get Footer font.
5596                  * @return array()
5597                  * @since 4.0.012 (2008-07-24)
5598                  */
5599                 function getFooterFont() {
5600                         return $this->footer_font;
5601                 }
5602
5603                 /**
5604                  * Set language array.
5605                  * @param array $language
5606                  * @since 1.1
5607                  */
5608                 function setLanguageArray($language) {
5609                         $this->l = $language;
5610                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
5611                 }
5612
5613                 /**
5614                  * Returns the PDF data.
5615                  */
5616                 function getPDFData() {
5617                         if ($this->state < 3) {
5618                                 $this->Close();
5619                         }
5620                         return $this->buffer;
5621                 }
5622
5623                 /**
5624                  * Sets font style.
5625                  * @param string $tag tag name in lowercase. Supported tags are:<ul>
5626                  * <li>b : bold text</li>
5627                  * <li>i : italic</li>
5628                  * <li>u : underlined</li>
5629                  * <li>d : line-through</li></ul>
5630                  * @param boolean $enable
5631                  * @access protected
5632                  */
5633                 function setStyle($tag, $enable) {
5634                         $this->$tag += ($enable ? 1 : -1);
5635                         $style = '';
5636                         foreach(array('b', 'i', 'u', 'd') as $s) {
5637                                 if ($this->$s > 0) {
5638                                         $style .= $s;
5639                                 }
5640                         }
5641                         $this->SetFont('', $style);
5642                 }
5643
5644                 /**
5645                  * Output anchor link.
5646                  * @param string $url link URL
5647                  * @param string $name link name
5648                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
5649                  * @param boolean $firstline if true prints only the first line and return the remaining string.
5650                  * @return the number of cells used or the remaining text if $firstline = true;
5651                  * @access public
5652                  */
5653                 function addHtmlLink($url, $name, $fill=0, $firstline=false) {
5654                         $prevcolor = $this->fgcolor;
5655                         $this->SetTextColor(0, 0, 255);
5656                         $this->setStyle('u', true);
5657                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
5658                         $this->setStyle('u', false);
5659                         $this->SetTextColorArray($prevcolor);
5660                         return $ret;
5661                 }
5662
5663                 /**
5664                  * 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).
5665                  * @param string $color html color
5666                  * @return array
5667                  * @access protected
5668                  */
5669                 function convertHTMLColorToDec($color="#000000") {
5670                         global $webcolor;
5671                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
5672                         // set default color to be returned in case of error
5673                         $returncolor = array ('R' => 0, 'G' => 0, 'B' => 0);
5674                         if (empty($color)) {
5675                                 return $returncolor;
5676                         }
5677                         if (substr(strtolower($color), 0, 3) == 'rgb') {
5678                                 $codes = substr($color, 4);
5679                                 $codes = str_replace(')', '', $codes);
5680                                 $returncolor = explode(',', $codes, 3);
5681                                 return $returncolor;    
5682                         }
5683                         if (substr($color, 0, 1) != "#") {
5684                                 // decode color name
5685                                 if (isset($webcolor[strtolower($color)])) {
5686                                         $color_code = $webcolor[strtolower($color)];
5687                                 } else {
5688                                         return $returncolor;
5689                                 }
5690                         } else {
5691                                 $color_code = substr($color, 1);
5692                         }
5693                         switch (strlen($color_code)) {
5694                                 case 3: {
5695                                         // three-digit hexadecimal representation
5696                                         $r = substr($color_code, 0, 1);
5697                                         $g = substr($color_code, 1, 1);
5698                                         $b = substr($color_code, 2, 1);
5699                                         $returncolor['R'] = hexdec($r.$r);
5700                                         $returncolor['G'] = hexdec($g.$g);
5701                                         $returncolor['B'] = hexdec($b.$b);
5702                                         break;
5703                                 }
5704                                 case 6: {
5705                                         // six-digit hexadecimal representation
5706                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
5707                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
5708                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
5709                                         break;
5710                                 }
5711                         }
5712                         return $returncolor;
5713                 }
5714
5715                 /**
5716                  * Converts pixels to Units.
5717                  * @param int $px pixels
5718                  * @return float millimeters
5719                  * @access public
5720                  */
5721                 function pixelsToUnits($px){
5722                         return $px / $this->k;
5723                 }
5724
5725                 /**
5726                  * Reverse function for htmlentities.
5727                  * Convert entities in UTF-8.
5728                  * @param $text_to_convert Text to convert.
5729                  * @return string converted
5730                  */
5731                 function unhtmlentities($text_to_convert) {
5732                         if (!$this->isunicode) {
5733                                 return html_entity_decode($text_to_convert, ENT_QUOTES);
5734                         }
5735                         return html_entity_decode_php4($text_to_convert);
5736                 }
5737
5738                 // ENCRYPTION METHODS ----------------------------------
5739                 // SINCE 2.0.000 (2008-01-02)
5740                 /**
5741                 * Compute encryption key depending on object number where the encrypted data is stored
5742                 * @param int $n object number
5743                 * @since 2.0.000 (2008-01-02)
5744                 */
5745                 function _objectkey($n) {
5746                         return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10);
5747                 }
5748
5749                 /**
5750                  * Put encryption on PDF document.
5751                  * @since 2.0.000 (2008-01-02)
5752                  */
5753                 function _putencryption() {
5754                         $this->_out('/Filter /Standard');
5755                         $this->_out('/V 1');
5756                         $this->_out('/R 2');
5757                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
5758                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
5759                         $this->_out('/P '.$this->Pvalue);
5760                 }
5761
5762                 /**
5763                 * Returns the input text exrypted using RC4 algorithm and the specified key.
5764                 * RC4 is the standard encryption algorithm used in PDF format
5765                 * @param string $key encryption key
5766                 * @param String $text input text to be encrypted
5767                 * @return String encrypted text
5768                 * @since 2.0.000 (2008-01-02)
5769                 * @author Klemen Vodopivec
5770                 */
5771                 function _RC4($key, $text) {
5772                         if ($this->last_rc4_key != $key) {
5773                                 $k = str_repeat($key, 256/strlen($key)+1);
5774                                 $rc4 = range(0,255);
5775                                 $j = 0;
5776                                 for ($i=0; $i < 256; $i++) {
5777                                         $t = $rc4[$i];
5778                                         $j = ($j + $t + ord($k{$i})) % 256;
5779                                         $rc4[$i] = $rc4[$j];
5780                                         $rc4[$j] = $t;
5781                                 }
5782                                 $this->last_rc4_key = $key;
5783                                 $this->last_rc4_key_c = $rc4;
5784                         } else {
5785                                 $rc4 = $this->last_rc4_key_c;
5786                         }
5787                         $len = strlen($text);
5788                         $a = 0;
5789                         $b = 0;
5790                         $out = '';
5791                         for ($i=0; $i < $len; $i++) {
5792                                 $a = ($a + 1) % 256;
5793                                 $t = $rc4[$a];
5794                                 $b = ($b + $t) % 256;
5795                                 $rc4[$a] = $rc4[$b];
5796                                 $rc4[$b] = $t;
5797                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
5798                                 $out .= chr(ord($text{$i}) ^ $k);
5799                         }
5800                         return $out;
5801                 }
5802
5803                 /**
5804                 * Encrypts a string using MD5 and returns it's value as a binary string.
5805                 * @param string $str input string
5806                 * @return String MD5 encrypted binary string
5807                 * @since 2.0.000 (2008-01-02)
5808                 * @author Klemen Vodopivec
5809                 */
5810                 function _md5_16($str) {
5811                         return pack('H*',md5($str));
5812                 }
5813
5814                 /**
5815                 * Compute O value (used for RC4 encryption)
5816                 * @param String $user_pass user password
5817                 * @param String $owner_pass user password
5818                 * @return String O value
5819                 * @since 2.0.000 (2008-01-02)
5820                 * @author Klemen Vodopivec
5821                 */
5822                 function _Ovalue($user_pass, $owner_pass) {
5823                         $tmp = $this->_md5_16($owner_pass);
5824                         $owner_RC4_key = substr($tmp,0,5);
5825                         return $this->_RC4($owner_RC4_key, $user_pass);
5826                 }
5827
5828                 /**
5829                 * Compute U value (used for RC4 encryption)
5830                 * @return String U value
5831                 * @since 2.0.000 (2008-01-02)
5832                 * @author Klemen Vodopivec
5833                 */
5834                 function _Uvalue() {
5835                         return $this->_RC4($this->encryption_key, $this->padding);
5836                 }
5837
5838                 /**
5839                 * Compute encryption key
5840                 * @param String $user_pass user password
5841                 * @param String $owner_pass user password
5842                 * @param String $protection protection type
5843                 * @since 2.0.000 (2008-01-02)
5844                 * @author Klemen Vodopivec
5845                 */
5846                 function _generateencryptionkey($user_pass, $owner_pass, $protection) {
5847                         // Pad passwords
5848                         $user_pass = substr($user_pass.$this->padding,0,32);
5849                         $owner_pass = substr($owner_pass.$this->padding,0,32);
5850                         // Compute O value
5851                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
5852                         // Compute encyption key
5853                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
5854                         $this->encryption_key = substr($tmp,0,5);
5855                         // Compute U value
5856                         $this->Uvalue = $this->_Uvalue();
5857                         // Compute P value
5858                         $this->Pvalue = -(($protection^255)+1);
5859                 }
5860
5861                 /**
5862                 * Set document protection
5863                 * The permission array is composed of values taken from the following ones:
5864                 * - copy: copy text and images to the clipboard
5865                 * - print: print the document
5866                 * - modify: modify it (except for annotations and forms)
5867                 * - annot-forms: add annotations and forms
5868                 * Remark: the protection against modification is for people who have the full Acrobat product.
5869                 * 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.
5870                 * 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.
5871                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
5872                 * @param String $user_pass user password. Empty by default.
5873                 * @param String $owner_pass owner password. If not specified, a random value is used.
5874                 * @since 2.0.000 (2008-01-02)
5875                 * @author Klemen Vodopivec
5876                 */
5877                 function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
5878                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
5879                         $protection = 192;
5880                         foreach($permissions as $permission) {
5881                                 if (!isset($options[$permission])) {
5882                                         $this->Error('Incorrect permission: '.$permission);
5883                                 }
5884                                 $protection += $options[$permission];
5885                         }
5886                         if ($owner_pass === null) {
5887                                 $owner_pass = uniqid(rand());
5888                         }
5889                         $this->encrypted = true;
5890                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
5891                 }
5892
5893                 // END OF ENCRYPTION FUNCTIONS -------------------------
5894
5895                 // START TRANSFORMATIONS SECTION -----------------------
5896                 // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni
5897
5898                 /**
5899                 * Starts a 2D tranformation saving current graphic state.
5900                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
5901                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5902                 * @since 2.1.000 (2008-01-07)
5903                 * @see StartTransform(), StopTransform()
5904                 */
5905                 function StartTransform() {
5906                         $this->_out('q');
5907                 }
5908
5909                 /**
5910                 * Stops a 2D tranformation restoring previous graphic state.
5911                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
5912                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5913                 * @since 2.1.000 (2008-01-07)
5914                 * @see StartTransform(), StopTransform()
5915                 */
5916                 function StopTransform() {
5917                         $this->_out('Q');
5918                 }
5919                 /**
5920                 * Horizontal Scaling.
5921                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5922                 * @param int $x abscissa of the scaling center. Default is current x position
5923                 * @param int $y ordinate of the scaling center. Default is current y position
5924                 * @since 2.1.000 (2008-01-07)
5925                 * @see StartTransform(), StopTransform()
5926                 */
5927                 function ScaleX($s_x, $x='', $y=''){
5928                         $this->Scale($s_x, 100, $x, $y);
5929                 }
5930
5931                 /**
5932                 * Vertical Scaling.
5933                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5934                 * @param int $x abscissa of the scaling center. Default is current x position
5935                 * @param int $y ordinate of the scaling center. Default is current y position
5936                 * @since 2.1.000 (2008-01-07)
5937                 * @see StartTransform(), StopTransform()
5938                 */
5939                 function ScaleY($s_y, $x='', $y=''){
5940                         $this->Scale(100, $s_y, $x, $y);
5941                 }
5942
5943                 /**
5944                 * Vertical and horizontal proportional Scaling.
5945                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
5946                 * @param int $x abscissa of the scaling center. Default is current x position
5947                 * @param int $y ordinate of the scaling center. Default is current y position
5948                 * @since 2.1.000 (2008-01-07)
5949                 * @see StartTransform(), StopTransform()
5950                 */
5951                 function ScaleXY($s, $x='', $y=''){
5952                         $this->Scale($s, $s, $x, $y);
5953                 }
5954
5955                 /**
5956                 * Vertical and horizontal non-proportional Scaling.
5957                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5958                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5959                 * @param int $x abscissa of the scaling center. Default is current x position
5960                 * @param int $y ordinate of the scaling center. Default is current y position
5961                 * @since 2.1.000 (2008-01-07)
5962                 * @see StartTransform(), StopTransform()
5963                 */
5964                 function Scale($s_x, $s_y, $x='', $y=''){
5965                         if ($x === '') {
5966                                 $x=$this->x;
5967                         }
5968                         if ($y === '') {
5969                                 $y=$this->y;
5970                         }
5971                         if ($this->rtl) {
5972                                 $x = $this->w - $x;
5973                         }
5974                         if (($s_x == 0) OR ($s_y == 0)) {
5975                                 $this->Error('Please do not use values equal to zero for scaling');
5976                         }
5977                         $y = ($this->h - $y) * $this->k;
5978                         $x *= $this->k;
5979                         //calculate elements of transformation matrix
5980                         $s_x /= 100;
5981                         $s_y /= 100;
5982                         $tm[0] = $s_x;
5983                         $tm[1] = 0;
5984                         $tm[2] = 0;
5985                         $tm[3] = $s_y;
5986                         $tm[4] = $x * (1 - $s_x);
5987                         $tm[5] = $y * (1 - $s_y);
5988                         //scale the coordinate system
5989                         $this->Transform($tm);
5990                 }
5991
5992                 /**
5993                 * Horizontal Mirroring.
5994                 * @param int $x abscissa of the point. Default is current x position
5995                 * @since 2.1.000 (2008-01-07)
5996                 * @see StartTransform(), StopTransform()
5997                 */
5998                 function MirrorH($x=''){
5999                         $this->Scale(-100, 100, $x);
6000                 }
6001
6002                 /**
6003                 * Verical Mirroring.
6004                 * @param int $y ordinate of the point. Default is current y position
6005                 * @since 2.1.000 (2008-01-07)
6006                 * @see StartTransform(), StopTransform()
6007                 */
6008                 function MirrorV($y=''){
6009                         $this->Scale(100, -100, '', $y);
6010                 }
6011
6012                 /**
6013                 * Point reflection mirroring.
6014                 * @param int $x abscissa of the point. Default is current x position
6015                 * @param int $y ordinate of the point. Default is current y position
6016                 * @since 2.1.000 (2008-01-07)
6017                 * @see StartTransform(), StopTransform()
6018                 */
6019                 function MirrorP($x='',$y=''){
6020                         $this->Scale(-100, -100, $x, $y);
6021                 }
6022
6023                 /**
6024                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
6025                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
6026                 * @param int $x abscissa of the point. Default is current x position
6027                 * @param int $y ordinate of the point. Default is current y position
6028                 * @since 2.1.000 (2008-01-07)
6029                 * @see StartTransform(), StopTransform()
6030                 */
6031                 function MirrorL($angle=0, $x='',$y=''){
6032                         $this->Scale(-100, 100, $x, $y);
6033                         $this->Rotate(-2*($angle-90), $x, $y);
6034                 }
6035
6036                 /**
6037                 * Translate graphic object horizontally.
6038                 * @param int $t_x movement to the right (or left for RTL)
6039                 * @since 2.1.000 (2008-01-07)
6040                 * @see StartTransform(), StopTransform()
6041                 */
6042                 function TranslateX($t_x){
6043                         $this->Translate($t_x, 0);
6044                 }
6045
6046                 /**
6047                 * Translate graphic object vertically.
6048                 * @param int $t_y movement to the bottom
6049                 * @since 2.1.000 (2008-01-07)
6050                 * @see StartTransform(), StopTransform()
6051                 */
6052                 function TranslateY($t_y){
6053                         $this->Translate(0, $t_y);
6054                 }
6055
6056                 /**
6057                 * Translate graphic object horizontally and vertically.
6058                 * @param int $t_x movement to the right
6059                 * @param int $t_y movement to the bottom
6060                 * @since 2.1.000 (2008-01-07)
6061                 * @see StartTransform(), StopTransform()
6062                 */
6063                 function Translate($t_x, $t_y){
6064                         if ($this->rtl) {
6065                                 $t_x = -$t_x;
6066                         }
6067                         //calculate elements of transformation matrix
6068                         $tm[0] = 1;
6069                         $tm[1] = 0;
6070                         $tm[2] = 0;
6071                         $tm[3] = 1;
6072                         $tm[4] = $t_x * $this->k;
6073                         $tm[5] = -$t_y * $this->k;
6074                         //translate the coordinate system
6075                         $this->Transform($tm);
6076                 }
6077
6078                 /**
6079                 * Rotate object.
6080                 * @param float $angle angle in degrees for counter-clockwise rotation
6081                 * @param int $x abscissa of the rotation center. Default is current x position
6082                 * @param int $y ordinate of the rotation center. Default is current y position
6083                 * @since 2.1.000 (2008-01-07)
6084                 * @see StartTransform(), StopTransform()
6085                 */
6086                 function Rotate($angle, $x='', $y=''){
6087                         if ($x === '') {
6088                                 $x=$this->x;
6089                         }
6090                         if ($y === '') {
6091                                 $y=$this->y;
6092                         }
6093                         if ($this->rtl) {
6094                                 $x = $this->w - $x;
6095                                 $angle = -$angle;
6096                         }
6097                         $y = ($this->h - $y) * $this->k;
6098                         $x *= $this->k;
6099                         //calculate elements of transformation matrix
6100                         $tm[0] = cos(deg2rad($angle));
6101                         $tm[1] = sin(deg2rad($angle));
6102                         $tm[2] = -$tm[1];
6103                         $tm[3] = $tm[0];
6104                         $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
6105                         $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
6106                         //rotate the coordinate system around ($x,$y)
6107                         $this->Transform($tm);
6108                 }
6109
6110                 /**
6111                 * Skew horizontally.
6112                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6113                 * @param int $x abscissa of the skewing center. default is current x position
6114                 * @param int $y ordinate of the skewing center. default is current y position
6115                 * @since 2.1.000 (2008-01-07)
6116                 * @see StartTransform(), StopTransform()
6117                 */
6118                 function SkewX($angle_x, $x='', $y=''){
6119                         $this->Skew($angle_x, 0, $x, $y);
6120                 }
6121
6122                 /**
6123                 * Skew vertically.
6124                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6125                 * @param int $x abscissa of the skewing center. default is current x position
6126                 * @param int $y ordinate of the skewing center. default is current y position
6127                 * @since 2.1.000 (2008-01-07)
6128                 * @see StartTransform(), StopTransform()
6129                 */
6130                 function SkewY($angle_y, $x='', $y=''){
6131                         $this->Skew(0, $angle_y, $x, $y);
6132                 }
6133
6134                 /**
6135                 * Skew.
6136                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6137                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6138                 * @param int $x abscissa of the skewing center. default is current x position
6139                 * @param int $y ordinate of the skewing center. default is current y position
6140                 * @since 2.1.000 (2008-01-07)
6141                 * @see StartTransform(), StopTransform()
6142                 */
6143                 function Skew($angle_x, $angle_y, $x='', $y=''){
6144                         if ($x === '') {
6145                                 $x = $this->x;
6146                         }
6147                         if ($y === '') {
6148                                 $y = $this->y;
6149                         }
6150                         if ($this->rtl) {
6151                                 $x = $this->w - $x;
6152                                 $angle_x = -$angle_x;
6153                         }
6154                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
6155                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
6156                         }
6157                         $x *= $this->k;
6158                         $y = ($this->h - $y) * $this->k;
6159                         //calculate elements of transformation matrix
6160                         $tm[0] = 1;
6161                         $tm[1] = tan(deg2rad($angle_y));
6162                         $tm[2] = tan(deg2rad($angle_x));
6163                         $tm[3] = 1;
6164                         $tm[4] = -$tm[2] * $y;
6165                         $tm[5] = -$tm[1] * $x;
6166                         //skew the coordinate system
6167                         $this->Transform($tm);
6168                 }
6169
6170                 /**
6171                 * Apply graphic transformations.
6172                 * @since 2.1.000 (2008-01-07)
6173                 * @see StartTransform(), StopTransform()
6174                 */
6175                 function Transform($tm){
6176                         $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
6177                 }
6178
6179                 // END TRANSFORMATIONS SECTION -------------------------
6180
6181
6182                 // START GRAPHIC FUNCTIONS SECTION ---------------------
6183                 // The following section is based on the code provided by David Hernandez Sanz
6184
6185                 /**
6186                 * 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.
6187                 * @param float $width The width.
6188                 * @since 1.0
6189                 * @see Line(), Rect(), Cell(), MultiCell()
6190                 */
6191                 function SetLineWidth($width) {
6192                         //Set line width
6193                         $this->LineWidth = $width;
6194                         //$this->linestyleWidth = sprintf('%.2f w', ($width * $this->k));
6195                         // FrontAccounting fix
6196                         // My PHP 5.2.6 environment gave an "Unsupported operand types"
6197                         // error for the multiplication on the next line some of the
6198                         // time when this method is called - I debugged and sometimes
6199                         // the $width parameter is some sort of weird array.  I don't
6200                         // understand what's going on, but casting it to a (float) seems
6201                         // to "fix" the problem.  -Jason Maas, 2009/09/25
6202                         $this->linestyleWidth = sprintf('%.2f w', ((float) $width * (float) $this->k));
6203                         $this->_out($this->linestyleWidth);
6204                 }
6205
6206                 /**
6207                 * Returns the current the line width.
6208                 * @return int Line width
6209                 * @since 2.1.000 (2008-01-07)
6210                 * @see Line(), SetLineWidth()
6211                 */
6212                 function GetLineWidth() {
6213                         return $this->LineWidth;
6214                 }
6215
6216                 /**
6217                 * Set line style.
6218                 * @param array $style Line style. Array with keys among the following:
6219                 * <ul>
6220                 *        <li>width (float): Width of the line in user units.</li>
6221                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
6222                 * butt, round, square. The difference between "square" and "butt" is that
6223                 * "square" projects a flat end past the end of the line.</li>
6224                 *        <li>join (string): Type of join. Possible values are: miter, round,
6225                 * bevel.</li>
6226                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
6227                 * series of length values, which are the lengths of the on and off dashes.
6228                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
6229                 * 1 off, 2 on, 1 off, ...</li>
6230                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
6231                 * the point at which the pattern starts.</li>
6232                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
6233                 * </ul>
6234                 * @access public
6235                 * @since 2.1.000 (2008-01-08)
6236                 */
6237                 function SetLineStyle($style) {
6238                         extract($style);
6239                         if (isset($width)) {
6240                                 $width_prev = $this->LineWidth;
6241                                 $this->SetLineWidth($width);
6242                                 $this->LineWidth = $width_prev;
6243                         }
6244                         if (isset($cap)) {
6245                                 $ca = array("butt" => 0, "round"=> 1, "square" => 2);
6246                                 if (isset($ca[$cap])) {
6247                                         $this->linestyleCap = $ca[$cap]." J";
6248                                         $this->_out($this->linestyleCap);
6249                                 }
6250                         }
6251                         if (isset($join)) {
6252                                 $ja = array("miter" => 0, "round" => 1, "bevel" => 2);
6253                                 if (isset($ja[$join])) {
6254                                         $this->linestyleJoin = $ja[$join]." j";
6255                                         $this->_out($this->linestyleJoin);
6256                                 }
6257                         }
6258                         if (isset($dash)) {
6259                                 $dash_string = "";
6260                                 if ($dash) {
6261                                         if (ereg("^.+,", $dash)) {
6262                                                 $tab = explode(",", $dash);
6263                                         } else {
6264                                                 $tab = array($dash);
6265                                         }
6266                                         $dash_string = "";
6267                                         foreach ($tab as $i => $v) {
6268                                                 if ($i) {
6269                                                         $dash_string .= " ";
6270                                                 }
6271                                                 $dash_string .= sprintf("%.2f", $v);
6272                                         }
6273                                 }
6274                                 if (!isset($phase) OR !$dash) {
6275                                         $phase = 0;
6276                                 }
6277                                 $this->linestyleDash = sprintf("[%s] %.2f d", $dash_string, $phase);
6278                                 $this->_out($this->linestyleDash);
6279                         }
6280                         if (isset($color)) {
6281                                 $this->SetDrawColorArray($color);
6282                         }
6283                 }
6284
6285                 /*
6286                 * Set a draw point.
6287                 * @param float $x Abscissa of point.
6288                 * @param float $y Ordinate of point.
6289                 * @access protected
6290                 * @since 2.1.000 (2008-01-08)
6291                 */
6292                 function _outPoint($x, $y) {
6293                         if ($this->rtl) {
6294                                 $x = $this->w - $x;
6295                         }
6296                         $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k));
6297                 }
6298
6299                 /*
6300                 * Draws a line from last draw point.
6301                 * @param float $x Abscissa of end point.
6302                 * @param float $y Ordinate of end point.
6303                 * @access protected
6304                 * @since 2.1.000 (2008-01-08)
6305                 */
6306                 function _outLine($x, $y) {
6307                         if ($this->rtl) {
6308                                 $x = $this->w - $x;
6309                         }
6310                         $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k));
6311                 }
6312
6313                 /**
6314                 * Draws a rectangle.
6315                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6316                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6317                 * @param float $w Width.
6318                 * @param float $h Height.
6319                 * @param string $op options
6320                 * @access protected
6321                 * @since 2.1.000 (2008-01-08)
6322                 */
6323                 function _outRect($x, $y, $w, $h, $op) {
6324                         if ($this->rtl) {
6325                                 $x = $this->w - $x - $w;
6326                         }
6327                         $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
6328                 }
6329
6330                 /*
6331                 * Draws a Bezier curve from last draw point.
6332                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
6333                 * @param float $x1 Abscissa of control point 1.
6334                 * @param float $y1 Ordinate of control point 1.
6335                 * @param float $x2 Abscissa of control point 2.
6336                 * @param float $y2 Ordinate of control point 2.
6337                 * @param float $x3 Abscissa of end point.
6338                 * @param float $y3 Ordinate of end point.
6339                 * @access protected
6340                 * @since 2.1.000 (2008-01-08)
6341                 */
6342                 function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
6343                         if ($this->rtl) {
6344                                 $x1 = $this->w - $x1;
6345                                 $x2 = $this->w - $x2;
6346                                 $x3 = $this->w - $x3;
6347                         }
6348                         $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));
6349                 }
6350
6351                 /**
6352                 * Draws a line between two points.
6353                 * @param float $x1 Abscissa of first point.
6354                 * @param float $y1 Ordinate of first point.
6355                 * @param float $x2 Abscissa of second point.
6356                 * @param float $y2 Ordinate of second point.
6357                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6358                 * @access public
6359                 * @since 1.0
6360                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
6361                 */
6362                 function Line($x1, $y1, $x2, $y2, $style=array()) {
6363                         if ($style) {
6364                                 $this->SetLineStyle($style);
6365                         }
6366                         $this->_outPoint($x1, $y1);
6367                         $this->_outLine($x2, $y2);
6368                         $this->_out(" S");
6369                 }
6370
6371                 /**
6372                 * Draws a rectangle.
6373                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6374                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6375                 * @param float $w Width.
6376                 * @param float $h Height.
6377                 * @param string $style Style of rendering. Possible values are:
6378                 * <ul>
6379                 *        <li>D or empty string: Draw (default).</li>
6380                 *        <li>F: Fill.</li>
6381                 *        <li>DF or FD: Draw and fill.</li>
6382                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6383                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6384                 * </ul>
6385                 * @param array $border_style Border style of rectangle. Array with keys among the following:
6386                 * <ul>
6387                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
6388                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
6389                 * </ul>
6390                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
6391                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6392                 * @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).
6393                 * @access public
6394                 * @since 1.0
6395                 * @see SetLineStyle()
6396                 */
6397                 function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
6398                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6399                                 $this->SetFillColorArray($fill_color);
6400                         }
6401                         switch ($style) {
6402                                 case "F": {
6403                                         $op = 'f';
6404                                         $border_style = array();
6405                                         $this->_outRect($x, $y, $w, $h, $op);
6406                                         break;
6407                                 }
6408                                 case "DF":
6409                                 case "FD": {
6410                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6411                                                 $op = 'B';
6412                                                 if (isset($border_style["all"])) {
6413                                                         $this->SetLineStyle($border_style["all"]);
6414                                                         $border_style = array();
6415                                                 }
6416                                         } else {
6417                                                 $op = 'f';
6418                                         }
6419                                         $this->_outRect($x, $y, $w, $h, $op);
6420                                         break;
6421                                 }
6422                                 case "CNZ": {
6423                                         $op = "W n";
6424                                         break;
6425                                 }
6426                                 case "CEO": {
6427                                         $op = "W* n";
6428                                         break;
6429                                 }
6430                                 default: {
6431                                         $op = 'S';
6432                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6433                                                 if (isset($border_style["all"]) AND $border_style["all"]) {
6434                                                         $this->SetLineStyle($border_style["all"]);
6435                                                         $border_style = array();
6436                                                 }
6437                                                 $this->_outRect($x, $y, $w, $h, $op);
6438                                         }
6439                                         break;
6440                                 }
6441                         }
6442                         if ($border_style) {
6443                                 $border_style2 = array();
6444                                 foreach ($border_style as $line => $value) {
6445                                         $lenght = strlen($line);
6446                                         for ($i = 0; $i < $lenght; $i++) {
6447                                                 $border_style2[$line[$i]] = $value;
6448                                         }
6449                                 }
6450                                 $border_style = $border_style2;
6451                                 if (isset($border_style["L"]) AND $border_style["L"]) {
6452                                         $this->Line($x, $y, $x, $y + $h, $border_style["L"]);
6453                                 }
6454                                 if (isset($border_style["T"]) AND $border_style["T"]) {
6455                                         $this->Line($x, $y, $x + $w, $y, $border_style["T"]);
6456                                 }
6457                                 if (isset($border_style["R"]) AND $border_style["R"]) {
6458                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]);
6459                                 }
6460                                 if (isset($border_style["B"]) AND $border_style["B"]) {
6461                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]);
6462                                 }
6463                         }
6464                 }
6465
6466
6467                 /**
6468                 * Draws a Bezier curve.
6469                 * The Bezier curve is a tangent to the line between the control points at
6470                 * either end of the curve.
6471                 * @param float $x0 Abscissa of start point.
6472                 * @param float $y0 Ordinate of start point.
6473                 * @param float $x1 Abscissa of control point 1.
6474                 * @param float $y1 Ordinate of control point 1.
6475                 * @param float $x2 Abscissa of control point 2.
6476                 * @param float $y2 Ordinate of control point 2.
6477                 * @param float $x3 Abscissa of end point.
6478                 * @param float $y3 Ordinate of end point.
6479                 * @param string $style Style of rendering. Possible values are:
6480                 * <ul>
6481                 *        <li>D or empty string: Draw (default).</li>
6482                 *        <li>F: Fill.</li>
6483                 *        <li>DF or FD: Draw and fill.</li>
6484                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6485                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6486                 * </ul>
6487                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6488                 * @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).
6489                 * @access public
6490                 * @see SetLineStyle()
6491                 * @since 2.1.000 (2008-01-08)
6492                 */
6493                 function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style="", $line_style=array(), $fill_color=array()) {
6494                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6495                                 $this->SetFillColorArray($fill_color);
6496                         }
6497                         switch ($style) {
6498                                 case "F": {
6499                                         $op = "f";
6500                                         $line_style = array();
6501                                         break;
6502                                 }
6503                                 case "FD":
6504                                 case "DF": {
6505                                         $op = "B";
6506                                         break;
6507                                 }
6508                                 case "CNZ": {
6509                                         $op = "W n";
6510                                         break;
6511                                 }
6512                                 case "CEO": {
6513                                         $op = "W* n";
6514                                         break;
6515                                 }
6516                                 default: {
6517                                         $op = "S";
6518                                         break;
6519                                 }
6520                         }
6521                         if ($line_style) {
6522                                 $this->SetLineStyle($line_style);
6523                         }
6524                         $this->_outPoint($x0, $y0);
6525                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6526                         $this->_out($op);
6527                 }
6528
6529                 /**
6530                 * Draws a poly-Bezier curve.
6531                 * Each Bezier curve segment is a tangent to the line between the control points at
6532                 * either end of the curve.
6533                 * @param float $x0 Abscissa of start point.
6534                 * @param float $y0 Ordinate of start point.
6535                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
6536                 * @param string $style Style of rendering. Possible values are:
6537                 * <ul>
6538                 *        <li>D or empty string: Draw (default).</li>
6539                 *        <li>F: Fill.</li>
6540                 *        <li>DF or FD: Draw and fill.</li>
6541                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6542                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6543                 * </ul>
6544                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6545                 * @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).
6546                 * @access public
6547                 * @see SetLineStyle()
6548                 * @since 3.0008 (2008-05-12)
6549                 */
6550                 function Polycurve($x0, $y0, $segments, $style="", $line_style=array(), $fill_color=array()) {
6551                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6552                                 $this->SetFillColorArray($fill_color);
6553                         }
6554                         switch ($style) {
6555                                 case "F": {
6556                                         $op = "f";
6557                                         $line_style = array();
6558                                         break;
6559                                 }
6560                                 case "FD":
6561                                 case "DF": {
6562                                         $op = "B";
6563                                         break;
6564                                 }
6565                                 case "CNZ": {
6566                                         $op = "W n";
6567                                         break;
6568                                 }
6569                                 case "CEO": {
6570                                         $op = "W* n";
6571                                         break;
6572                                 }
6573                                 default: {
6574                                         $op = "S";
6575                                         break;
6576                                 }
6577                         }
6578                         if ($line_style) {
6579                                 $this->SetLineStyle($line_style);
6580                         }
6581                         $this->_outPoint($x0, $y0);
6582                         foreach ($segments as $segment) {
6583                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
6584                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6585                         }
6586                         $this->_out($op);
6587                 }
6588
6589                 /**
6590                 * Draws an ellipse.
6591                 * An ellipse is formed from n Bezier curves.
6592                 * @param float $x0 Abscissa of center point.
6593                 * @param float $y0 Ordinate of center point.
6594                 * @param float $rx Horizontal radius.
6595                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
6596                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6597                 * @param float $astart: Angle start of draw line. Default value: 0.
6598                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6599                 * @param string $style Style of rendering. Possible values are:
6600                 * <ul>
6601                 *        <li>D or empty string: Draw (default).</li>
6602                 *        <li>F: Fill.</li>
6603                 *        <li>DF or FD: Draw and fill.</li>
6604                 *        <li>C: Draw close.</li>
6605                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6606                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6607                 * </ul>
6608                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6609                 * @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).
6610                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
6611                 * @access public
6612                 * @since 2.1.000 (2008-01-08)
6613                 */
6614                 function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6615                         if ($angle) {
6616                                 $this->StartTransform();
6617                                 $this->Rotate($angle, $x0, $y0);
6618                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6619                                 $this->StopTransform();
6620                                 return;
6621                         }
6622                         if ($rx) {
6623                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6624                                         $this->SetFillColorArray($fill_color);
6625                                 }
6626                                 switch ($style) {
6627                                         case "F": {
6628                                                 $op = "f";
6629                                                 $line_style = array();
6630                                                 break;
6631                                         }
6632                                         case "FD":
6633                                         case "DF": {
6634                                                 $op = "B";
6635                                                 break;
6636                                         }
6637                                         case "C": {
6638                                                 $op = "s"; // Small "s" signifies closing the path as well
6639                                                 break;
6640                                         }
6641                                         case "CNZ": {
6642                                                 $op = "W n";
6643                                                 break;
6644                                         }
6645                                         case "CEO": {
6646                                                 $op = "W* n";
6647                                                 break;
6648                                         }
6649                                         default: {
6650                                                 $op = "S";
6651                                                 break;
6652                                         }
6653                                 }
6654                                 if ($line_style) {
6655                                         $this->SetLineStyle($line_style);
6656                                 }
6657                                 if (!$ry) {
6658                                         $ry = $rx;
6659                                 }
6660                                 $rx *= $this->k;
6661                                 $ry *= $this->k;
6662                                 if ($nc < 2){
6663                                         $nc = 2;
6664                                 }
6665                                 $astart = deg2rad((float) $astart);
6666                                 $afinish = deg2rad((float) $afinish);
6667                                 $total_angle = $afinish - $astart;
6668                                 $dt = $total_angle / $nc;
6669                                 $dtm = $dt / 3;
6670                                 $x0 *= $this->k;
6671                                 $y0 = ($this->h - $y0) * $this->k;
6672                                 $t1 = $astart;
6673                                 $a0 = $x0 + ($rx * cos($t1));
6674                                 $b0 = $y0 + ($ry * sin($t1));
6675                                 $c0 = -$rx * sin($t1);
6676                                 $d0 = $ry * cos($t1);
6677                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
6678                                 for ($i = 1; $i <= $nc; $i++) {
6679                                         // Draw this bit of the total curve
6680                                         $t1 = ($i * $dt) + $astart;
6681                                         $a1 = $x0 + ($rx * cos($t1));
6682                                         $b1 = $y0 + ($ry * sin($t1));
6683                                         $c1 = -$rx * sin($t1);
6684                                         $d1 = $ry * cos($t1);
6685                                         $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));
6686                                         $a0 = $a1;
6687                                         $b0 = $b1;
6688                                         $c0 = $c1;
6689                                         $d0 = $d1;
6690                                 }
6691                                 $this->_out($op);
6692                         }
6693                 }
6694
6695                 /**
6696                 * Draws a circle.
6697                 * A circle is formed from n Bezier curves.
6698                 * @param float $x0 Abscissa of center point.
6699                 * @param float $y0 Ordinate of center point.
6700                 * @param float $r Radius.
6701                 * @param float $astart: Angle start of draw line. Default value: 0.
6702                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6703                 * @param string $style Style of rendering. Possible values are:
6704                 * <ul>
6705                 *        <li>D or empty string: Draw (default).</li>
6706                 *        <li>F: Fill.</li>
6707                 *        <li>DF or FD: Draw and fill.</li>
6708                 *        <li>C: Draw close.</li>
6709                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6710                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6711                 * </ul>
6712                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6713                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6714                 * @param integer $nc Number of curves used in circle. Default value: 8.
6715                 * @access public
6716                 * @since 2.1.000 (2008-01-08)
6717                 */
6718                 function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6719                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6720                 }
6721
6722                 /**
6723                 * Draws a polygon.
6724                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
6725                 * @param string $style Style of rendering. Possible values are:
6726                 * <ul>
6727                 *        <li>D or empty string: Draw (default).</li>
6728                 *        <li>F: Fill.</li>
6729                 *        <li>DF or FD: Draw and fill.</li>
6730                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6731                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6732                 * </ul>
6733                 * @param array $line_style Line style of polygon. Array with keys among the following:
6734                 * <ul>
6735                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
6736                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
6737                 * </ul>
6738                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
6739                 * @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).
6740                 * @access public
6741                 * @since 2.1.000 (2008-01-08)
6742                 */
6743                 function Polygon($p, $style="", $line_style=array(), $fill_color=array()) {
6744                         $np = count($p) / 2;
6745                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6746                                 $this->SetFillColorArray($fill_color);
6747                         }
6748                         switch ($style) {
6749                                 case "F": {
6750                                         $line_style = array();
6751                                         $op = "f";
6752                                         break;
6753                                 }
6754                                 case "FD":
6755                                 case "DF": {
6756                                         $op = "B";
6757                                         break;
6758                                 }
6759                                 case "CNZ": {
6760                                         $op = "W n";
6761                                         break;
6762                                 }
6763                                 case "CEO": {
6764                                         $op = "W* n";
6765                                         break;
6766                                 }
6767                                 default: {
6768                                         $op = "S";
6769                                         break;
6770                                 }
6771                         }
6772                         $draw = true;
6773                         if ($line_style) {
6774                                 if (isset($line_style["all"])) {
6775                                         $this->SetLineStyle($line_style["all"]);
6776                                 } else { // 0 .. (np - 1), op = {B, S}
6777                                         $draw = false;
6778                                         if ("B" == $op) {
6779                                                 $op = "f";
6780                                                 $this->_outPoint($p[0], $p[1]);
6781                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6782                                                         $this->_outLine($p[$i], $p[$i + 1]);
6783                                                 }
6784                                                 $this->_outLine($p[0], $p[1]);
6785                                                 $this->_out($op);
6786                                         }
6787                                         $p[($np * 2)] = $p[0];
6788                                         $p[(($np * 2) + 1)] = $p[1];
6789                                         for ($i = 0; $i < $np; $i++) {
6790                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
6791                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
6792                                                 }
6793                                         }
6794                                 }
6795                         }
6796                         if ($draw) {
6797                                 $this->_outPoint($p[0], $p[1]);
6798                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6799                                         $this->_outLine($p[$i], $p[$i + 1]);
6800                                 }
6801                                 $this->_outLine($p[0], $p[1]);
6802                                 $this->_out($op);
6803                         }
6804                 }
6805
6806                 /**
6807                 * Draws a regular polygon.
6808                 * @param float $x0 Abscissa of center point.
6809                 * @param float $y0 Ordinate of center point.
6810                 * @param float $r: Radius of inscribed circle.
6811                 * @param integer $ns Number of sides.
6812                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
6813                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
6814                 * @param string $style Style of rendering. Possible values are:
6815                 * <ul>
6816                 *        <li>D or empty string: Draw (default).</li>
6817                 *        <li>F: Fill.</li>
6818                 *        <li>DF or FD: Draw and fill.</li>
6819                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6820                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6821                 * </ul>
6822                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6823                 * <ul>
6824                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
6825                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6826                 * </ul>
6827                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6828                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6829                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6830                 * <ul>
6831                 *        <li>D or empty string: Draw (default).</li>
6832                 *        <li>F: Fill.</li>
6833                 *        <li>DF or FD: Draw and fill.</li>
6834                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6835                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6836                 * </ul>
6837                 * @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).
6838                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6839                 * @access public
6840                 * @since 2.1.000 (2008-01-08)
6841                 */
6842                 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()) {
6843                         if (3 > $ns) {
6844                                 $ns = 3;
6845                         }
6846                         if ($draw_circle) {
6847                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6848                         }
6849                         $p = array();
6850                         for ($i = 0; $i < $ns; $i++) {
6851                                 $a = $angle + ($i * 360 / $ns);
6852                                 $a_rad = deg2rad((float) $a);
6853                                 $p[] = $x0 + ($r * sin($a_rad));
6854                                 $p[] = $y0 + ($r * cos($a_rad));
6855                         }
6856                         $this->Polygon($p, $style, $line_style, $fill_color);
6857                 }
6858
6859                 /**
6860                 * Draws a star polygon
6861                 * @param float $x0 Abscissa of center point.
6862                 * @param float $y0 Ordinate of center point.
6863                 * @param float $r Radius of inscribed circle.
6864                 * @param integer $nv Number of vertices.
6865                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
6866                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6867                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
6868                 * @param string $style Style of rendering. Possible values are:
6869                 * <ul>
6870                 *        <li>D or empty string: Draw (default).</li>
6871                 *        <li>F: Fill.</li>
6872                 *        <li>DF or FD: Draw and fill.</li>
6873                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6874                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6875                 * </ul>
6876                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6877                 * <ul>
6878                 *        <li>all: Line style of all sides. Array like for
6879                 * {@link SetLineStyle SetLineStyle}.</li>
6880                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6881                 * </ul>
6882                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6883                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6884                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6885                 * <ul>
6886                 *        <li>D or empty string: Draw (default).</li>
6887                 *        <li>F: Fill.</li>
6888                 *        <li>DF or FD: Draw and fill.</li>
6889                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6890                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6891                 * </ul>
6892                 * @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).
6893                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6894                 * @access public
6895                 * @since 2.1.000 (2008-01-08)
6896                 */
6897                 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()) {
6898                         if (2 > $nv) {
6899                                 $nv = 2;
6900                         }
6901                         if ($draw_circle) {
6902                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6903                         }
6904                         $p2 = array();
6905                         $visited = array();
6906                         for ($i = 0; $i < $nv; $i++) {
6907                                 $a = $angle + ($i * 360 / $nv);
6908                                 $a_rad = deg2rad((float) $a);
6909                                 $p2[] = $x0 + ($r * sin($a_rad));
6910                                 $p2[] = $y0 + ($r * cos($a_rad));
6911                                 $visited[] = false;
6912                         }
6913                         $p = array();
6914                         $i = 0;
6915                         do {
6916                                 $p[] = $p2[$i * 2];
6917                                 $p[] = $p2[($i * 2) + 1];
6918                                 $visited[$i] = true;
6919                                 $i += $ng;
6920                                 $i %= $nv;
6921                         } while (!$visited[$i]);
6922                         $this->Polygon($p, $style, $line_style, $fill_color);
6923                 }
6924
6925                 /**
6926                 * Draws a rounded rectangle.
6927                 * @param float $x Abscissa of upper-left corner.
6928                 * @param float $y Ordinate of upper-left corner.
6929                 * @param float $w Width.
6930                 * @param float $h Height.
6931                 * @param float $r Radius of the rounded corners.
6932                 * @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").
6933                 * @param string $style Style of rendering. Possible values are:
6934                 * <ul>
6935                 *        <li>D or empty string: Draw (default).</li>
6936                 *        <li>F: Fill.</li>
6937                 *        <li>DF or FD: Draw and fill.</li>
6938                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6939                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6940                 * </ul>
6941                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6942                 * @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).
6943                 * @access public
6944                 * @since 2.1.000 (2008-01-08)
6945                 */
6946                 function RoundedRect($x, $y, $w, $h, $r, $round_corner="1111", $style="", $border_style=array(), $fill_color=array()) {
6947                         if ("0000" == $round_corner) { // Not rounded
6948                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
6949                         } else { // Rounded
6950                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6951                                         $this->SetFillColorArray($fill_color);
6952                                 }
6953                                 switch ($style) {
6954                                         case "F": {
6955                                                 $border_style = array();
6956                                                 $op = "f";
6957                                                 break;
6958                                         }
6959                                         case "FD":
6960                                         case "DF": {
6961                                                 $op = "B";
6962                                                 break;
6963                                         }
6964                                         case "CNZ": {
6965                                                 $op = "W n";
6966                                                 break;
6967                                         }
6968                                         case "CEO": {
6969                                                 $op = "W* n";
6970                                                 break;
6971                                         }
6972                                         default: {
6973                                                 $op = "S";
6974                                                 break;
6975                                         }
6976                                 }
6977                                 if ($border_style) {
6978                                         $this->SetLineStyle($border_style);
6979                                 }
6980                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
6981                                 $this->_outPoint($x + $r, $y);
6982                                 $xc = $x + $w - $r;
6983                                 $yc = $y + $r;
6984                                 $this->_outLine($xc, $y);
6985                                 if ($round_corner[0]) {
6986                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
6987                                 } else {
6988                                         $this->_outLine($x + $w, $y);
6989                                 }
6990                                 $xc = $x + $w - $r;
6991                                 $yc = $y + $h - $r;
6992                                 $this->_outLine($x + $w, $yc);
6993                                 if ($round_corner[1]) {
6994                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
6995                                 } else {
6996                                         $this->_outLine($x + $w, $y + $h);
6997                                 }
6998                                 $xc = $x + $r;
6999                                 $yc = $y + $h - $r;
7000                                 $this->_outLine($xc, $y + $h);
7001                                 if ($round_corner[2]) {
7002                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
7003                                 } else {
7004                                         $this->_outLine($x, $y + $h);
7005                                 }
7006                                 $xc = $x + $r;
7007                                 $yc = $y + $r;
7008                                 $this->_outLine($x, $yc);
7009                                 if ($round_corner[3]) {
7010                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
7011                                 } else {
7012                                         $this->_outLine($x, $y);
7013                                         $this->_outLine($x + $r, $y);
7014                                 }
7015                                 $this->_out($op);
7016                         }
7017                 }
7018
7019                 // END GRAPHIC FUNCTIONS SECTION -----------------------
7020
7021                 // BIDIRECTIONAL TEXT SECTION --------------------------
7022                 /**
7023                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7024                  * @param string $str string to manipulate.
7025                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7026                  * @return string
7027                  * @author Nicola Asuni
7028                  * @since 2.1.000 (2008-01-08)
7029                 */
7030                 function utf8StrRev($str, $setbom=false, $forcertl=false) {
7031                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $forcertl), $setbom);
7032                 }
7033
7034                 /**
7035                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7036                  * @param array $ta array of characters composing the string.
7037                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7038                  * @return string
7039                  * @author Nicola Asuni
7040                  * @since 2.4.000 (2008-03-06)
7041                 */
7042                 function utf8Bidi($ta, $forcertl=false) {
7043                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
7044                         // paragraph embedding level
7045                         $pel = 0;
7046                         // max level
7047                         $maxlevel = 0;
7048                         // create string from array
7049                         $str = $this->UTF8ArrSubString($ta);
7050                         // check if string contains arabic text
7051                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
7052                                 $arabic = true;
7053                         } else {
7054                                 $arabic = false;
7055                         }
7056                         // check if string contains RTL text
7057                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
7058                                 return $ta;
7059                         }
7060                         
7061                         // get number of chars
7062                         $numchars = count($ta);
7063                         
7064                         if ($forcertl == 'R') {
7065                                         $pel = 1;
7066                         } elseif ($forcertl == 'L') {
7067                                         $pel = 0;
7068                         } else {
7069                                 // P2. In each paragraph, find the first character of type L, AL, or R.
7070                                 // 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.
7071                                 for ($i=0; $i < $numchars; $i++) {
7072                                         $type = $unicode[$ta[$i]];
7073                                         if ($type == 'L') {
7074                                                 $pel = 0;
7075                                                 break;
7076                                         } elseif (($type == 'AL') OR ($type == 'R')) {
7077                                                 $pel = 1;
7078                                                 break;
7079                                         }
7080                                 }
7081                         }
7082                         
7083                         // Current Embedding Level
7084                         $cel = $pel;
7085                         // directional override status
7086                         $dos = 'N';
7087                         $remember = array();
7088                         // start-of-level-run
7089                         $sor = $pel % 2 ? 'R' : 'L';
7090                         $eor = $sor;
7091
7092                         //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array()));
7093                         //$current_level = &$levels[count( $levels )-1];
7094
7095                         // Array of characters data
7096                         $chardata = Array();
7097
7098                         // 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.
7099                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
7100                         for ($i=0; $i < $numchars; $i++) {
7101                                 if ($ta[$i] == K_RLE) {
7102                                         // X2. With each RLE, compute the least greater odd embedding level.
7103                                         //      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.
7104                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7105                                         $next_level = $cel + ($cel % 2) + 1;
7106                                         if ($next_level < 62) {
7107                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
7108                                                 $cel = $next_level;
7109                                                 $dos = 'N';
7110                                                 $sor = $eor;
7111                                                 $eor = $cel % 2 ? 'R' : 'L';
7112                                         }
7113                                 } elseif ($ta[$i] == K_LRE) {
7114                                         // X3. With each LRE, compute the least greater even embedding level.
7115                                         //      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.
7116                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7117                                         $next_level = $cel + 2 - ($cel % 2);
7118                                         if ( $next_level < 62 ) {
7119                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
7120                                                 $cel = $next_level;
7121                                                 $dos = 'N';
7122                                                 $sor = $eor;
7123                                                 $eor = $cel % 2 ? 'R' : 'L';
7124                                         }
7125                                 } elseif ($ta[$i] == K_RLO) {
7126                                         // X4. With each RLO, compute the least greater odd embedding level.
7127                                         //      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.
7128                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7129                                         $next_level = $cel + ($cel % 2) + 1;
7130                                         if ($next_level < 62) {
7131                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
7132                                                 $cel = $next_level;
7133                                                 $dos = 'R';
7134                                                 $sor = $eor;
7135                                                 $eor = $cel % 2 ? 'R' : 'L';
7136                                         }
7137                                 } elseif ($ta[$i] == K_LRO) {
7138                                         // X5. With each LRO, compute the least greater even embedding level.
7139                                         //      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.
7140                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7141                                         $next_level = $cel + 2 - ($cel % 2);
7142                                         if ( $next_level < 62 ) {
7143                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
7144                                                 $cel = $next_level;
7145                                                 $dos = 'L';
7146                                                 $sor = $eor;
7147                                                 $eor = $cel % 2 ? 'R' : 'L';
7148                                         }
7149                                 } elseif ($ta[$i] == K_PDF) {
7150                                         // 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.
7151                                         if (count($remember)) {
7152                                                 $last = count($remember ) - 1;
7153                                                 if (($remember[$last]['num'] == K_RLE) OR
7154                                                           ($remember[$last]['num'] == K_LRE) OR
7155                                                           ($remember[$last]['num'] == K_RLO) OR
7156                                                           ($remember[$last]['num'] == K_LRO)) {
7157                                                         $match = array_pop($remember);
7158                                                         $cel = $match['cel'];
7159                                                         $dos = $match['dos'];
7160                                                         $sor = $eor;
7161                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
7162                                                 }
7163                                         }
7164                                 } elseif (($ta[$i] != K_RLE) AND
7165                                                                  ($ta[$i] != K_LRE) AND
7166                                                                  ($ta[$i] != K_RLO) AND
7167                                                                  ($ta[$i] != K_LRO) AND
7168                                                                  ($ta[$i] != K_PDF)) {
7169                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
7170                                         //      a. Set the level of the current character to the current embedding level.
7171                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
7172                                         if ($dos != 'N') {
7173                                                 $chardir = $dos;
7174                                         } else {
7175                                                 $chardir = $unicode[$ta[$i]];
7176                                         }
7177                                         // stores string characters and other information
7178                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
7179                                 }
7180                         } // end for each char
7181
7182                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
7183                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
7184                         // 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.
7185
7186                         // 3.3.3 Resolving Weak Types
7187                         // 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.
7188                         // Nonspacing marks are now resolved based on the previous characters.
7189                         $numchars = count($chardata);
7190
7191                         // 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.
7192                         $prevlevel = -1; // track level changes
7193                         $levcount = 0; // counts consecutive chars at the same level
7194                         for ($i=0; $i < $numchars; $i++) {
7195                                 if ($chardata[$i]['type'] == 'NSM') {
7196                                         if ($levcount) {
7197                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7198                                         } elseif ($i > 0) {
7199                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
7200                                         }
7201                                 }
7202                                 if ($chardata[$i]['level'] != $prevlevel) {
7203                                         $levcount = 0;
7204                                 } else {
7205                                         $levcount++;
7206                                 }
7207                                 $prevlevel = $chardata[$i]['level'];
7208                         }
7209
7210                         // 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.
7211                         $prevlevel = -1;
7212                         $levcount = 0;
7213                         for ($i=0; $i < $numchars; $i++) {
7214                                 if ($chardata[$i]['char'] == 'EN') {
7215                                         for ($j=$levcount; $j >= 0; $j--) {
7216                                                 if ($chardata[$j]['type'] == 'AL') {
7217                                                         $chardata[$i]['type'] = 'AN';
7218                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
7219                                                         break;
7220                                                 }
7221                                         }
7222                                 }
7223                                 if ($chardata[$i]['level'] != $prevlevel) {
7224                                         $levcount = 0;
7225                                 } else {
7226                                         $levcount++;
7227                                 }
7228                                 $prevlevel = $chardata[$i]['level'];
7229                         }
7230
7231                         // W3. Change all ALs to R.
7232                         for ($i=0; $i < $numchars; $i++) {
7233                                 if ($chardata[$i]['type'] == 'AL') {
7234                                         $chardata[$i]['type'] = 'R';
7235                                 }
7236                         }
7237
7238                         // 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.
7239                         $prevlevel = -1;
7240                         $levcount = 0;
7241                         for ($i=0; $i < $numchars; $i++) {
7242                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7243                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7244                                                 $chardata[$i]['type'] = 'EN';
7245                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7246                                                 $chardata[$i]['type'] = 'EN';
7247                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
7248                                                 $chardata[$i]['type'] = 'AN';
7249                                         }
7250                                 }
7251                                 if ($chardata[$i]['level'] != $prevlevel) {
7252                                         $levcount = 0;
7253                                 } else {
7254                                         $levcount++;
7255                                 }
7256                                 $prevlevel = $chardata[$i]['level'];
7257                         }
7258
7259                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
7260                         $prevlevel = -1;
7261                         $levcount = 0;
7262                         for ($i=0; $i < $numchars; $i++) {
7263                                 if ($chardata[$i]['type'] == 'ET') {
7264                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
7265                                                 $chardata[$i]['type'] = 'EN';
7266                                         } else {
7267                                                 $j = $i+1;
7268                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
7269                                                         if ($chardata[$j]['type'] == 'EN') {
7270                                                                 $chardata[$i]['type'] = 'EN';
7271                                                                 break;
7272                                                         } elseif ($chardata[$j]['type'] != 'ET') {
7273                                                                 break;
7274                                                         }
7275                                                         $j++;
7276                                                 }
7277                                         }
7278                                 }
7279                                 if ($chardata[$i]['level'] != $prevlevel) {
7280                                         $levcount = 0;
7281                                 } else {
7282                                         $levcount++;
7283                                 }
7284                                 $prevlevel = $chardata[$i]['level'];
7285                         }
7286
7287                         // W6. Otherwise, separators and terminators change to Other Neutral.
7288                         $prevlevel = -1;
7289                         $levcount = 0;
7290                         for ($i=0; $i < $numchars; $i++) {
7291                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
7292                                         $chardata[$i]['type'] = 'ON';
7293                                 }
7294                                 if ($chardata[$i]['level'] != $prevlevel) {
7295                                         $levcount = 0;
7296                                 } else {
7297                                         $levcount++;
7298                                 }
7299                                 $prevlevel = $chardata[$i]['level'];
7300                         }
7301
7302                         //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.
7303                         $prevlevel = -1;
7304                         $levcount = 0;
7305                         for ($i=0; $i < $numchars; $i++) {
7306                                 if ($chardata[$i]['char'] == 'EN') {
7307                                         for ($j=$levcount; $j >= 0; $j--) {
7308                                                 if ($chardata[$j]['type'] == 'L') {
7309                                                         $chardata[$i]['type'] = 'L';
7310                                                 } elseif ($chardata[$j]['type'] == 'R') {
7311                                                         break;
7312                                                 }
7313                                         }
7314                                 }
7315                                 if ($chardata[$i]['level'] != $prevlevel) {
7316                                         $levcount = 0;
7317                                 } else {
7318                                         $levcount++;
7319                                 }
7320                                 $prevlevel = $chardata[$i]['level'];
7321                         }
7322
7323                         // 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.
7324                         $prevlevel = -1;
7325                         $levcount = 0;
7326                         for ($i=0; $i < $numchars; $i++) {
7327                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7328                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7329                                                 $chardata[$i]['type'] = 'L';
7330                                         } elseif (($chardata[$i]['type'] == 'N') AND
7331                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7332                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7333                                                 $chardata[$i]['type'] = 'R';
7334                                         } elseif ($chardata[$i]['type'] == 'N') {
7335                                                 // N2. Any remaining neutrals take the embedding direction
7336                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7337                                         }
7338                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7339                                         // first char
7340                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7341                                                 $chardata[$i]['type'] = 'L';
7342                                         } elseif (($chardata[$i]['type'] == 'N') AND
7343                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
7344                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7345                                                 $chardata[$i]['type'] = 'R';
7346                                         } elseif ($chardata[$i]['type'] == 'N') {
7347                                                 // N2. Any remaining neutrals take the embedding direction
7348                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7349                                         }
7350                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
7351                                         //last char
7352                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
7353                                                 $chardata[$i]['type'] = 'L';
7354                                         } elseif (($chardata[$i]['type'] == 'N') AND
7355                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7356                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
7357                                                 $chardata[$i]['type'] = 'R';
7358                                         } elseif ($chardata[$i]['type'] == 'N') {
7359                                                 // N2. Any remaining neutrals take the embedding direction
7360                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7361                                         }
7362                                 } elseif ($chardata[$i]['type'] == 'N') {
7363                                         // N2. Any remaining neutrals take the embedding direction
7364                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
7365                                 }
7366                                 if ($chardata[$i]['level'] != $prevlevel) {
7367                                         $levcount = 0;
7368                                 } else {
7369                                         $levcount++;
7370                                 }
7371                                 $prevlevel = $chardata[$i]['level'];
7372                         }
7373
7374                         // 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.
7375                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
7376                         for ($i=0; $i < $numchars; $i++) {
7377                                 $odd = $chardata[$i]['level'] % 2;
7378                                 if ($odd) {
7379                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7380                                                 $chardata[$i]['level'] += 1;
7381                                         }
7382                                 } else {
7383                                         if ($chardata[$i]['type'] == 'R') {
7384                                                 $chardata[$i]['level'] += 1;
7385                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7386                                                 $chardata[$i]['level'] += 2;
7387                                         }
7388                                 }
7389                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
7390                         }
7391
7392                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
7393                         //      1. Segment separators,
7394                         //      2. Paragraph separators,
7395                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
7396                         //      4. Any sequence of white space characters at the end of the line.
7397                         for ($i=0; $i < $numchars; $i++) {
7398                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
7399                                         $chardata[$i]['level'] = $pel;
7400                                 } elseif ($chardata[$i]['type'] == 'WS') {
7401                                         $j = $i+1;
7402                                         while ($j < $numchars) {
7403                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
7404                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
7405                                                         $chardata[$i]['level'] = $pel;
7406                                                         break;
7407                                                 } elseif ($chardata[$j]['type'] != 'WS') {
7408                                                         break;
7409                                                 }
7410                                                 $j++;
7411                                         }
7412                                 }
7413                         }
7414
7415                         // Arabic Shaping
7416                         // 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.
7417                         if ($arabic) {
7418                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
7419                                 $alfletter = array(1570,1571,1573,1575);
7420                                 $chardata2 = $chardata;
7421                                 $laaletter = false;
7422                                 $charAL = array();
7423                                 $x = 0;
7424                                 for ($i=0; $i < $numchars; $i++) {
7425                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
7426                                                 $charAL[$x] = $chardata[$i];
7427                                                 $charAL[$x]['i'] = $i;
7428                                                 $chardata[$i]['x'] = $x;
7429                                                 $x++;
7430                                         }
7431                                 }
7432                                 $numAL = $x;
7433                                 for ($i=0; $i < $numchars; $i++) {
7434                                         $thischar = $chardata[$i];
7435                                         if ($i > 0) {
7436                                                 $prevchar = $chardata[($i-1)];
7437                                         } else {
7438                                                 $prevchar = false;
7439                                         }
7440                                         if (($i+1) < $numchars) {
7441                                                 $nextchar = $chardata[($i+1)];
7442                                         } else {
7443                                                 $nextchar = false;
7444                                         }
7445                                         if ($unicode[$thischar['char']] == 'AL') {
7446                                                 $x = $thischar['x'];
7447                                                 if ($x > 0) {
7448                                                         $prevchar = $charAL[($x-1)];
7449                                                 } else {
7450                                                         $prevchar = false;
7451                                                 }
7452                                                 if (($x+1) < $numAL) {
7453                                                         $nextchar = $charAL[($x+1)];
7454                                                 } else {
7455                                                         $nextchar = false;
7456                                                 }
7457                                                 // if laa letter
7458                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
7459                                                         $arabicarr = $laa_array;
7460                                                         $laaletter = true;
7461                                                         if ($x > 1) {
7462                                                                 $prevchar = $charAL[($x-2)];
7463                                                         } else {
7464                                                                 $prevchar = false;
7465                                                         }
7466                                                 } else {
7467                                                         $arabicarr = $unicode_arlet;
7468                                                         $laaletter = false;
7469                                                 }
7470                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
7471                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7472                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7473                                                         ($prevchar['type'] == $thischar['type']) AND
7474                                                         ($nextchar['type'] == $thischar['type']) AND
7475                                                         ($nextchar['char'] != 1567)) {
7476                                                         if (in_array($prevchar['char'], $endedletter)) {
7477                                                                 if (isset($arabicarr[$thischar['char']][2])) {
7478                                                                         // initial
7479                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7480                                                                 }
7481                                                         } else {
7482                                                                 if (isset($arabicarr[$thischar['char']][3])) {
7483                                                                         // medial
7484                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
7485                                                                 }
7486                                                         }
7487                                                 } elseif (($nextchar !== false) AND
7488                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7489                                                         ($nextchar['type'] == $thischar['type']) AND
7490                                                         ($nextchar['char'] != 1567)) {
7491                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
7492                                                                 // initial
7493                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7494                                                         }
7495                                                 } elseif ((($prevchar !== false) AND
7496                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7497                                                         ($prevchar['type'] == $thischar['type'])) OR
7498                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
7499                                                         // final
7500                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
7501                                                                 ($chardata[$i-1]['char'] == 1604) AND
7502                                                                 ($chardata[$i-2]['char'] == 1604)) {
7503                                                                 //Allah Word
7504                                                                 // mark characters to delete with false
7505                                                                 $chardata2[$i-2]['char'] = false;
7506                                                                 $chardata2[$i-1]['char'] = false;
7507                                                                 $chardata2[$i]['char'] = 65010;
7508                                                         } else {
7509                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
7510                                                                         if (isset($arabicarr[$thischar['char']][0])) {
7511                                                                                 // isolated
7512                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7513                                                                         }
7514                                                                 } else {
7515                                                                         if (isset($arabicarr[$thischar['char']][1])) {
7516                                                                                 // final
7517                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
7518                                                                         }
7519                                                                 }
7520                                                         }
7521                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
7522                                                         // isolated
7523                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7524                                                 }
7525                                                 // if laa letter
7526                                                 if ($laaletter) {
7527                                                         // mark characters to delete with false
7528                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
7529                                                 }
7530                                         } // end if AL (Arabic Letter)
7531                                 } // end for each char
7532                                 /*
7533                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594.
7534                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
7535                                  */
7536                                 $cw = &$this->CurrentFont['cw'];
7537                                 for ($i=0; $i < ($numchars-1); $i++) {
7538                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
7539                                                 // check if the subtitution font is defined on current font
7540                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
7541                                                         $chardata2[$i]['char'] = false;
7542                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
7543                                                 }
7544                                         }
7545                                 }
7546                                 // remove marked characters
7547                                 foreach($chardata2 as $key => $value) {
7548                                         if ($value['char'] === false) {
7549                                                 unset($chardata2[$key]);
7550                                         }
7551                                 }
7552                                 $chardata = array_values($chardata2);
7553                                 $numchars = count($chardata);
7554                                 unset($chardata2);
7555                                 unset($arabicarr);
7556                                 unset($laaletter);
7557                                 unset($charAL);
7558                         }
7559
7560                         // 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.
7561                         for ($j=$maxlevel; $j > 0; $j--) {
7562                                 $ordarray = Array();
7563                                 $revarr = Array();
7564                                 $onlevel = false;
7565                                 for ($i=0; $i < $numchars; $i++) {
7566                                         if ($chardata[$i]['level'] >= $j) {
7567                                                 $onlevel = true;
7568                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
7569                                                         // 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.
7570                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
7571                                                 }
7572                                                 $revarr[] = $chardata[$i];
7573                                         } else {
7574                                                 if ($onlevel) {
7575                                                         $revarr = array_reverse($revarr);
7576                                                         $ordarray = array_merge($ordarray, $revarr);
7577                                                         $revarr = Array();
7578                                                         $onlevel = false;
7579                                                 }
7580                                                 $ordarray[] = $chardata[$i];
7581                                         }
7582                                 }
7583                                 if ($onlevel) {
7584                                         $revarr = array_reverse($revarr);
7585                                         $ordarray = array_merge($ordarray, $revarr);
7586                                 }
7587                                 $chardata = $ordarray;
7588                         }
7589
7590                         $ordarray = array();
7591                         for ($i=0; $i < $numchars; $i++) {
7592                                 $ordarray[] = $chardata[$i]['char'];
7593                         }
7594
7595                         return $ordarray;
7596                 }
7597
7598                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
7599
7600                 /*
7601                 * Adds a bookmark.
7602                 * @param string $txt bookmark description.
7603                 * @param int $level bookmark level (minimum value is 0).
7604                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
7605                 * @access public
7606                 * @author Olivier Plathey, Nicola Asuni
7607                 * @since 2.1.002 (2008-02-12)
7608                 */
7609                 function Bookmark($txt, $level=0, $y=-1) {
7610                         if ($level < 0) {
7611                                 $level = 0;
7612                         }
7613                         if (isset($this->outlines[0])) {
7614                                 $lastoutline = end($this->outlines);
7615                                 $maxlevel = $lastoutline['l'] + 1;
7616                         } else {
7617                                 $maxlevel = 0;
7618                         }
7619                         if ($level > $maxlevel) {
7620                                 $level = $maxlevel;
7621                         }
7622                         if ($y == -1) {
7623                                 $y = $this->GetY();
7624                         }
7625                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->PageNo());
7626                 }
7627
7628                 /*
7629                 * Create a bookmark PDF string.
7630                 * @access protected
7631                 * @author Olivier Plathey, Nicola Asuni
7632                 * @since 2.1.002 (2008-02-12)
7633                 */
7634                 function _putbookmarks() {
7635                         $nb = count($this->outlines);
7636                         if ($nb == 0) {
7637                                 return;
7638                         }
7639                         $lru = array();
7640                         $level = 0;
7641                         foreach($this->outlines as $i => $o) {
7642                                 if ($o['l'] > 0) {
7643                                         $parent = $lru[($o['l'] - 1)];
7644                                         //Set parent and last pointers
7645                                         $this->outlines[$i]['parent'] = $parent;
7646                                         $this->outlines[$parent]['last'] = $i;
7647                                         if ($o['l'] > $level) {
7648                                                 //Level increasing: set first pointer
7649                                                 $this->outlines[$parent]['first'] = $i;
7650                                         }
7651                                 } else {
7652                                         $this->outlines[$i]['parent'] = $nb;
7653                                 }
7654                                 if (($o['l'] <= $level) AND ($i > 0)) {
7655                                         //Set prev and next pointers
7656                                         $prev = $lru[$o['l']];
7657                                         $this->outlines[$prev]['next'] = $i;
7658                                         $this->outlines[$i]['prev'] = $prev;
7659                                 }
7660                                 $lru[$o['l']] = $i;
7661                                 $level = $o['l'];
7662                         }
7663                         //Outline items
7664                         $n = $this->n + 1;
7665                         foreach($this->outlines as $i => $o) {
7666                                 $this->_newobj();
7667                                 $this->_out('<</Title '.$this->_textstring($o['t']));
7668                                 $this->_out('/Parent '.($n+$o['parent']).' 0 R');
7669                                 if (isset($o['prev']))
7670                                 $this->_out('/Prev '.($n+$o['prev']).' 0 R');
7671                                 if (isset($o['next']))
7672                                 $this->_out('/Next '.($n+$o['next']).' 0 R');
7673                                 if (isset($o['first']))
7674                                 $this->_out('/First '.($n+$o['first']).' 0 R');
7675                                 if (isset($o['last']))
7676                                 $this->_out('/Last '.($n+$o['last']).' 0 R');
7677                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]', 1+2*$o['p'], ($this->h-$o['y'])*$this->k));
7678                                 $this->_out('/Count 0>>');
7679                                 $this->_out('endobj');
7680                         }
7681                         //Outline root
7682                         $this->_newobj();
7683                         $this->OutlineRoot=$this->n;
7684                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
7685                         $this->_out('/Last '.($n+$lru[0]).' 0 R>>');
7686                         $this->_out('endobj');
7687                 }
7688
7689
7690                 // --- JAVASCRIPT - FORMS ------------------------------
7691
7692                 /*
7693                 * Adds a javascript
7694                 * @access public
7695                 * @author Johannes Güntert, Nicola Asuni
7696                 * @since 2.1.002 (2008-02-12)
7697                 */
7698                 function IncludeJS($script) {
7699                         $this->javascript .= $script;
7700                 }
7701
7702                 /*
7703                 * Create a javascript PDF string.
7704                 * @access protected
7705                 * @author Johannes Güntert, Nicola Asuni
7706                 * @since 2.1.002 (2008-02-12)
7707                 */
7708                 function _putjavascript() {
7709                         if (empty($this->javascript)) {
7710                                 return;
7711                         }
7712                         // the following two lines are uded to avoid form fields duplication after saving
7713                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
7714                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
7715                         $this->_newobj();
7716                         $this->n_js = $this->n;
7717                         $this->_out('<<');
7718                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
7719                         $this->_out('>>');
7720                         $this->_out('endobj');
7721                         $this->_newobj();
7722                         $this->_out('<<');
7723                         $this->_out('/S /JavaScript');
7724                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
7725                         $this->_out('>>');
7726                         $this->_out('endobj');
7727                 }
7728
7729                 /*
7730                 * Convert color to javascript color.
7731                 * @param string $color color name or #RRGGBB
7732                 * @access protected
7733                 * @author Denis Van Nuffelen, Nicola Asuni
7734                 * @since 2.1.002 (2008-02-12)
7735                 */
7736                 function _JScolor($color) {
7737                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
7738                         if (substr($color,0,1) == '#') {
7739                                 return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
7740                         }
7741                         if (!in_array($color,$aColors)) {
7742                                 $this->Error('Invalid color: '.$color);
7743                         }
7744                         return 'color.'.$color;
7745                 }
7746
7747                 /*
7748                 * Adds a javascript form field.
7749                 * @param string $type field type
7750                 * @param string $name field name
7751                 * @param int $x horizontal position
7752                 * @param int $y vertical position
7753                 * @param int $w width
7754                 * @param int $h height
7755                 * @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>
7756                 * @access protected
7757                 * @author Denis Van Nuffelen, Nicola Asuni
7758                 * @since 2.1.002 (2008-02-12)
7759                 */
7760                 function _addfield($type, $name, $x, $y, $w, $h, $prop) {
7761                         // the followind avoid fields duplication after saving the document
7762                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
7763                         $k = $this->k;
7764                         $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";
7765                         $this->javascript .= "f".$name.".textSize=".$this->FontSizePt.";\n";
7766                         while (list($key, $val) = each($prop)) {
7767                                 if (strcmp(substr($key,-5),"Color") == 0) {
7768                                         $val = $this->_JScolor($val);
7769                                 } else {
7770                                         $val = "'".$val."'";
7771                                 }
7772                                 $this->javascript .= "f".$name.".".$key."=".$val.";\n";
7773                         }
7774                         $this->x += $w;
7775                         $this->javascript .= "}";
7776                 }
7777
7778                 /*
7779                 * Creates a text field
7780                 * @param string $name field name
7781                 * @param int $w width
7782                 * @param int $h height
7783                 * @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>
7784                 * @access public
7785                 * @author Denis Van Nuffelen, Nicola Asuni
7786                 * @since 2.1.002 (2008-02-12)
7787                 */
7788                 function TextField($name, $w, $h, $prop=array()) {
7789                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
7790                 }
7791
7792                 /*
7793                 * Creates a RadioButton field
7794                 * @param string $name field name
7795                 * @param int $w width
7796                 * @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>
7797                 * @access public
7798                 * @author Nicola Asuni
7799                 * @since 2.2.003 (2008-03-03)
7800                 */
7801                 function RadioButton($name, $w, $prop=array()) {
7802                         if (!isset($prop['strokeColor'])) {
7803                                 $prop['strokeColor']='black';
7804                         }
7805                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
7806                 }
7807
7808                 /*
7809                 * Creates a List-box field
7810                 * @param string $name field name
7811                 * @param int $w width
7812                 * @param int $h height
7813                 * @param array $values array containing the list of values.
7814                 * @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>
7815                 * @access public
7816                 * @author Nicola Asuni
7817                 * @since 2.2.003 (2008-03-03)
7818                 */
7819                 function ListBox($name, $w, $h, $values, $prop=array()) {
7820                         if (!isset($prop['strokeColor'])) {
7821                                 $prop['strokeColor'] = 'ltGray';
7822                         }
7823                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
7824                         $s = '';
7825                         foreach($values as $value) {
7826                                 $s .= "'".addslashes($value)."',";
7827                         }
7828                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7829                 }
7830
7831                 /*
7832                 * Creates a Combo-box field
7833                 * @param string $name field name
7834                 * @param int $w width
7835                 * @param int $h height
7836                 * @param array $values array containing the list of values.
7837                 * @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>
7838                 * @access public
7839                 * @author Denis Van Nuffelen, Nicola Asuni
7840                 * @since 2.1.002 (2008-02-12)
7841                 */
7842                 function ComboBox($name, $w, $h, $values, $prop=array()) {
7843                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
7844                         $s = '';
7845                         foreach($values as $value) {
7846                                 $s .= "'".addslashes($value)."',";
7847                         }
7848                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7849                 }
7850
7851                 /*
7852                 * Creates a CheckBox field
7853                 * @param string $name field name
7854                 * @param int $w width
7855                 * @param boolean $checked define the initial state (default = false).
7856                 * @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>
7857                 * @access public
7858                 * @author Denis Van Nuffelen, Nicola Asuni
7859                 * @since 2.1.002 (2008-02-12)
7860                 */
7861                 function CheckBox($name, $w, $checked=false, $prop=array()) {
7862                         $prop['value'] = ($checked ? 'Yes' : 'Off');
7863                         if (!isset($prop['strokeColor'])) {
7864                                 $prop['strokeColor'] = 'black';
7865                         }
7866                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
7867                 }
7868
7869                 /*
7870                 * Creates a button field
7871                 * @param string $name field name
7872                 * @param int $w width
7873                 * @param int $h height
7874                 * @param string $caption caption.
7875                 * @param string $action action triggered by the button (JavaScript code).
7876                 * @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>
7877                 * @access public
7878                 * @author Denis Van Nuffelen, Nicola Asuni
7879                 * @since 2.1.002 (2008-02-12)
7880                 */
7881                 function Button($name, $w, $h, $caption, $action, $prop=array()) {
7882                         if (!isset($prop['strokeColor'])) {
7883                                 $prop['strokeColor'] = 'black';
7884                         }
7885                         if (!isset($prop['borderStyle'])) {
7886                                 $prop['borderStyle'] = 'beveled';
7887                         }
7888                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
7889                         $this->javascript .= "f".$name.".buttonSetCaption('".addslashes($caption)."');\n";
7890                         $this->javascript .= "f".$name.".setAction('MouseUp','".addslashes($action)."');\n";
7891                         $this->javascript .= "f".$name.".highlight='push';\n";
7892                         $this->javascript .= "f".$name.".print=false;\n";
7893                 }
7894
7895                 // END JAVASCRIPT - FORMS ------------------------------
7896
7897                 /*
7898                 * Enable Write permissions for PDF Reader.
7899                 * @access protected
7900                 * @author Nicola Asuni
7901                 * @since 2.9.000 (2008-03-26)
7902                 */
7903                 function _putuserrights() {
7904                         if (!$this->ur) {
7905                                 return;
7906                         }
7907                         $this->_out('/Perms');
7908                         $this->_out('<<');
7909                         $this->_out('/UR3');
7910                         $this->_out('<<');
7911                         //$this->_out('/SubFilter/adbe.pkcs7.detached/Filter/Adobe.PPKLite/Contents');
7912                         //$this->_out('<0>');
7913                         //$this->_out('/ByteRange[0 3]');
7914                         $this->_out('/M '.$this->_datestring('D:'.date('YmdHis')));
7915                         $this->_out('/Name(TCPDF)');
7916                         $this->_out('/Reference[');
7917                         $this->_out('<<');
7918                         $this->_out('/TransformParams');
7919                         $this->_out('<<');
7920                         $this->_out('/Type/TransformParams');
7921                         $this->_out('/V/2.2');
7922                         if (!empty($this->ur_document)) {
7923                                 $this->_out('/Document['.$this->ur_document.']');
7924                         }
7925                         if (!empty($this->ur_annots)) {
7926                                 $this->_out('/Annots['.$this->ur_annots.']');
7927                         }
7928                         if (!empty($this->ur_form)) {
7929                                 $this->_out('/Form['.$this->ur_form.']');
7930                         }
7931                         if (!empty($this->ur_signature)) {
7932                                 $this->_out('/Signature['.$this->ur_signature.']');
7933                         }
7934                         $this->_out('>>');
7935                         $this->_out('/TransformMethod/UR3');
7936                         $this->_out('/Type/SigRef');
7937                         $this->_out('>>');
7938                         $this->_out(']');
7939                         $this->_out('/Type/Sig');
7940                         $this->_out('>>');
7941                         $this->_out('>>');
7942                 }
7943
7944                 /*
7945                 * Set User's Rights for PDF Reader
7946                 * Check the PDF Reference 8.7.1 Transform Methods,
7947                 * Table 8.105 Entries in the UR transform parameters dictionary
7948                 * @param boolean $enable if true enable user's rights on PDF reader
7949                 * @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.
7950                 * @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.
7951                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
7952                 * @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.
7953                 * @access public
7954                 * @author Nicola Asuni
7955                 * @since 2.9.000 (2008-03-26)
7956                 */
7957                 function setUserRights(
7958                                 $enable=true,
7959                                 $document="/FullSave",
7960                                 $annots="/Create/Delete/Modify/Copy/Import/Export",
7961                                 $form="/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate",
7962                                 $signature="/Modify") {
7963                         $this->ur = $enable;
7964                         $this->ur_document = $document;
7965                         $this->ur_annots = $annots;
7966                         $this->ur_form = $form;
7967                         $this->ur_signature = $signature;
7968                 }
7969
7970                 /*
7971                 * Create a new page group.
7972                 * NOTE: call this function before calling AddPage()
7973                 * @access public
7974                 * @since 3.0.000 (2008-03-27)
7975                 */
7976                 function startPageGroup() {
7977                         $this->newpagegroup = true;
7978                 }
7979
7980                 /*
7981                 * Return the current page in the group.
7982                 * @return current page in the group
7983                 * @access public
7984                 * @since 3.0.000 (2008-03-27)
7985                 */
7986                 function getGroupPageNo() {
7987                         return $this->pagegroups[$this->currpagegroup];
7988                 }
7989
7990                 /*
7991                 * Return the alias of the current page group
7992         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
7993                 * (will be replaced by the total number of pages in this group).
7994                 * @return alias of the current page group
7995                 * @access public
7996                 * @since 3.0.000 (2008-03-27)
7997                 */
7998                 function getPageGroupAlias() {
7999                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
8000                                 return "{".$this->currpagegroup."}";
8001             }
8002                         return $this->currpagegroup;
8003                 }
8004
8005                 /*
8006                 * Put visibility settings.
8007                 * @access protected
8008                 * @since 3.0.000 (2008-03-27)
8009                 */
8010                 function _putocg() {
8011                         $this->_newobj();
8012                         $this->n_ocg_print = $this->n;
8013                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
8014                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
8015                         $this->_out('endobj');
8016                         $this->_newobj();
8017                         $this->n_ocg_view=$this->n;
8018                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
8019                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
8020                         $this->_out('endobj');
8021                 }
8022
8023                 /*
8024                 * Set the visibility of the successive elements.
8025                 * This can be useful, for instance, to put a background
8026                 * image or color that will show on screen but won't print.
8027                 * @param string $v visibility mode. Legal values are: all, print, screen.
8028                 * @access public
8029                 * @since 3.0.000 (2008-03-27)
8030                 */
8031                 function setVisibility($v) {
8032                         if ($this->openMarkedContent) {
8033                                 // close existing open marked-content
8034                                 $this->_out('EMC');
8035                                 $this->openMarkedContent = false;
8036                         }
8037                         switch($v) {
8038                                 case "print": {
8039                                         $this->_out('/OC /OC1 BDC');
8040                                         $this->openMarkedContent = true;
8041                                         break;
8042                                 }
8043                                 case "screen": {
8044                                         $this->_out('/OC /OC2 BDC');
8045                                         $this->openMarkedContent = true;
8046                                         break;
8047                                 }
8048                                 case "all": {
8049                                         $this->_out('');
8050                                         break;
8051                                 }
8052                                 default: {
8053                                         $this->Error('Incorrect visibility: '.$v);
8054                                         break;
8055                                 }
8056                         }
8057                         $this->visibility = $v;
8058                 }
8059
8060                 /*
8061                 * Add transparency parameters to the current extgstate
8062                 * @param array $params parameters
8063                 * @return the number of extgstates
8064                 * @access protected
8065                 * @since 3.0.000 (2008-03-27)
8066                 */
8067                 function addExtGState($parms) {
8068                         $n = count($this->extgstates) + 1;
8069                         $this->extgstates[$n]['parms'] = $parms;
8070                         return $n;
8071                 }
8072
8073                 /*
8074                 * Add an extgstate
8075                 * @param array $gs extgstate
8076                 * @access protected
8077                 * @since 3.0.000 (2008-03-27)
8078                 */
8079                 function setExtGState($gs) {
8080                         $this->_out(sprintf('/GS%d gs', $gs));
8081                 }
8082
8083                 /*
8084                 * Put extgstates for object transparency
8085                 * @param array $gs extgstate
8086                 * @access protected
8087                 * @since 3.0.000 (2008-03-27)
8088                 */
8089                 function _putextgstates() {
8090                         $ne = count($this->extgstates);
8091                         for ($i = 1; $i <= $ne; $i++) {
8092                                 $this->_newobj();
8093                                 $this->extgstates[$i]['n'] = $this->n;
8094                                 $this->_out('<</Type /ExtGState');
8095                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
8096                                         $this->_out('/'.$k.' '.$v);
8097                                 }
8098                                 $this->_out('>>');
8099                                 $this->_out('endobj');
8100                         }
8101                 }
8102
8103                 /*
8104                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
8105                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
8106                 * @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
8107                 * @access public
8108                 * @since 3.0.000 (2008-03-27)
8109                 */
8110                 function setAlpha($alpha, $bm='Normal') {
8111                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
8112                         $this->setExtGState($gs);
8113                 }
8114
8115                 /*
8116                 * Set the default JPEG compression quality (1-100)
8117                 * @param int $quality JPEG quality, integer between 1 and 100
8118                 * @access public
8119                 * @since 3.0.000 (2008-03-27)
8120                 */
8121                 function setJPEGQuality($quality) {
8122                         if (($quality < 1) OR ($quality > 100)) {
8123                                 $quality = 75;
8124                         }
8125                         $this->jpeg_quality = intval($quality);
8126                 }
8127
8128                 /*
8129                 * Set the default number of columns in a row for HTML tables.
8130                 * @param int $cols number of columns
8131                 * @access public
8132                 * @since 3.0.014 (2008-06-04)
8133                 */
8134                 function setDefaultTableColumns($cols=4) {
8135                         $this->default_table_columns = intval($cols);
8136                 }
8137
8138                 /*
8139                 * Set the height of cell repect font height.
8140                 * @param int $h cell proportion respect font height (typical value = 1.25).
8141                 * @access public
8142                 * @since 3.0.014 (2008-06-04)
8143                 */
8144                 function setCellHeightRatio($h) {
8145                         $this->cell_height_ratio = $h;
8146                 }
8147
8148                 /*
8149                 * return the height of cell repect font height.
8150                 * @access public
8151                 * @since 4.0.012 (2008-07-24)
8152                 */
8153                 function getCellHeightRatio() {
8154                         return $this->cell_height_ratio;
8155                 }
8156
8157                 /*
8158                 * Set the PDF version (check PDF reference for valid values).
8159                 * Default value is 1.t
8160                 * @access public
8161                 * @since 3.1.000 (2008-06-09)
8162                 */
8163                 function setPDFVersion($version="1.7") {
8164                         $this->PDFVersion = $version;
8165                 }
8166
8167                 /*
8168                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
8169                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
8170                 * <ul>
8171                 * <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>
8172                 * <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>
8173                 * <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>
8174                 * <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>
8175                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
8176                 * <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>
8177                 * <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>
8178                 * <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>
8179                 * <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>
8180                 * <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>
8181                 * <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>
8182                 * <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>
8183                 * <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>
8184                 * <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>
8185                 * <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>
8186                 * <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>
8187                 * </ul>
8188                 * @param array $preferences array of options.
8189                 * @author Nicola Asuni
8190                 * @access public
8191                 * @since 3.1.000 (2008-06-09)
8192                 */
8193                 function setViewerPreferences($preferences) {
8194                         $this->viewer_preferences = $preferences;
8195                 }
8196
8197                 /**
8198                 * Paints a linear colour gradient.
8199                 * @param float $x abscissa of the top left corner of the rectangle.
8200                 * @param float $y ordinate of the top left corner of the rectangle.
8201                 * @param float $w width of the rectangle.
8202                 * @param float $h height of the rectangle.
8203                 * @param array $col1 first color (RGB components).
8204                 * @param array $col2 second color (RGB components).
8205                 * @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).
8206                 * @author Andreas Würmser, Nicola Asuni
8207                 * @since 3.1.000 (2008-06-09)
8208                 * @access public
8209                 */
8210                 function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
8211                         $this->Clip($x, $y, $w, $h);
8212                         $this->Gradient(2, $col1, $col2, $coords);
8213                 }
8214
8215                 /**
8216                 * Paints a radial colour gradient.
8217                 * @param float $x abscissa of the top left corner of the rectangle.
8218                 * @param float $y ordinate of the top left corner of the rectangle.
8219                 * @param float $w width of the rectangle.
8220                 * @param float $h height of the rectangle.
8221                 * @param array $col1 first color (RGB components).
8222                 * @param array $col2 second color (RGB components).
8223                 * @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.
8224                 * @author Andreas Würmser, Nicola Asuni
8225                 * @since 3.1.000 (2008-06-09)
8226                 * @access public
8227                 */
8228                 function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
8229                         $this->Clip($x, $y, $w, $h);
8230                         $this->Gradient(3, $col1, $col2, $coords);
8231                 }
8232
8233                 /**
8234                 * Paints a coons patch mesh.
8235                 * @param float $x abscissa of the top left corner of the rectangle.
8236                 * @param float $y ordinate of the top left corner of the rectangle.
8237                 * @param float $w width of the rectangle.
8238                 * @param float $h height of the rectangle.
8239                 * @param array $col1 first color (lower left corner) (RGB components).
8240                 * @param array $col2 second color (lower right corner) (RGB components).
8241                 * @param array $col3 third color (upper right corner) (RGB components).
8242                 * @param array $col4 fourth color (upper left corner) (RGB components).
8243                 * @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>
8244                 * @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
8245                 * @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
8246                 * @author Andreas Würmser, Nicola Asuni
8247                 * @since 3.1.000 (2008-06-09)
8248                 * @access public
8249                 */
8250                 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) {
8251                         $this->Clip($x, $y, $w, $h);
8252                         $n = count($this->gradients) + 1;
8253                         $this->gradients[$n]['type'] = 6; //coons patch mesh
8254                         //check the coords array if it is the simple array or the multi patch array
8255                         if (!isset($coords[0]['f'])){
8256                                 //simple array -> convert to multi patch array
8257                                 if (!isset($col1[1])) {
8258                                         $col1[1] = $col1[2] = $col1[0];
8259                                 }
8260                                 if (!isset($col2[1])) {
8261                                         $col2[1] = $col2[2] = $col2[0];
8262                                 }
8263                                 if (!isset($col3[1])) {
8264                                         $col3[1] = $col3[2] = $col3[0];
8265                                 }
8266                                 if (!isset($col4[1])) {
8267                                         $col4[1] = $col4[2] = $col4[0];
8268                                 }
8269                                 $patch_array[0]['f'] = 0;
8270                                 $patch_array[0]['points'] = $coords;
8271                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
8272                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
8273                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
8274                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
8275                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
8276                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
8277                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
8278                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
8279                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
8280                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
8281                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
8282                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
8283                         } else {
8284                                 //multi patch array
8285                                 $patch_array = $coords;
8286                         }
8287                         $bpcd = 65535; //16 BitsPerCoordinate
8288                         //build the data stream
8289                         $this->gradients[$n]['stream'] = "";
8290                         for($i=0; $i < count($patch_array); $i++) {
8291                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
8292                                 for($j=0; $j < count($patch_array[$i]['points']); $j++) {
8293                                         //each point as 16 bit
8294                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j]-$coords_min)/($coords_max-$coords_min))*$bpcd;
8295                                         if ($patch_array[$i]['points'][$j] < 0) {
8296                                                 $patch_array[$i]['points'][$j] = 0;
8297                                         }
8298                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
8299                                                 $patch_array[$i]['points'][$j] = $bpcd;
8300                                         }
8301                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]/256));
8302                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]%256));
8303                                 }
8304                                 for($j=0; $j < count($patch_array[$i]['colors']); $j++) {
8305                                         //each color component as 8 bit
8306                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
8307                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
8308                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
8309                                 }
8310                         }
8311                         //paint the gradient
8312                         $this->_out('/Sh'.$n.' sh');
8313                         //restore previous Graphic State
8314                         $this->_out('Q');
8315                 }
8316
8317                 /**
8318                 * Set a rectangular clipping area.
8319                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
8320                 * @param float $y ordinate of the top left corner of the rectangle.
8321                 * @param float $w width of the rectangle.
8322                 * @param float $h height of the rectangle.
8323                 * @author Andreas Würmser, Nicola Asuni
8324                 * @since 3.1.000 (2008-06-09)
8325                 * @access protected
8326                 */
8327                 function Clip($x, $y, $w, $h){
8328                         if ($this->rtl) {
8329                                 $x = $this->w - $x - $w;
8330                         }
8331                         //save current Graphic State
8332                         $s = 'q';
8333                         //set clipping area
8334                         $s .= sprintf(' %.2f %.2f %.2f %.2f re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
8335                         //set up transformation matrix for gradient
8336                         $s .= sprintf(' %.3f 0 0 %.3f %.3f %.3f cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
8337                         $this->_out($s);
8338                 }
8339
8340                 /**
8341                 * Output gradient.
8342                 * @param int $type type of gradient.
8343                 * @param array $col1 first color (RGB components).
8344                 * @param array $col2 second color (RGB components).
8345                 * @param array $coords array of coordinates.
8346                 * @author Andreas Würmser, Nicola Asuni
8347                 * @since 3.1.000 (2008-06-09)
8348                 * @access protected
8349                 */
8350                 function Gradient($type, $col1, $col2, $coords){
8351                         $n = count($this->gradients) + 1;
8352                         $this->gradients[$n]['type'] = $type;
8353                         if (!isset($col1[1])) {
8354                                 $col1[1]=$col1[2]=$col1[0];
8355                         }
8356                         $this->gradients[$n]['col1'] = sprintf('%.3f %.3f %.3f', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
8357                         if (!isset($col2[1])) {
8358                                 $col2[1] = $col2[2] = $col2[0];
8359                         }
8360                         $this->gradients[$n]['col2'] = sprintf('%.3f %.3f %.3f', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
8361                         $this->gradients[$n]['coords'] = $coords;
8362                         //paint the gradient
8363                         $this->_out('/Sh'.$n.' sh');
8364                         //restore previous Graphic State
8365                         $this->_out('Q');
8366                 }
8367
8368                 /**
8369                 * Output shaders.
8370                 * @author Andreas Würmser, Nicola Asuni
8371                 * @since 3.1.000 (2008-06-09)
8372                 * @access protected
8373                 */
8374                 function _putshaders() {
8375                         foreach($this->gradients as $id => $grad) {
8376                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
8377                                         $this->_newobj();
8378                                         $this->_out('<<');
8379                                         $this->_out('/FunctionType 2');
8380                                         $this->_out('/Domain [0.0 1.0]');
8381                                         $this->_out('/C0 ['.$grad['col1'].']');
8382                                         $this->_out('/C1 ['.$grad['col2'].']');
8383                                         $this->_out('/N 1');
8384                                         $this->_out('>>');
8385                                         $this->_out('endobj');
8386                                         $f1 = $this->n;
8387                                 }
8388                                 $this->_newobj();
8389                                 $this->_out('<<');
8390                                 $this->_out('/ShadingType '.$grad['type']);
8391                                 $this->_out('/ColorSpace /DeviceRGB');
8392                                 if ($grad['type'] == 2) {
8393                                         $this->_out(sprintf('/Coords [%.3f %.3f %.3f %.3f]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
8394                                         $this->_out('/Function '.$f1.' 0 R');
8395                                         $this->_out('/Extend [true true] ');
8396                                         $this->_out('>>');
8397                                 } elseif ($grad['type'] == 3) {
8398                                         //x0, y0, r0, x1, y1, r1
8399                                         //at this this time radius of inner circle is 0
8400                                         $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]));
8401                                         $this->_out('/Function '.$f1.' 0 R');
8402                                         $this->_out('/Extend [true true] ');
8403                                         $this->_out('>>');
8404                                 } elseif ($grad['type'] == 6) {
8405                                         $this->_out('/BitsPerCoordinate 16');
8406                                         $this->_out('/BitsPerComponent 8');
8407                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
8408                                         $this->_out('/BitsPerFlag 8');
8409                                         $this->_out('/Length '.strlen($grad['stream']));
8410                                         $this->_out('>>');
8411                                         $this->_putstream($grad['stream']);
8412                                 }
8413                                 $this->_out('endobj');
8414                                 $this->gradients[$id]['id'] = $this->n;
8415                         }
8416                 }
8417
8418                 /**
8419                 * Output an arc
8420                 * @author Maxime Delorme, Nicola Asuni
8421                 * @since 3.1.000 (2008-06-09)
8422                 * @access protected
8423                 */
8424                 function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
8425                         $h = $this->h;
8426                         $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));
8427                 }
8428
8429                 /**
8430                 * Draw the sector of a circle.
8431                 * It can be used for instance to render pie charts.
8432                 * @param float $xc abscissa of the center.
8433                 * @param float $yc ordinate of the center.
8434                 * @param float $r radius.
8435                 * @param float $a start angle (in degrees).
8436                 * @param float $b end angle (in degrees).
8437                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
8438                 * @param float $cw: indicates whether to go clockwise (default: true).
8439                 * @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.
8440                 * @author Maxime Delorme, Nicola Asuni
8441                 * @since 3.1.000 (2008-06-09)
8442                 * @access public
8443                 */
8444                 function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
8445                         if ($this->rtl) {
8446                                 $xc = $this->w - $xc - $w;
8447                         }
8448                         if ($cw) {
8449                                 $d = $b;
8450                                 $b = $o - $a;
8451                                 $a = $o - $d;
8452                         } else {
8453                                 $b += $o;
8454                                 $a += $o;
8455                         }
8456                         $a = ($a % 360) + 360;
8457                         $b = ($b % 360) + 360;
8458                         if ($a > $b) {
8459                                 $b +=360;
8460                         }
8461                         $b = $b / 360 * 2 * M_PI;
8462                         $a = $a / 360 * 2 * M_PI;
8463                         $d = $b - $a;
8464                         if ($d == 0 ) {
8465                                 $d = 2 * M_PI;
8466                         }
8467                         $k = $this->k;
8468                         $hp = $this->h;
8469                         if ($style=='F') {
8470                                 $op = 'f';
8471                         } elseif ($style=='FD' or $style=='DF') {
8472                                 $op = 'b';
8473                         } else {
8474                                 $op = 's';
8475                         }
8476                         if (sin($d/2)) {
8477                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
8478                         }
8479                         //first put the center
8480                         $this->_out(sprintf('%.2f %.2f m', ($xc)*$k, ($hp-$yc)*$k));
8481                         //put the first point
8482                         $this->_out(sprintf('%.2f %.2f l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
8483                         //draw the arc
8484                         if ($d < (M_PI/2)) {
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                         } else {
8487                                 $b = $a + $d/4;
8488                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
8489                                 $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));
8490                                 $a = $b;
8491                                 $b = $a + $d/4;
8492                                 $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));
8493                                 $a = $b;
8494                                 $b = $a + $d/4;
8495                                 $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) );
8496                                 $a = $b;
8497                                 $b = $a + $d/4;
8498                                 $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));
8499                         }
8500                         //terminate drawing
8501                         $this->_out($op);
8502                 }
8503
8504                 /**
8505                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
8506                 * Only vector drawing is supported, not text or bitmap.
8507                 * 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).
8508                 * @param string $file Name of the file containing the image.
8509                 * @param float $x Abscissa of the upper-left corner.
8510                 * @param float $y Ordinate of the upper-left corner.
8511                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
8512                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
8513                 * @param mixed $link URL or identifier returned by AddLink().
8514                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
8515                 * @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>
8516                 * @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>
8517                 * @author Valentin Schmidt, Nicola Asuni
8518                 * @since 3.1.000 (2008-06-09)
8519                 * @access public
8520                 */
8521                 function ImageEps($file, $x, $y, $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='') {
8522                         $data = file_get_contents($file);
8523                         if ($data === false) {
8524                                 $this->Error('EPS file not found: '.$file);
8525                         }
8526                         $regs = array();
8527                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
8528                         preg_match ('/%%Creator:([^\r\n]+)/', $data, $regs); # find Creator
8529                         if (count($regs) > 1) {
8530                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
8531                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
8532                                         $versexp = explode(' ', $version_str);
8533                                         $version = (float)array_pop($versexp);
8534                                         if ($version >= 9) {
8535                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
8536                                         }
8537                                 }
8538                         }
8539                         // strip binary bytes in front of PS-header
8540                         $start = strpos($data, '%!PS-Adobe');
8541                         if ($start > 0) {
8542                                 $data = substr($data, $start);
8543                         }
8544                         // find BoundingBox params
8545                         preg_match ("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
8546                         if (count($regs) > 1) {
8547                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
8548                         } else {
8549                                 $this->Error('No BoundingBox found in EPS file: '.$file);
8550                         }
8551                         $start = strpos($data, '%%EndSetup');
8552                         if ($start === false) {
8553                                 $start = strpos($data, '%%EndProlog');
8554                         }
8555                         if ($start === false) {
8556                                 $start = strpos($data, '%%BoundingBox');
8557                         }
8558                         $data = substr($data, $start);
8559                         $end = strpos($data, '%%PageTrailer');
8560                         if ($end===false) {
8561                                 $end = strpos($data, 'showpage');
8562                         }
8563                         if ($end) {
8564                                 $data = substr($data, 0, $end);
8565                         }
8566                         $k = $this->k;
8567                         if ($w > 0) {
8568                                 $scale_x = $w/(($x2-$x1)/$k);
8569                                 if ($h > 0) {
8570                                         $scale_y = $h/(($y2-$y1)/$k);
8571                                 } else {
8572                                         $scale_y = $scale_x;
8573                                         $h = ($y2-$y1)/$k * $scale_y;
8574                                 }
8575                         } else {
8576                                 if ($h > 0) {
8577                                         $scale_y = $h/(($y2-$y1)/$k);
8578                                         $scale_x = $scale_y;
8579                                         $w = ($x2-$x1)/$k * $scale_x;
8580                                 } else {
8581                                         $w = ($x2 - $x1) / $k;
8582                                         $h = ($y2 - $y1) / $k;
8583                                 }
8584                         }
8585                         // Check whether we need a new page first as this does not fit
8586                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
8587                                 // Automatic page break
8588                                 $this->AddPage($this->CurOrientation);
8589                                 // Reset coordinates to top fo next page
8590                                 //$x = $this->GetX();
8591                                 $y = $this->GetY() + $this->cMargin;
8592                         }
8593                         // set bottomcoordinates
8594                         $this->img_rb_y = $y + $h;
8595                         // set alignment
8596                         if ($this->rtl) {
8597                                 if ($palign == 'L') {
8598                                         $ximg = $this->lMargin;
8599                                         // set right side coordinate
8600                                         $this->img_rb_x = $ximg + $w;
8601                                 } elseif ($palign == 'C') {
8602                                         $ximg = ($this->w - $x - $w) / 2;
8603                                         // set right side coordinate
8604                                         $this->img_rb_x = $ximg + $w;
8605                                 } else {
8606                                         $ximg = $this->w - $x - $w;
8607                                         // set left side coordinate
8608                                         $this->img_rb_x = $ximg;
8609                                 }
8610                         } else {
8611                                 if ($palign == 'R') {
8612                                         $ximg = $this->w - $this->rMargin - $w;
8613                                         // set left side coordinate
8614                                         $this->img_rb_x = $ximg;
8615                                 } elseif ($palign == 'C') {
8616                                         $ximg = ($this->w - $x - $w) / 2;
8617                                         // set right side coordinate
8618                                         $this->img_rb_x = $ximg + $w;
8619                                 } else {
8620                                         $ximg = $x;
8621                                         // set right side coordinate
8622                                         $this->img_rb_x = $ximg + $w;
8623                                 }
8624                         }
8625                         if ($useBoundingBox){
8626                                 $dx = $ximg * $k - $x1;
8627                                 $dy = $y * $k - $y1;
8628                         } else {
8629                                 $dx = $ximg * $k;
8630                                 $dy = $y * $k;
8631                         }
8632                         // save the current graphic state
8633                         $this->_out('q');
8634                         // translate
8635                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy+($this->hPt - 2*$y*$k - ($y2-$y1))));
8636                         // scale
8637                         if (isset($scale_x)) {
8638                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1*(1-$scale_x), $y2*(1-$scale_y)));
8639                         }
8640                         // handle pc/unix/mac line endings
8641                         $lines = split("\r\n|[\r\n]", $data);
8642                         $u=0;
8643                         $cnt = count($lines);
8644                         for ($i=0; $i < $cnt; $i++) {
8645                                 $line = $lines[$i];
8646                                 if (($line == '') OR ($line{0} == '%')) {
8647                                         continue;
8648                                 }
8649                                 $len = strlen($line);
8650                                 $chunks = explode(' ', $line);
8651                                 $cmd = array_pop($chunks);
8652                                 // RGB
8653                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
8654                                         $b = array_pop($chunks);
8655                                         $g = array_pop($chunks);
8656                                         $r = array_pop($chunks);
8657                                         $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!
8658                                         continue;
8659                                 }
8660                                 switch ($cmd) {
8661                                         case 'm':
8662                                         case 'l':
8663                                         case 'v':
8664                                         case 'y':
8665                                         case 'c':
8666                                         case 'k':
8667                                         case 'K':
8668                                         case 'g':
8669                                         case 'G':
8670                                         case 's':
8671                                         case 'S':
8672                                         case 'J':
8673                                         case 'j':
8674                                         case 'w':
8675                                         case 'M':
8676                                         case 'd':
8677                                         case 'n':
8678                                         case 'v': {
8679                                                 $this->_out($line);
8680                                                 break;
8681                                         }
8682                                         case 'x': {// custom fill color
8683                                                 list($c,$m,$y,$k) = $chunks;
8684                                                 $this->_out("$c $m $y $k k");
8685                                                 break;
8686                                         }
8687                                         case 'X': { // custom stroke color
8688                                                 list($c,$m,$y,$k) = $chunks;
8689                                                 $this->_out("$c $m $y $k K");
8690                                                 break;
8691                                         }
8692                                         case 'Y':
8693                                         case 'N':
8694                                         case 'V':
8695                                         case 'L':
8696                                         case 'C': {
8697                                                 $line{$len-1} = strtolower($cmd);
8698                                                 $this->_out($line);
8699                                                 break;
8700                                         }
8701                                         case 'b':
8702                                         case 'B': {
8703                                                 $this->_out($cmd . '*');
8704                                                 break;
8705                                         }
8706                                         case 'f':
8707                                         case 'F': {
8708                                                 if ($u > 0) {
8709                                                         $isU = false;
8710                                                         $max = min($i+5, $cnt);
8711                                                         for ($j=$i+1; $j < $max; $j++)
8712                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
8713                                                         if ($isU) {
8714                                                                 $this->_out("f*");
8715                                                         }
8716                                                 } else {
8717                                                         $this->_out("f*");
8718                                                 }
8719                                                 break;
8720                                         }
8721                                         case '*u': {
8722                                                 $u++;
8723                                                 break;
8724                                         }
8725                                         case '*U': {
8726                                                 $u--;
8727                                                 break;
8728                                         }
8729                                 }
8730                         }
8731                         // restore previous graphic state
8732                         $this->_out('Q');
8733                         if ($link) {
8734                                 $this->Link($ximg, $y, $w, $h, $link);
8735                         }
8736                         // set pointer to align the successive text/objects
8737                         switch($align) {
8738                                 case 'T':{
8739                                         $this->y = $y;
8740                                         $this->x = $this->img_rb_x;
8741                                         break;
8742                                 }
8743                                 case 'M':{
8744                                         $this->y = $y + round($h/2);
8745                                         $this->x = $this->img_rb_x;
8746                                         break;
8747                                 }
8748                                 case 'B':{
8749                                         $this->y = $this->img_rb_y;
8750                                         $this->x = $this->img_rb_x;
8751                                         break;
8752                                 }
8753                                 case 'N':{
8754                                         $this->SetY($this->img_rb_y);
8755                                         break;
8756                                 }
8757                                 default:{
8758                                         break;
8759                                 }
8760                         }
8761                         $this->endlinex = $this->img_rb_x;
8762                 }
8763
8764                 /**
8765                  * Set document barcode.
8766                  * @param string $bc barcode
8767                  */
8768                 function setBarcode($bc="") {
8769                         $this->barcode = $bc;
8770                 }
8771
8772                 /**
8773                  * Get current barcode.
8774                  * @return string
8775                  * @since 4.0.012 (2008-07-24)
8776                  */
8777                 function getBarcode() {
8778                         return $this->barcode;
8779                 }
8780
8781                 /**
8782                  * Print Barcode.
8783                  * @param string $code code to print
8784                  * @param string $type type of barcode.
8785                  * @param int $x x position in user units
8786                  * @param int $y y position in user units
8787                  * @param int $w width in user units
8788                  * @param int $h height position in user units
8789                  * @param float $xres width of the smallest bar in user units
8790                  * @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>
8791                  * @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>
8792                  * @author Nicola Asuni
8793                  * @since 3.1.000 (2008-06-09)
8794                  * @access public
8795                  */
8796                 function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
8797                         if (empty($code)) {
8798                                 return;
8799                         }
8800                         $barcodeobj = new TCPDFbarcode($code, $type);
8801                         $arrcode = $barcodeobj->getBarcodeArray();
8802                         if ($arrcode === false) {
8803                                 $this->Error('Error in barcode string');
8804                         }
8805                         // set default values
8806                         if (!isset($style["position"])) {
8807                                 if ($this->rtl) {
8808                                         $style["position"] = "R";
8809                                 } else {
8810                                         $style["position"] = "L";
8811                                 }
8812                         }
8813                         if (!isset($style["padding"])) {
8814                                 $style["padding"] = 0;
8815                         }
8816                         if (!isset($style["fgcolor"])) {
8817                                 $style["fgcolor"] = array(0,0,0); // default black
8818                         }
8819                         if (!isset($style["bgcolor"])) {
8820                                 $style["bgcolor"] = false; // default transparent
8821                         }
8822                         if (!isset($style["border"])) {
8823                                 $style["border"] = false;
8824                         }
8825                         if (!isset($style["text"])) {
8826                                 $style["text"] = false;
8827                                 $fontsize = 0;
8828                         }
8829                         if ($style["text"] AND isset($style["font"])) {
8830                                 $prevFontFamily = $this->FontFamily;
8831                                 $prevFontStyle = $this->FontStyle;
8832                                 $prevFontSizePt = $this->FontSizePt;
8833                                 if (isset($style["fontsize"])) {
8834                                         $fontsize = $style["fontsize"];
8835                                 } else {
8836                                         $fontsize = 0;
8837                                 }
8838                                 $this->SetFont($style["font"], '', $fontsize);
8839                         }
8840                         if (!isset($style["stretchtext"])) {
8841                                 $style["stretchtext"] = 4;
8842                         }
8843                         // set foreground color
8844                         $prevDrawColor = $this->DrawColor;
8845                         $prevTextColor = $this->TextColor;
8846                         $this->SetDrawColorArray($style["fgcolor"]);
8847                         $this->SetTextColorArray($style["fgcolor"]);
8848                         if (empty($w) OR ($w <= 0)) {
8849                                 if ($this->rtl) {
8850                                         $w = $this->x - $this->lMargin;
8851                                 } else {
8852                                         $w = $this->w - $this->rMargin - $this->x;
8853                                 }
8854                         }
8855                         if (empty($x)) {
8856                                 $x = $this->GetX();
8857                         }
8858                         if ($this->rtl) {
8859                                 $x = $this->w - $x;
8860                         }
8861                         if (empty($y)) {
8862                                 $y = $this->GetY();
8863                         }
8864                         if (empty($xres)) {
8865                                 $xres = 0.4;
8866                         }
8867                         $fbw = ($arrcode["maxw"] * $xres) + (2 * $style["padding"]);
8868                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style["padding"]);
8869                         if (empty($h)) {
8870                                 $h = 10 + $extraspace;
8871                         }
8872                         if ((($y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
8873                                 //Automatic page break
8874                                 $x = $this->x;
8875                                 $ws = $this->ws;
8876                                 if ($ws > 0) {
8877                                         $this->ws = 0;
8878                                         $this->_out('0 Tw');
8879                                 }
8880                                 $this->AddPage($this->CurOrientation);
8881                                 if ($ws > 0) {
8882                                         $this->ws = $ws;
8883                                         $this->_out(sprintf('%.3f Tw',$ws * $k));
8884                                 }
8885                                 $this->x = $x;
8886                                 $y = $this->y;
8887                         }
8888                         // maximum bar heigth
8889                         $barh = $h - $extraspace;
8890                         switch ($style["position"]) {
8891                                 case "L": { // left
8892                                         if ($this->rtl) {
8893                                                 $xpos = $x - $w;
8894                                         } else {
8895                                                 $xpos = $x;
8896                                         }
8897                                         break;
8898                                 }
8899                                 case "C": { // center
8900                                         $xdiff = (($w - $fbw) / 2);
8901                                         if ($this->rtl) {
8902                                                 $xpos = $x - $w + $xdiff;
8903                                         } else {
8904                                                 $xpos = $x + $xdiff;
8905                                         }
8906                                         break;
8907                                 }
8908                                 case "R": { // right
8909                                         if ($this->rtl) {
8910                                                 $xpos = $x - $fbw;
8911                                         } else {
8912                                                 $xpos = $x + $w - $fbw;
8913                                         }
8914                                         break;
8915                                 }
8916                                 case "S": { // stretch
8917                                         $fbw = $w;
8918                                         $xres = ($w - (2 * $style["padding"])) / $arrcode["maxw"];
8919                                         if ($this->rtl) {
8920                                                 $xpos = $x - $w;
8921                                         } else {
8922                                                 $xpos = $x;
8923                                         }
8924                                         break;
8925                                 }
8926                         }
8927                         $xpos_rect = $xpos;
8928                         $xpos = $xpos_rect + $style["padding"];
8929                         $xpos_text = $xpos;
8930                         // barcode is always printed in LTR direction
8931                         $tempRTL = $this->rtl;
8932                         $this->rtl = false;
8933                         // print background color
8934                         if ($style["bgcolor"]) {
8935                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style["bgcolor"]);
8936                         } elseif ($style["border"]) {
8937                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
8938                         }
8939                         // print bars
8940                         if ($arrcode !== false) {
8941                                 foreach ($arrcode["bcode"] as $k => $v) {
8942                                         $bw = ($v["w"] * $xres);
8943                                         if ($v["t"]) {
8944                                                 // braw a vertical bar
8945                                                 $ypos = $y + $style["padding"] + ($v["p"] * $barh / $arrcode["maxh"]);
8946                                                 $this->Rect($xpos, $ypos, $bw, ($v["h"] * $barh  / $arrcode["maxh"]), 'DF', array("L"=>0,"T"=>0,"R"=>0,"B"=>0), $style["fgcolor"]);
8947                                         }
8948                                         $xpos += $bw;
8949                                 }
8950                         }
8951                         // print text
8952                         if ($style["text"]) {
8953                                 // print text
8954                                 $this->x = $xpos_text;
8955                                 $this->y = $y + $style["padding"] + $barh;
8956                                 $this->Cell(($arrcode["maxw"] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style["stretchtext"]);
8957                         }
8958                         // restore original direction
8959                         $this->rtl = $tempRTL;
8960                         // restore previous font
8961                         if ($style["text"] AND isset($style["font"])) {
8962                                 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
8963                         }
8964                         // restore colors
8965                         $this->DrawColor = $prevDrawColor;
8966                         $this->TextColor = $prevTextColor;
8967                         // set bottomcoordinates
8968                         $this->img_rb_y = $y + $h;
8969                         if ($this->rtl) {
8970                                 // set left side coordinate
8971                                 $this->img_rb_x = ($this->w - $x - $w);
8972                         } else {
8973                                 // set right side coordinate
8974                                 $this->img_rb_x = $x + $w;
8975                         }
8976                         // set pointer to align the successive text/objects
8977                         switch($align) {
8978                                 case 'T':{
8979                                         $this->y = $y;
8980                                         $this->x = $this->img_rb_x;
8981                                         break;
8982                                 }
8983                                 case 'M':{
8984                                         $this->y = $y + round($h/2);
8985                                         $this->x = $this->img_rb_x;
8986                                         break;
8987                                 }
8988                                 case 'B':{
8989                                         $this->y = $this->img_rb_y;
8990                                         $this->x = $this->img_rb_x;
8991                                         break;
8992                                 }
8993                                 case 'N':{
8994                                         $this->SetY($this->img_rb_y);
8995                                         break;
8996                                 }
8997                                 default:{
8998                                         break;
8999                                 }
9000                         }
9001                 }
9002
9003                 /**
9004                  * This function is DEPRECATED, please use the new write1DBarcode() function.
9005                  * @param int $x x position in user units
9006                  * @param int $y y position in user units
9007                  * @param int $w width in user units
9008                  * @param int $h height position in user units
9009                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
9010                  * @param string $style barcode style
9011                  * @param string $font font for text
9012                  * @param int $xres x resolution
9013                  * @param string $code code to print
9014                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
9015                  * @see write1DBarcode()
9016                  */
9017                 function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
9018                         // convert old settings for the new write1DBarcode() function.
9019                         $xres = 1 / $xres;
9020                         $newstyle = array(
9021                                 "position" => "L",
9022                                 "border" => false,
9023                                 "padding" => 0,
9024                                 "fgcolor" => array(0,0,0),
9025                                 "bgcolor" => false,
9026                                 "text" => true,
9027                                 "font" => $font,
9028                                 "fontsize" => 8,
9029                                 "stretchtext" => 4
9030                         );
9031                         if ($style & 1) {
9032                                 $newstyle["border"] = true;
9033                         }
9034                         if ($style & 2) {
9035                                 $newstyle["bgcolor"] = false;
9036                         }
9037                         if ($style & 4) {
9038                                 $newstyle["position"] = "C";
9039                         } elseif ($style & 8) {
9040                                 $newstyle["position"] = "L";
9041                         } elseif ($style & 16) {
9042                                 $newstyle["position"] = "R";
9043                         }
9044                         if ($style & 128) {
9045                                 $newstyle["text"] = true;
9046                         }
9047                         if ($style & 256) {
9048                                 $newstyle["stretchtext"] = 4;
9049                         }
9050                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
9051                 }
9052
9053                 /**
9054                  * Returns an array containing current margins:
9055                  * <ul>
9056                                 <li>$ret['left'] = left  margin</li>
9057                                 <li>$ret['right'] = right margin</li>
9058                                 <li>$ret['top'] = top margin</li>
9059                                 <li>$ret['bottom'] = bottom margin</li>
9060                                 <li>$ret['header'] = header margin</li>
9061                                 <li>$ret['footer'] = footer margin</li>
9062                                 <li>$ret['cell'] = cell margin</li>
9063                  * </ul>
9064                  * @return array containing all margins measures
9065                  * @since 3.2.000 (2008-06-23)
9066                  */
9067                 function getMargins() {
9068                         $ret = array(
9069                                 'left' => $this->lMargin,
9070                                 'right' => $this->rMargin,
9071                                 'top' => $this->tMargin,
9072                                 'bottom' => $this->bMargin,
9073                                 'header' => $this->header_margin,
9074                                 'footer' => $this->footer_margin,
9075                                 'cell' => $this->cMargin,
9076                         );
9077                         return $ret;
9078                 }
9079
9080                 /**
9081                  * Returns an array containing original margins:
9082                  * <ul>
9083                                 <li>$ret['left'] = left  margin</li>
9084                                 <li>$ret['right'] = right margin</li>
9085                  * </ul>
9086                  * @return array containing all margins measures
9087                  * @since 4.0.012 (2008-07-24)
9088                  */
9089                 function getOriginalMargins() {
9090                         $ret = array(
9091                                 'left' => $this->original_lMargin,
9092                                 'right' => $this->original_rMargin
9093                         );
9094                         return $ret;
9095                 }
9096
9097                 /**
9098                  * Returns the current font size.
9099                  * @return current font size
9100                  * @since 3.2.000 (2008-06-23)
9101                  */
9102                 function getFontSize() {
9103                         return $this->FontSize;
9104                 }
9105
9106                 /**
9107                  * Returns the current font size in points unit.
9108                  * @return current font size in points unit
9109                  * @since 3.2.000 (2008-06-23)
9110                  */
9111                 function getFontSizePt() {
9112                         return $this->FontSizePt;
9113                 }
9114
9115                 /**
9116                  * Prints a cell (rectangular area) with optional borders, background color and html text string.
9117                  * 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 />
9118                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
9119                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
9120                  * @param float $h Cell minimum height. The cell extends automatically if needed.
9121                  * @param float $x upper-left corner X coordinate
9122                  * @param float $y upper-left corner Y coordinate
9123                  * @param string $html html text to print. Default value: empty string.
9124                  * @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>
9125                  * @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>
9126         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
9127                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
9128                  * @param boolean $reseth if true reset the last cell height (default true).
9129                  * @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>
9130                  * @uses MultiCell()
9131                  * @see Multicell(), writeHTML()
9132                  */
9133                 function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='') {
9134                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true);
9135                 }
9136
9137                 /**
9138                  * Returns the HTML DOM array.
9139                  * <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>
9140                  * @param string $html html code
9141                  * @return array
9142                  * @since 3.2.000 (2008-06-20)
9143                  */
9144                 function getHtmlDomArray($html) {
9145                         // remove all unsupported tags (the line below lists all supported tags)
9146                         $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>");
9147                         //replace carriage returns, newlines and tabs
9148                         $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ", "\\" => "\\\\");
9149                         $html = strtr($html, $repTable);
9150                         // remove extra spaces from tables
9151                         $html = preg_replace('/[\s]*<\/table>[\s]*/', '</table>', $html);
9152                         $html = preg_replace('/[\s]*<\/tr>[\s]*/', '</tr>', $html);
9153                         $html = preg_replace('/[\s]*<tr/', '<tr', $html);
9154                         $html = preg_replace('/[\s]*<\/th>[\s]*/', '</th>', $html);
9155                         $html = preg_replace('/[\s]*<th/', '<th', $html);
9156                         $html = preg_replace('/[\s]*<\/td>[\s]*/', '</td>', $html);
9157                         $html = preg_replace('/[\s]*<td/', '<td', $html);
9158                         $html = preg_replace('/<\/th>/', '<span></span></th>', $html);
9159                         $html = preg_replace('/<\/td>/', '<span></span></td>', $html);
9160                         // pattern for generic tag
9161                         $tagpattern = '/(<[^>]+>)/Uu';
9162                         // explodes the string
9163                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9164                         // count elements
9165                         $maxel = count($a);
9166                         $key = 0;
9167                         // create an array of elements
9168                         $dom = array();
9169                         $dom[$key] = array();
9170                         // set first void element
9171                         $dom[$key]['tag'] = false;
9172                         $dom[$key]['value'] = "";
9173                         $dom[$key]['parent'] = 0;
9174                         $dom[$key]['fontname'] = $this->FontFamily;
9175                         $dom[$key]['fontstyle'] = $this->FontStyle;
9176                         $dom[$key]['fontsize'] = $this->FontSizePt;
9177                         $dom[$key]['bgcolor'] = false;
9178                         $dom[$key]['fgcolor'] = $this->fgcolor;
9179                         $dom[$key]['align'] = '';
9180                         $key++;
9181                         $level = array();
9182                         array_push($level, 0); // root
9183                         while ($key <= $maxel) {
9184                                 if ($key > 0) {
9185                                         $dom[$key] = array();
9186                                 }
9187                                 $element = $a[($key-1)];
9188                                 if (preg_match($tagpattern, $element)) {
9189                                         // html tag
9190                                         $dom[$key]['tag'] = true;
9191                                         $element = substr($element, 1, -1);
9192                                         // get tag name
9193                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
9194                                         $dom[$key]['value'] = strtolower($tag[1]);
9195                                         if ($element{0} == '/') {
9196                                                 // closing html tag
9197                                                 $dom[$key]['opening'] = false;
9198                                                 $dom[$key]['parent'] = end($level);
9199                                                 array_pop($level);
9200                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
9201                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
9202                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
9203                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
9204                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
9205                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
9206                                                 // set the number of columns in table tag
9207                                                 if (($dom[$key]['value'] == "tr") AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
9208                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
9209                                                 }
9210                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9211                                                         $dom[($dom[$key]['parent'])]['content'] = "";
9212                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; $i++) {
9213                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[($i-1)];
9214                                                         }
9215                                                         $key = $i;
9216                                                 }
9217                                         } else {
9218                                                 // opening html tag
9219                                                 $dom[$key]['opening'] = true;
9220                                                 $dom[$key]['parent'] = end($level);
9221                                                 if (substr($element, -1, 1) != '/') {
9222                                                         // not self-closing tag
9223                                                         array_push($level, $key);
9224                                                         $dom[$key]['self'] = false;
9225                                                 } else {
9226                                                         $dom[$key]['self'] = true;
9227                                                 }
9228                                                 // copy some values from parent
9229                                                 if ($key > 0) {
9230                                                         $dom[$key]['fontname'] = $dom[($dom[$key]['parent'])]['fontname'];
9231                                                         $dom[$key]['fontstyle'] = $dom[($dom[$key]['parent'])]['fontstyle'];
9232                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'];
9233                                                         $dom[$key]['bgcolor'] = $dom[($dom[$key]['parent'])]['bgcolor'];
9234                                                         $dom[$key]['fgcolor'] = $dom[($dom[$key]['parent'])]['fgcolor'];
9235                                                         $dom[$key]['align'] = $dom[($dom[$key]['parent'])]['align'];
9236                                                 }
9237                                                 // get attributes
9238                                                 preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER);
9239                                                 $dom[$key]['attribute'] = array(); // reset attribute array
9240                                                 while (list($id, $name) = each($attr_array[1])) {
9241                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
9242                                                 }
9243                                                 // split style attributes
9244                                                 if (isset($dom[$key]['attribute']['style'])) {
9245                                                         // get style attributes
9246                                                         preg_match_all('/([^:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
9247                                                         $dom[$key]['style'] = array(); // reset style attribute array
9248                                                         while (list($id, $name) = each($style_array[1])) {
9249                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
9250                                                         }
9251                                                         // --- get some style attributes ---
9252                                                         if (isset($dom[$key]['style']['font-family'])) {
9253                                                                 // font family
9254                                                                 if (isset($dom[$key]['style']['font-family'])) {
9255                                                                         $fontslist = split(",", strtolower($dom[$key]['style']['font-family']));
9256                                                                         foreach($fontslist as $font) {
9257                                                                                 $font = trim(strtolower($font));
9258                                                                                 if (in_array($font, $this->fontlist)){
9259                                                                                         $dom[$key]['fontname'] = $font;
9260                                                                                         break;
9261                                                                                 }
9262                                                                         }
9263                                                                 }
9264                                                         }
9265                                                         // font size
9266                                                         if (isset($dom[$key]['style']['font-size'])) {
9267                                                                 $fsize = trim($dom[$key]['style']['font-size']);
9268                                                                 switch ($fsize) {
9269                                                                         case 'xx-small': {
9270                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
9271                                                                                 break;
9272                                                                         }
9273                                                                         case 'x-small': {
9274                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
9275                                                                                 break;
9276                                                                         }
9277                                                                         case 'small': {
9278                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
9279                                                                                 break;
9280                                                                         }
9281                                                                         case 'medium': {
9282                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
9283                                                                                 break;
9284                                                                         }
9285                                                                         case 'large': {
9286                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
9287                                                                                 break;
9288                                                                         }
9289                                                                         case 'x-large': {
9290                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
9291                                                                                 break;
9292                                                                         }
9293                                                                         case 'xx-large': {
9294                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
9295                                                                                 break;
9296                                                                         }
9297                                                                         default: {
9298                                                                                 $dom[$key]['fontsize'] = intval($fsize);
9299                                                                         }
9300                                                                 }
9301                                                         }
9302                                                         // font style
9303                                                         $dom[$key]['fontstyle'] = "";
9304                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == "b")) {
9305                                                                 $dom[$key]['fontstyle'] .= "B";
9306                                                         }
9307                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == "i")) {
9308                                                                 $dom[$key]['fontstyle'] .= "I";
9309                                                         }
9310                                                         // font color
9311                                                         if (isset($dom[$key]['style']['color']) AND (!empty($dom[$key]['style']['color']))) {
9312                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
9313                                                         }
9314                                                         // background color
9315                                                         if (isset($dom[$key]['style']['background-color']) AND (!empty($dom[$key]['style']['background-color']))) {
9316                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
9317                                                         }
9318                                                         // text-decoration
9319                                                         if (isset($dom[$key]['style']['text-decoration'])) {
9320                                                                 $decors = explode(" ", strtolower($dom[$key]['style']['text-decoration']));
9321                                                                 foreach ($decors as $dec) {
9322                                                                         $dec = trim($dec);
9323                                                                         if ($dec{0} == "u") {
9324                                                                                 $dom[$key]['fontstyle'] .= "U";
9325                                                                         } elseif ($dec{0} == "l") {
9326                                                                                 $dom[$key]['fontstyle'] .= "D";
9327                                                                         }
9328                                                                 }
9329                                                         }
9330                                                         // check for width attribute
9331                                                         if (isset($dom[$key]['style']['width'])) {
9332                                                                 $dom[$key]['width'] = intval($dom[$key]['style']['width']);
9333                                                         }
9334                                                         // check for height attribute
9335                                                         if (isset($dom[$key]['style']['height'])) {
9336                                                                 $dom[$key]['height'] = intval($dom[$key]['style']['height']);
9337                                                         }
9338                                                         // check for text alignment
9339                                                         if (isset($dom[$key]['style']['text-align'])) {
9340                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
9341                                                         }
9342                                                 }
9343                                                 // check for font tag
9344                                                 if ($dom[$key]['value'] == "font") {
9345                                                         // font family
9346                                                         if (isset($dom[$key]['attribute']['face'])) {
9347                                                                 $fontslist = split(",", strtolower($dom[$key]['attribute']['face']));
9348                                                                 foreach($fontslist as $font) {
9349                                                                         $font = trim(strtolower($font));
9350                                                                         if (in_array($font, $this->fontlist)){
9351                                                                                 $dom[$key]['fontname'] = $font;
9352                                                                                 break;
9353                                                                         }
9354                                                                 }
9355                                                         }
9356                                                         // font size
9357                                                         if (isset($dom[$key]['attribute']['size'])) {
9358                                                                 if ($key > 0) {
9359                                                                         if ($dom[$key]['attribute']['size']{0} == "+") {
9360                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
9361                                                                         } elseif ($dom[$key]['attribute']['size']{0} == "-") {
9362                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
9363                                                                         } else {
9364                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9365                                                                         }
9366                                                                 } else {
9367                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9368                                                                 }
9369                                                         }
9370                                                 }
9371                                                 if (($dom[$key]['value'] == "ul") OR ($dom[$key]['value'] == "ol") OR ($dom[$key]['value'] == "dl")) {
9372                                                         // force natural alignment for lists
9373                                                         if ($this->rtl) {
9374                                                                 $dom[$key]['align'] = "R";
9375                                                         } else {
9376                                                                 $dom[$key]['align'] = "L";
9377                                                         }
9378                                                 }
9379                                                 if (($dom[$key]['value'] == "small") OR ($dom[$key]['value'] == "sup") OR ($dom[$key]['value'] == "sub")) {
9380                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
9381                                                 }
9382                                                 if (($dom[$key]['value'] == "strong") OR ($dom[$key]['value'] == "b")) {
9383                                                         $dom[$key]['fontstyle'] .= "B";
9384                                                 }
9385                                                 if (($dom[$key]['value'] == "em") OR ($dom[$key]['value'] == "i")) {
9386                                                         $dom[$key]['fontstyle'] .= "I";
9387                                                 }
9388                                                 if (($dom[$key]['value']{0} == "h") AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
9389                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
9390                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
9391                                                         $dom[$key]['fontstyle'] .= "B";
9392                                                 }
9393                                                 if (($dom[$key]['value'] == "table")) {
9394                                                         $dom[$key]['rows'] = 0; // number of rows
9395                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
9396                                                 }
9397                                                 if (($dom[$key]['value'] == "tr")) {
9398                                                         $dom[$key]['cols'] = 0;
9399                                                         // store the number of rows on table element
9400                                                         $dom[($dom[$key]['parent'])]['rows']++;
9401                                                         // store the TR elements IDs on table element
9402                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
9403                                                 }
9404                                                 if (($dom[$key]['value'] == "th") OR ($dom[$key]['value'] == "td")) {
9405                                                         if (isset($dom[$key]['attribute']['colspan'])) {
9406                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
9407                                                         } else {
9408                                                                 $colspan = 1;
9409                                                         }
9410                                                         $dom[$key]['attribute']['colspan'] = $colspan;
9411                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
9412                                                 }
9413                                                 // set foreground color attribute
9414                                                 if (isset($dom[$key]['attribute']['color']) AND (!empty($dom[$key]['attribute']['color']))) {
9415                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
9416                                                 }
9417                                                 // set background color attribute
9418                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!empty($dom[$key]['attribute']['bgcolor']))) {
9419                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
9420                                                 }
9421                                                 // check for width attribute
9422                                                 if (isset($dom[$key]['attribute']['width'])) {
9423                                                         $dom[$key]['width'] = intval($dom[$key]['attribute']['width']);
9424                                                 }
9425                                                 // check for height attribute
9426                                                 if (isset($dom[$key]['attribute']['height'])) {
9427                                                         $dom[$key]['height'] = intval($dom[$key]['attribute']['height']);
9428                                                 }
9429                                                 // check for text alignment
9430                                                 if (isset($dom[$key]['attribute']['align']) AND (!empty($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
9431                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
9432                                                 }
9433                                         } // end opening tag
9434                                 } else {
9435                                         // text
9436                                         $dom[$key]['tag'] = false;
9437                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
9438                                         $dom[$key]['parent'] = end($level);
9439                                         // calculate text width
9440                                         //$dom[$key]['width'] = $this->GetStringWidth($dom[$key]['value'], $dom[($dom[$key]['parent'])]['fontname'], $dom[($dom[$key]['parent'])]['fontstyle'], $dom[($dom[$key]['parent'])]['fontsize']);
9441                                 }
9442                                 $key++;
9443                         }
9444                         return $dom;
9445                 }
9446
9447                 /**
9448                  * Allows to preserve some HTML formatting (limited support).<br />
9449                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
9450                  * 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,
9451                  * @param string $html text to display
9452                  * @param boolean $ln if true add a new line after text (default = true)
9453                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
9454                  * @param boolean $reseth if true reset the last cell height (default false).
9455                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
9456                  * @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>
9457                  */
9458                 function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
9459                         // store current values
9460                         $prevlMargin = $this->lMargin;
9461                         $prevrMargin = $this->rMargin;
9462                         $prevcMargin = $this->cMargin;
9463                         $prevFontFamily = $this->FontFamily;
9464                         $prevFontStyle = $this->FontStyle;
9465                         $prevFontSizePt = $this->FontSizePt;
9466                         $curfontname = $prevFontFamily;
9467                         $curfontstyle = $prevFontStyle;
9468                         $curfontsize = $prevFontSizePt;
9469                         $prevbgcolor = $this->bgcolor;
9470                         $prevfgcolor = $this->fgcolor;
9471                         $this->newline = true;
9472                         $minstartliney = $this->y;
9473                         $yshift = 0;
9474                         $startlinepage = $this->page;
9475                         $newline = true;
9476                         if (isset($this->footerlen[$this->page])) {
9477                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9478                         } else {
9479                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9480                         }
9481                         $startlinepos = $this->footerpos[$this->page];
9482                         $lalign = $align;
9483                         $plalign = $align;
9484                         if ($this->rtl) {
9485                                 $w = $this->x - $this->lMargin;
9486                         } else {
9487                                 $w = $this->w - $this->rMargin - $this->x;
9488                         }
9489                         $w -= (2 * $this->cMargin);
9490                         if ($cell) {
9491                                 if ($this->rtl) {
9492                                         $this->x -= $this->cMargin;
9493                                 } else {
9494                                         $this->x += $this->cMargin;
9495                                 }
9496                         }
9497                         $this->listindent = $this->GetStringWidth("0000");
9498                         $this->listnum = 0;
9499                         if ((empty($this->lasth))OR ($reseth)) {
9500                                 //set row height
9501                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9502                         }
9503                         $dom = $this->getHtmlDomArray($html);
9504                         $maxel = count($dom);
9505                         $key = 0;
9506                         while ($key < $maxel) {
9507                                 if ($dom[$key]['tag'] OR ($key == 0)) {
9508                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
9509                                                 $dom[$key]['align'] = ($this->rtl)?'R':'L';
9510                                         }
9511                                         // vertically align image in line
9512                                         if ((!$this->newline) AND ($dom[$key]['value'] == 'img') 
9513                                                 AND (isset($dom[$key]['attribute']['height']))
9514                                                 AND ($dom[$key]['attribute']['height'] > 0)
9515                                                 ) {
9516                                                 $this->y += (($curfontsize / $this->k) - $this->pixelsToUnits($dom[$key]['attribute']['height']));
9517                                                 $minstartliney = min($this->y, $minstartliney);
9518                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
9519                                                 // account for different font size
9520                                                 $pfontname = $curfontname;
9521                                                 $pfontstyle = $curfontstyle;
9522                                                 $pfontsize = $curfontsize;
9523                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
9524                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
9525                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
9526                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
9527                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9528                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
9529                                                         if (is_numeric($fontsize) AND ($fontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($fontsize != $curfontsize) AND (!$this->newline)) {
9530                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
9531                                                                 $minstartliney = min($this->y, $minstartliney);
9532                                                         }
9533                                                         $curfontname = $fontname;
9534                                                         $curfontstyle = $fontstyle;
9535                                                         $curfontsize = $fontsize;
9536                                                 }
9537                                         }
9538                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
9539                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
9540                                                 $wfill = true;
9541                                         } else {
9542                                                 $wfill = $fill | false;
9543                                         }
9544                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
9545                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
9546                                         }
9547                                         if (isset($dom[$key]['align'])) {
9548                                                 $lalign = $dom[$key]['align'];
9549                                         }
9550                                         if (empty($lalign)) {
9551                                                 $lalign = $align;
9552                                         }
9553                                 }
9554                                 // align lines
9555                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
9556                                         $newline = true;
9557                                         // we are at the beginning of a new line
9558                                         if (isset($startlinex)) {
9559                                                 $yshift = $minstartliney - $startliney;
9560                                                 if ($yshift > 0) {
9561                                                         $yshift = 0;
9562                                                 }
9563                                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9564                                                         // the last line must be shifted to be aligned as requested
9565                                                         $linew = abs($this->endlinex - $startlinex);
9566                                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9567                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9568                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9569                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9570                                                         } elseif (isset($opentagpos)) {
9571                                                                 $midpos = $opentagpos;
9572                                                         } elseif (isset($this->footerlen[$startlinepage])) {
9573                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9574                                                                 $midpos = $this->footerpos[$startlinepage];
9575                                                         } else {
9576                                                                 $midpos = 0;
9577                                                         }
9578                                                         if ($midpos > 0) {
9579                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9580                                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9581                                                         } else {
9582                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9583                                                                 $pend = "";
9584                                                         }
9585                                                         // calculate shifting amount
9586                                                         $mdiff = abs($w - $linew);
9587                                                         if ($plalign == "C") {
9588                                                                 if ($this->rtl) {
9589                                                                         $t_x = -($mdiff / 2);
9590                                                                 } else {
9591                                                                         $t_x = ($mdiff / 2);
9592                                                                 }
9593                                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9594                                                                 // right alignment on LTR document
9595                                                                 $t_x = $mdiff;
9596                                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9597                                                                 // left alignment on RTL document
9598                                                                 $t_x = -$mdiff;
9599                                                         } else {
9600                                                                 $t_x = 0;
9601                                                         }
9602                                                         if (($t_x != 0) OR ($yshift < 0)) {
9603                                                                 // shift the line
9604                                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9605                                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9606                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9607                                                                 // shift the annotations and links
9608                                                                 if (isset($this->PageAnnots[$this->page])) {
9609                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9610                                                                                 if ($pac['y'] >= $minstartliney) {
9611                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9612                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9613                                                                                 }
9614                                                                         }
9615                                                                 }
9616                                                                 $this->y -= $yshift;
9617                                                         }
9618                                                 }
9619                                         }
9620                                         $this->checkPageBreak($this->lasth);
9621                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9622                                         if ($wfill) {
9623                                                 $this->SetFillColorArray($this->bgcolor);
9624                                         }
9625                                         $startlinex = $this->x;
9626                                         $startliney = $this->y;
9627                                         $minstartliney = $this->y;
9628                                         $startlinepage = $this->page;
9629                                         if (isset($endlinepos)) {
9630                                                 $startlinepos = $endlinepos;
9631                                                 unset($endlinepos);
9632                                         } else {
9633                                                 if (isset($this->footerlen[$this->page])) {
9634                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9635                                                 } else {
9636                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9637                                                 }
9638                                                 $startlinepos = $this->footerpos[$this->page];
9639                                         }
9640                                         $plalign = $lalign;
9641                                         $this->newline = false;
9642                                 }
9643                                 if (isset($opentagpos)) {
9644                                         unset($opentagpos);
9645                                 }
9646                                 if ($dom[$key]['tag']) {
9647                                         if ($dom[$key]['opening']) {
9648                                                 // table content is handled in a special way
9649                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9650                                                         $trid = $dom[$key]['parent'];
9651                                                         $table_el = $dom[$trid]['parent'];
9652                                                         if (!isset($dom[$table_el]['cols'])) {
9653                                                                 $dom[$table_el]['cols'] = $trid['cols'];
9654                                                         }
9655                                                         // calculate cell width
9656                                                         if (isset($dom[($dom[$key]['parent'])]['width'])) {
9657                                                                 $table_width = $this->pixelsToUnits($dom[($dom[$key]['parent'])]['width']);
9658                                                         } else {
9659                                                                 $table_width = $w;
9660                                                         }
9661                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
9662                                                                 $currentcmargin = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding']);
9663                                                         } else {
9664                                                                 $currentcmargin = 0;
9665                                                         }
9666                                                         $this->cMargin = $currentcmargin;
9667                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
9668                                                                 $cellspacing = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing']);
9669                                                         } else {
9670                                                                 $cellspacing = 0;
9671                                                         }
9672                                                         if ($this->rtl) {
9673                                                                 $cellspacingx = -$cellspacing;
9674                                                         } else {
9675                                                                 $cellspacingx = $cellspacing;
9676                                                         }
9677                                                         $colspan = $dom[$key]['attribute']['colspan'];
9678                                                         if (isset($dom[$key]['width'])) {
9679                                                                 $cellw = $this->pixelsToUnits($dom[$key]['width']);
9680                                                         } else {
9681                                                                 $cellw = ($colspan * ($table_width / $dom[$table_el]['cols']));
9682                                                         }
9683                                                         $cellw -= $cellspacing;
9684                                                         if (isset($dom[$key]['content'])) {
9685                                                                 $cell_content = $dom[$key]['content'];
9686                                                         } else {
9687                                                                 $cell_content = "&nbsp;";
9688                                                         }
9689                                                         $tagtype = $dom[$key]['value'];
9690                                                         $parentid = $key;
9691                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
9692                                                                 // move $key index forward
9693                                                                 $key++;
9694                                                         }
9695                                                         if (!isset($dom[$trid]['startpage'])) {
9696                                                                 $dom[$trid]['startpage'] = $this->page;
9697                                                         } else {
9698                                                                 $this->setPage($dom[$trid]['startpage']);
9699                                                         }
9700                                                         if (!isset($dom[$trid]['starty'])) {
9701                                                                 $dom[$trid]['starty'] = $this->y;
9702                                                         } else {
9703                                                                 $this->y = $dom[$trid]['starty'];
9704                                                         }
9705                                                         if (!isset($dom[$trid]['startx'])) {
9706                                                                 $dom[$trid]['startx'] = $this->x;
9707                                                         }
9708                                                         $this->x += ($cellspacingx / 2);
9709                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
9710                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
9711                                                         }       else {
9712                                                                 $rowspan = 1;
9713                                                         }
9714                                                         // skip row-spanned cells started on the previous rows
9715                                                         if (isset($dom[$table_el]['rowspans'])) {
9716                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9717                                                                         if  (($trwsp['startx'] == $this->x) AND (($trwsp['starty'] < $this->y) OR ($trwsp['startpage'] < $this->page)) AND ($trwsp['rowspan'] > 0)) {
9718                                                                                 $this->x = $trwsp['endx'] + $cellspacingx;
9719                                                                         }
9720                                                                 }
9721                                                         }
9722                                                         // add rowspan information to table element
9723                                                         if ($rowspan > 1) {
9724                                                                 if (isset($this->footerlen[$this->page])) {
9725                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9726                                                                 } else {
9727                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9728                                                                 }
9729                                                                 $trintmrkpos = $this->footerpos[$this->page];
9730                                                                 $trsid = array_push($dom[$table_el]['rowspans'], array('rowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
9731                                                         }
9732                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
9733                                                         if ($rowspan > 1) {
9734                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
9735                                                         }
9736                                                         // push background colors
9737                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
9738                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
9739                                                         }
9740
9741                                                         // write the cell content
9742                                                         $this->MultiCell($cellw, 0, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
9743
9744                                                         $this->cMargin = $currentcmargin;
9745                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
9746                                                         
9747                                                         // update the end of row position
9748                                                         if ($rowspan <= 1) {
9749                                                                 if (isset($dom[$trid]['endy'])) {
9750                                                                         if ($this->page == $dom[$trid]['endpage']) {
9751                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
9752                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
9753                                                                                 $dom[$trid]['endy'] = $this->y;
9754                                                                         }
9755                                                                 } else {
9756                                                                         $dom[$trid]['endy'] = $this->y;
9757                                                                 }
9758                                                                 if (isset($dom[$trid]['endpage'])) {
9759                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
9760                                                                 } else {
9761                                                                         $dom[$trid]['endpage'] = $this->page;
9762                                                                 }
9763                                                         } else {
9764                                                         // account for row-spanned cells
9765                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
9766                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
9767                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
9768                                                         }
9769                                                         if (isset($dom[$table_el]['rowspans'])) {
9770                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9771                                                                         if ($trwsp['rowspan'] > 0) {
9772                                                                                 if (isset($dom[$trid]['endpage'])) {
9773                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
9774                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
9775                                                                                         } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[$trid]['endpage']) {
9776                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $trwsp['endy'];
9777                                                                                         } else {
9778                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
9779                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
9780                                                                                         }
9781                                                                                 }
9782                                                                         }
9783                                                                 }
9784                                                         }
9785                                                         $this->x += ($cellspacingx / 2);
9786                                                 } else {
9787                                                         // opening tag (or self-closing tag)
9788                                                         if (!isset($opentagpos)) {
9789                                                                 if (isset($this->footerlen[$this->page])) {
9790                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9791                                                                 } else {
9792                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9793                                                                 }
9794                                                                 $opentagpos = $this->footerpos[$this->page];
9795                                                         }
9796                                                         $this->openHTMLTagHandler($dom, $key, $cell);
9797                                                 }
9798                                         } else {
9799                                                 // closing tag
9800                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
9801                                         }
9802                                 } elseif (strlen($dom[$key]['value']) > 0) {
9803                                         // print list-item
9804                                         if (!empty($this->lispacer)) {
9805                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
9806                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9807                                                 $minstartliney = $this->y;
9808                                                 $tmpx = $this->x;
9809                                                 $lspace = $this->GetStringWidth($this->lispacer."  ");
9810                                                 if ($this->rtl) {
9811                                                         $this->x += $lspace;
9812                                                 } else {
9813                                                         $this->x -= $lspace;
9814                                                 }
9815                                                 $this->Write($this->lasth, $this->lispacer, '', false, '', false, 0, false);
9816                                                 $this->x = $tmpx;
9817                                                 $this->lispacer = "";
9818                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
9819                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9820                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
9821                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
9822                                                         $minstartliney = min($this->y, $minstartliney);
9823                                                 }
9824                                         }
9825                                         // text
9826                                         $this->htmlvspace = 0;
9827                                         if ($newline) {
9828                                                 if ($this->rtl OR $this->tmprtl) {
9829                                                         $dom[$key]['value'] = rtrim($dom[$key]['value']);
9830                                                 } else {
9831                                                         $dom[$key]['value'] = ltrim($dom[$key]['value']);
9832                                                 }
9833                                                 $newline = false;
9834                                         }
9835                                         if ($this->HREF) {
9836                                                 // HTML <a> Link
9837                                                 $strrest = $this->addHtmlLink($this->HREF, $dom[$key]['value'], $wfill, true);
9838                                         } else {
9839                                                 $ctmpmargin = $this->cMargin;
9840                                                 $this->cMargin = 0;
9841                                                 // write only until the end of the line and get the rest
9842                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, "", false, 0, true);
9843                                                 $this->cMargin = $ctmpmargin;
9844                                         }
9845                                         if (strlen($strrest) > 0) {
9846                                                 // store the remaining string on the previous $key position
9847                                                 $this->newline = true;
9848                                                 if ($cell) {
9849                                                         if ($this->rtl) {
9850                                                                 $this->x -= $this->cMargin;
9851                                                         } else {
9852                                                                 $this->x += $this->cMargin;
9853                                                         }
9854                                                 }
9855                                                 $dom[$key]['value'] = ltrim($strrest);
9856                                                 $key--;
9857                                         }
9858                                 }
9859                                 $key++;
9860                         } // end for each $key
9861                         // align the last line
9862                         if (isset($startlinex)) {
9863                                 $yshift = $minstartliney - $startliney;
9864                                 if ($yshift > 0) {
9865                                         $yshift = 0;
9866                                 }
9867                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9868                                         // the last line must be shifted to be aligned as requested
9869                                         $linew = abs($this->endlinex - $startlinex);
9870                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9871                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9872                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9873                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9874                                         } elseif (isset($opentagpos)) {
9875                                                 $midpos = $opentagpos;
9876                                         } elseif (isset($this->footerlen[$startlinepage])) {
9877                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9878                                                 $midpos = $this->footerpos[$startlinepage];
9879                                         } else {
9880                                                 $midpos = 0;
9881                                         }
9882                                         if ($midpos > 0) {
9883                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9884                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9885                                         } else {
9886                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9887                                                 $pend = "";
9888                                         }
9889                                         // calculate shifting amount
9890                                         $mdiff = abs($w - $linew);
9891                                         if ($plalign == "C") {
9892                                                 if ($this->rtl) {
9893                                                         $t_x = -($mdiff / 2);
9894                                                 } else {
9895                                                         $t_x = ($mdiff / 2);
9896                                                 }
9897                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9898                                                 // right alignment on LTR document
9899                                                 $t_x = $mdiff;
9900                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9901                                                 // left alignment on RTL document
9902                                                 $t_x = -$mdiff;
9903                                         } else {
9904                                                 $t_x = 0;
9905                                         }
9906                                         if (($t_x != 0) OR ($yshift < 0)) {
9907                                                 // shift the line
9908                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9909                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9910                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9911                                                 // shift the annotations and links
9912                                                 if (isset($this->PageAnnots[$this->page])) {
9913                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9914                                                                 if ($pac['y'] >= $minstartliney) {
9915                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9916                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9917                                                                 }
9918                                                         }
9919                                                 }
9920                                                 $this->y -= $yshift;
9921                                         }
9922                                 }
9923                         }
9924                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == "table")))) {
9925                                 $this->Ln($this->lasth);
9926                         }
9927                         // restore previous values
9928                         $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
9929                         $this->SetFillColorArray($prevbgcolor);
9930                         $this->SetTextColorArray($prevfgcolor);
9931                         $this->lMargin = $prevlMargin;
9932                         $this->rMargin = $prevrMargin;
9933                         $this->cMargin = $prevcMargin;
9934                         unset($dom);
9935                 }
9936
9937                 /**
9938                  * Process opening tags.
9939                  * @param array $dom html dom array
9940                  * @param int $key current element id
9941                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
9942                  * @access protected
9943                  */
9944                 function openHTMLTagHandler(&$dom, $key, $cell=false) {
9945                         $tag = $dom[$key];
9946                         $parent = $dom[($dom[$key]['parent'])];
9947                         // check for text direction attribute
9948                         if (isset($tag['attribute']['dir'])) {
9949                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
9950                         } else {
9951                                 $this->tmprtl = false;
9952                         }
9953                         //Opening tag
9954                         switch($tag['value']) {
9955                                 case 'table': {
9956                                         $dom[$key]['rowspans'] = array();
9957                                         if (isset($tag['attribute']['cellpadding'])) {
9958                                                 $this->oldcMargin = $this->cMargin;
9959                                                 $this->cMargin = $this->pixelsToUnits($tag['attribute']['cellpadding']);
9960                                         }
9961                                         break;
9962                                 }
9963                                 case 'tr': {
9964                                         // array of columns positions
9965                                         $dom[$key]['cellpos'] = array();
9966                                         break;
9967                                 }
9968                                 case 'td':
9969                                 case 'th': {
9970                                         break;
9971                                 }
9972                                 case 'hr': {
9973                                         $this->addHTMLVertSpace(1, $cell);
9974                                         $this->htmlvspace = 0;
9975                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
9976                                                 $hrWidth = $this->pixelsToUnits($tag['attribute']['width']);
9977                                         } else {
9978                                                 $hrWidth = $this->w - $this->lMargin - $this->rMargin;
9979                                         }
9980                                         $x = $this->GetX();
9981                                         $y = $this->GetY();
9982                                         $prevlinewidth = $this->GetLineWidth();
9983                                         $this->Line($x, $y, $x + $hrWidth, $y);
9984                                         $this->SetLineWidth($prevlinewidth);
9985                                         $this->addHTMLVertSpace(1, $cell);
9986                                         break;
9987                                 }
9988                                 case 'b': {
9989                                         $this->setStyle('b', true);
9990                                         break;
9991                                 }
9992                                 case 'i': {
9993                                         $this->setStyle('i', true);
9994                                         break;
9995                                 }
9996                                 case 'u': {
9997                                         $this->setStyle('u', true);
9998                                         break;
9999                                 }
10000                                 case 'del': {
10001                                         $this->setStyle('d', true);
10002                                         break;
10003                                 }
10004                                 case 'a': {
10005                                         if (array_key_exists('href', $tag['attribute'])) {
10006                                                 $this->HREF = $tag['attribute']['href'];
10007                                         }
10008                                         break;
10009                                 }
10010                                 case 'img': {
10011                                         if (isset($tag['attribute']['src'])) {
10012                                                 // replace relative path with real server path
10013                                                 if ($tag['attribute']['src'][0] == '/') {
10014                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
10015                                                 }
10016                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
10017                                                 if (!isset($tag['attribute']['width'])) {
10018                                                         $tag['attribute']['width'] = 0;
10019                                                 }
10020                                                 if (!isset($tag['attribute']['height'])) {
10021                                                         $tag['attribute']['height'] = 0;
10022                                                 }
10023                                                 //if (!isset($tag['attribute']['align'])) {
10024                                                         // the only alignment supported is "bottom"
10025                                                         // further development is required for other modes.
10026                                                         $tag['attribute']['align'] = 'bottom';
10027                                                 //} 
10028                                                 switch($tag['attribute']['align']) {
10029                                                         case 'top': {
10030                                                                 $align = 'T';
10031                                                                 break;
10032                                                         }
10033                                                         case 'middle': {
10034                                                                 $align = 'M';
10035                                                                 break;
10036                                                         }
10037                                                         case 'bottom': {
10038                                                                 $align = 'B';
10039                                                                 break;
10040                                                         }
10041                                                         default: {
10042                                                                 $align = 'B';
10043                                                                 break;
10044                                                         }
10045                                                 }
10046                                                 $fileinfo = pathinfo($tag['attribute']['src']);
10047                                                 if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
10048                                                         $type = strtolower($fileinfo['extension']);
10049                                                 }
10050                                                 $prevy = $this->y;
10051                                                 if (($type == "eps") OR ($type == "ai")) {
10052                                                         $this->ImageEps($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', true, $align);
10053                                                 } else {
10054                                                         $this->Image($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', '', $align);
10055                                                 }
10056                                                 switch($align) {
10057                                                         case 'T': {
10058                                                                 $this->y = $prevy;
10059                                                                 break;
10060                                                         }
10061                                                         case 'M': {
10062                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
10063                                                                 break;
10064                                                         }
10065                                                         case 'B': {
10066                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
10067                                                                 break;
10068                                                         }
10069                                                 }
10070                                         }
10071                                         break;
10072                                 }
10073                                 case 'dl': {
10074                                         $this->listnum++;
10075                                         break;
10076                                 }
10077                                 case 'dt': {
10078                                         $this->addHTMLVertSpace(1, $cell);
10079                                         break;
10080                                 }
10081                                 case 'dd': {
10082                                         if ($this->rtl) {
10083                                                 $this->rMargin += $this->listindent;
10084                                         } else {
10085                                                 $this->lMargin += $this->listindent;
10086                                         }
10087                                         $this->addHTMLVertSpace(1, $cell);
10088                                         break;
10089                                 }
10090                                 case 'ul':
10091                                 case 'ol': {
10092                                         $this->listnum++;
10093                                         if ($tag['value'] == "ol") {
10094                                                 $this->listordered[$this->listnum] = true;
10095                                         } else {
10096                                                 $this->listordered[$this->listnum] = false;
10097                                         }
10098                                         $this->listcount[$this->listnum] = 0;
10099                                         if ($this->rtl) {
10100                                                 $this->rMargin += $this->listindent;
10101                                         } else {
10102                                                 $this->lMargin += $this->listindent;
10103                                         }
10104                                         break;
10105                                 }
10106                                 case 'li': {
10107                                         $this->Ln('', $cell);
10108                                         if ($tag['value'] == 'li') {
10109                                                 if ($this->listordered[$this->listnum]) {
10110                                                         if (isset($tag['attribute']['value'])) {
10111                                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
10112                                                         }
10113                                                         $this->listcount[$this->listnum]++;
10114                                                         if ($this->rtl) {
10115                                                                 $this->lispacer = ".".($this->listcount[$this->listnum]);
10116                                                         } else {
10117                                                                 $this->lispacer = ($this->listcount[$this->listnum]).".";
10118                                                         }
10119                                                 } else {
10120                                                         //unordered list symbol
10121                                                         $this->lispacer = "-";
10122                                                 }
10123                                         } else {
10124                                                 $this->lispacer = "";
10125                                         }
10126                                         break;
10127                                 }
10128                                 case 'blockquote': {
10129                                         if ($this->rtl) {
10130                                                 $this->rMargin += $this->listindent;
10131                                         } else {
10132                                                 $this->lMargin += $this->listindent;
10133                                         }
10134                                         $this->addHTMLVertSpace(2, $cell);
10135                                         break;
10136                                 }
10137                                 case 'br': {
10138                                         $this->Ln('', $cell);
10139                                         break;
10140                                 }
10141                                 case 'div': {
10142                                         $this->addHTMLVertSpace(2, $cell);
10143                                         break;
10144                                 }
10145                                 case 'p': {
10146                                         $this->addHTMLVertSpace(2, $cell);
10147                                         break;
10148                                 }
10149                                 case 'sup': {
10150                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
10151                                         break;
10152                                 }
10153                                 case 'sub': {
10154                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
10155                                         break;
10156                                 }
10157                                 case 'h1':
10158                                 case 'h2':
10159                                 case 'h3':
10160                                 case 'h4':
10161                                 case 'h5':
10162                                 case 'h6': {
10163                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k);
10164                                         break;
10165                                 }
10166                                 default: {
10167                                         break;
10168                                 }
10169                         }
10170                 }
10171
10172                 /**
10173                  * Process closing tags.
10174                  * @param array $dom html dom array
10175                  * @param int $key current element id
10176                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10177                  * @access protected
10178                  */
10179                 function closeHTMLTagHandler(&$dom, $key, $cell=false) {
10180                         $tag = $dom[$key];
10181                         $parent = $dom[($dom[$key]['parent'])];
10182                         //Closing tag
10183                         switch($tag['value']) {
10184                                 case 'td':
10185                                 case 'th': {
10186                                         break;
10187                                 }
10188                                 case 'tr': {
10189                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
10190                                         // update row-spanned cells
10191                                         if (isset($dom[$table_el]['rowspans'])) {
10192                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
10193                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
10194                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
10195                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $dom[($dom[$key]['parent'])]['endpage']) {
10196                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
10197                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[($dom[$key]['parent'])]['endpage']) {
10198                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
10199                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
10200                                                                 }
10201                                                         }
10202                                                 }
10203                                         }
10204                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
10205                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];
10206                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
10207                                                 $cellspacing = $this->pixelsToUnits($dom[$table_el]['attribute']['cellspacing']);
10208                                                 $this->y += $cellspacing;
10209                                         }                               
10210                                         $this->Ln(0, $cell);
10211                                         $this->x = $dom[($dom[$key]['parent'])]['startx'];
10212                                         break;
10213                                 }
10214                                 case 'table': {
10215                                         // draw borders
10216                                         $table_el = $parent;
10217                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
10218                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
10219                                                         $border = 1;
10220                                         } else {
10221                                                 $border = 0;
10222                                         }
10223                                         // for each row
10224                                         foreach ($table_el['trids'] as $j => $trkey) {
10225                                                 $parent = $dom[$trkey];
10226                                                 $restspace = $this->getPageHeight() - $this->y - $this->getBreakMargin();
10227                                                 // for each cell on the row
10228                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
10229                                                         if (isset($cellpos['rowspanid'])) {
10230                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
10231                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
10232                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
10233                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
10234                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
10235                                                         } else {
10236                                                                 $endy = $parent['endy'];
10237                                                                 $startpage = $parent['startpage'];
10238                                                                 $endpage = $parent['endpage'];
10239                                                         }
10240                                                         $this->setPage($startpage);
10241                                                         $this->y = $parent['starty'];
10242                                                         if ($endpage > $startpage) {
10243                                                                 // design borders around HTML cells.
10244                                                                 for ($page=$startpage; $page <= $endpage; $page++) {
10245                                                                         $this->setPage($page);
10246                                                                         if ($page == $startpage) {
10247                                                                                 $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin();
10248                                                                                 $ch = $restspace;
10249                                                                         } elseif ($page == $endpage) {
10250                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10251                                                                                 $ch = $endy - $this->tMargin;
10252                                                                         } else {
10253                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10254                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
10255                                                                         }
10256
10257                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10258                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
10259                                                                                 $fill = true;
10260                                                                         } else {
10261                                                                                 $fill = false;
10262                                                                         }
10263                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
10264                                                                         $this->x = $cellpos['startx'];
10265                                                                         // design a cell around the text
10266                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10267                                                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10268                                                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10269                                                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10270                                                                         $this->intmrk[$this->page] += strlen($ccode."\n");
10271                                                                 }
10272                                                         } else {
10273                                                                 $ch = $endy - $parent['starty'];
10274                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10275                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
10276                                                                         $fill = true;
10277                                                                 } else {
10278                                                                         $fill = false;
10279                                                                 }
10280                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
10281                                                                 $this->x = $cellpos['startx'];
10282                                                                 $this->y = $parent['starty'];
10283                                                                 // design a cell around the text
10284                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10285                                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10286                                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10287                                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10288                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
10289                                                         }
10290                                                 }
10291                                                 if (isset($table_el['attribute']['cellspacing'])) {
10292                                                         $cellspacing = $this->pixelsToUnits($table_el['attribute']['cellspacing']);
10293                                                         $this->y += $cellspacing;
10294                                                 }
10295                                                 $this->Ln(0, $cell);
10296                                                 $this->x = $parent['startx'];
10297                                         }
10298                                         if (isset($parent['cellpadding'])) {
10299                                                 $this->cMargin = $this->oldcMargin;
10300                                         }
10301                                         //set row height
10302                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10303                                         break;
10304                                 }
10305                                 case 'b': {
10306                                         $this->setStyle('b', false);
10307                                         break;
10308                                 }
10309                                 case 'i': {
10310                                         $this->setStyle('i', false);
10311                                         break;
10312                                 }
10313                                 case 'u': {
10314                                         $this->setStyle('u', false);
10315                                         break;
10316                                 }
10317                                 case 'del': {
10318                                         $this->setStyle('d', false);
10319                                         break;
10320                                 }
10321                                 case 'a': {
10322                                         $this->HREF = '';
10323                                         break;
10324                                 }
10325                                 case 'sup': {
10326                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
10327                                         break;
10328                                 }
10329                                 case 'sub': {
10330                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
10331                                         break;
10332                                 }
10333                                 case 'div': {
10334                                         $this->addHTMLVertSpace(1, $cell);
10335                                         break;
10336                                 }
10337                                 case 'blockquote': {
10338                                         if ($this->rtl) {
10339                                                 $this->rMargin -= $this->listindent;
10340                                         } else {
10341                                                 $this->lMargin -= $this->listindent;
10342                                         }
10343                                         $this->addHTMLVertSpace(2, $cell);
10344                                         break;
10345                                 }
10346                                 case 'p': {
10347                                         $this->addHTMLVertSpace(2, $cell);
10348                                         break;
10349                                 }
10350                                 case 'dl': {
10351                                         $this->listnum--;
10352                                         if ($this->listnum <= 0) {
10353                                                 $this->listnum = 0;
10354                                                 $this->addHTMLVertSpace(2, $cell);
10355                                         }
10356                                         break;
10357                                 }
10358                                 case 'dt': {
10359                                         $this->lispacer = "";
10360                                         break;
10361                                 }
10362                                 case 'dd': {
10363                                         $this->lispacer = "";
10364                                         if ($this->rtl) {
10365                                                 $this->rMargin -= $this->listindent;
10366                                         } else {
10367                                                 $this->lMargin -= $this->listindent;
10368                                         }
10369                                         break;
10370                                 }
10371                                 case 'ul':
10372                                 case 'ol': {
10373                                         $this->listnum--;
10374                                         $this->lispacer = "";
10375                                         if ($this->rtl) {
10376                                                 $this->rMargin -= $this->listindent;
10377                                         } else {
10378                                                 $this->lMargin -= $this->listindent;
10379                                         }
10380                                         if ($this->listnum <= 0) {
10381                                                 $this->listnum = 0;
10382                                                 $this->addHTMLVertSpace(2, $cell);
10383                                         }
10384                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10385                                         break;
10386                                 }
10387                                 case 'li': {
10388                                         $this->lispacer = "";
10389                                         break;
10390                                 }
10391                                 case 'h1':
10392                                 case 'h2':
10393                                 case 'h3':
10394                                 case 'h4':
10395                                 case 'h5':
10396                                 case 'h6': {
10397                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k);
10398                                         break;
10399                                 }
10400                                 default : {
10401                                         break;
10402                                 }
10403                         }
10404                         $this->tmprtl = false;
10405                 }
10406                 
10407                 /**
10408                  * Add vertical spaces if needed.
10409                  * @param int $n number of spaces to add
10410                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10411                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
10412                  * @access protected
10413                  */
10414                 function addHTMLVertSpace($n, $cell=false, $h='') {
10415                         if (is_string($h)) {
10416                                 $vsize = $n * $this->lasth;
10417                         } else {
10418                                 $vsize = $n * $h;
10419                         }
10420                         if ($vsize > $this->htmlvspace) {
10421                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
10422                                 $this->htmlvspace = $vsize;
10423             }
10424         }
10425                 
10426         } // END OF TCPDF CLASS
10427 }
10428 //============================================================+
10429 // END OF FILE
10430 //============================================================+
10431 ?>