eec95ee9509fc7c5aa662e184795e751e90facea
[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   * 7. Line 6261. ereg replaced by preg_match (with start and end delimiter)
147   * 8. Lines 8642,9256 and 9348. split replaced by preg_split.
148   * -------------------------------------------------------------------------------
149   */
150 if (!defined("K_PATH_FONTS"))
151         define ("K_PATH_FONTS", dirname(__FILE__)."/../fonts/");
152 define ("K_PATH_CACHE", dirname(__FILE__)."/../fonts/");
153 define("K_CELL_HEIGHT_RATIO", 1.25);
154
155 //require_once(dirname(__FILE__).'/config/tcpdf_config.php');
156
157 // includes some support files
158
159 /**
160  * unicode data
161  */
162 // only included if unicode
163 //include_once(dirname(__FILE__)."/unicode_data2.php");
164
165 /**
166  * html colors table
167  */
168 require_once(dirname(__FILE__).'/htmlcolors.php');
169
170 /**
171  * barcode class
172  */
173 require_once(dirname(__FILE__)."/barcodes.php");
174
175 /**
176  * HTML entity decode functions
177  */
178 require_once(dirname(__FILE__)."/html_entity_decode_php4.php");
179
180 //
181 // Check image file format against specific TCPDF engine requirements.
182 //
183 function check_image_file($filename)
184 {
185         $test = new TCPDF();
186         if ( !$test->Image($filename, 0, 0) )
187                 return _('Unsupported image file format.');
188         return '';
189 }
190
191 if (!class_exists('TCPDF')) {
192         /**
193          * define default PDF document producer
194          */
195         define('PDF_PRODUCER','TCPDF 4.0.027_PHP4 (http://www.tcpdf.org)');
196
197         /**
198         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
199         * 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>
200         * @name TCPDF
201         * @package com.tecnick.tcpdf
202         * @version 4.0.027_PHP4
203         * @author Nicola Asuni - info@tecnick.com
204         * @link http://www.tcpdf.org
205         * @license http://www.gnu.org/copyleft/lesser.html LGPL
206         */
207         class TCPDF {
208                 
209                 // protected or Protected properties
210
211                 /**
212                 * @var current page number
213                 * @access protected
214                 */
215                 var $page;
216
217                 /**
218                 * @var current object number
219                 * @access protected
220                 */
221                 var $n;
222
223                 /**
224                 * @var array of object offsets
225                 * @access protected
226                 */
227                 var $offsets;
228
229                 /**
230                 * @var buffer holding in-memory PDF
231                 * @access protected
232                 */
233                 var $buffer;
234
235                 /**
236                 * @var array containing pages
237                 * @access protected
238                 */
239                 var $pages = array();
240
241                 /**
242                 * @var current document state
243                 * @access protected
244                 */
245                 var $state;
246
247                 /**
248                 * @var compression flag
249                 * @access protected
250                 */
251                 var $compress;
252
253                 /**
254                 * @var current page orientation (P = Portrait, L = Landscape)
255                 * @access protected
256                 */
257                 var $CurOrientation;
258
259                 /**
260                 * @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>
261                 * @access protected
262                 */
263                 var $pagedim = array();
264
265                 /**
266                 * @var scale factor (number of points in user unit)
267                 * @access protected
268                 */
269                 var $k;
270
271                 /**
272                 * @var width of page format in points
273                 * @access protected
274                 */
275                 var $fwPt;
276
277                 /**
278                 * @var height of page format in points
279                 * @access protected
280                 */
281                 var $fhPt;
282
283                 /**
284                 * @var current width of page in points
285                 * @access protected
286                 */
287                 var $wPt;
288
289                 /**
290                 * @var current height of page in points
291                 * @access protected
292                 */
293                 var $hPt;
294
295                 /**
296                 * @var current width of page in user unit
297                 * @access protected
298                 */
299                 var $w;
300
301                 /**
302                 * @var current height of page in user unit
303                 * @access protected
304                 */
305                 var $h;
306
307                 /**
308                 * @var left margin
309                 * @access protected
310                 */
311                 var $lMargin;
312
313                 /**
314                 * @var top margin
315                 * @access protected
316                 */
317                 var $tMargin;
318
319                 /**
320                 * @var right margin
321                 * @access protected
322                 */
323                 var $rMargin;
324
325                 /**
326                 * @var page break margin
327                 * @access protected
328                 */
329                 var $bMargin;
330
331                 /**
332                 * @var cell internal padding
333                 * @access protected
334                 */
335                 var $cMargin;
336
337                 /**
338                 * @var cell internal padding (previous value)
339                 * @access protected
340                 */
341                 var $oldcMargin;
342
343                 /**
344                 * @var current horizontal position in user unit for cell positioning
345                 * @access protected
346                 */
347                 var $x;
348
349                 /**
350                 * @var current vertical position in user unit for cell positioning
351                 * @access protected
352                 */
353                 var $y;
354
355                 /**
356                 * @var height of last cell printed
357                 * @access protected
358                 */
359                 var $lasth;
360
361                 /**
362                 * @var line width in user unit
363                 * @access protected
364                 */
365                 var $LineWidth;
366
367                 /**
368                 * @var array of standard font names
369                 * @access protected
370                 */
371                 var $CoreFonts;
372
373                 /**
374                 * @var array of used fonts
375                 * @access protected
376                 */
377                 var $fonts = array();
378
379                 /**
380                 * @var array of font files
381                 * @access protected
382                 */
383                 var $FontFiles = array();
384
385                 /**
386                 * @var array of encoding differences
387                 * @access protected
388                 */
389                 var $diffs = array();
390
391                 /**
392                 * @var array of used images
393                 * @access protected
394                 */
395                 var $images = array();
396
397                 /**
398                 * @var array of Annotations in pages
399                 * @access protected
400                 */
401                 var $PageAnnots = array();
402
403                 /**
404                 * @var array of internal links
405                 * @access protected
406                 */
407                 var $links = array();
408
409                 /**
410                 * @var current font family
411                 * @access protected
412                 */
413                 var $FontFamily;
414
415                 /**
416                 * @var current font style
417                 * @access protected
418                 */
419                 var $FontStyle;
420
421                 /**
422                 * @var current font ascent (distance between font top and baseline)
423                 * @access protected
424                 * @since 2.8.000 (2007-03-29)
425                 */
426                 var $FontAscent;
427
428                 /**
429                 * @var current font descent (distance between font bottom and baseline)
430                 * @access protected
431                 * @since 2.8.000 (2007-03-29)
432                 */
433                 var $FontDescent;
434
435                 /**
436                 * @var underlining flag
437                 * @access protected
438                 */
439                 var $underline;
440
441                 /**
442                 * @var current font info
443                 * @access protected
444                 */
445                 var $CurrentFont;
446
447                 /**
448                 * @var current font size in points
449                 * @access protected
450                 */
451                 var $FontSizePt;
452
453                 /**
454                 * @var current font size in user unit
455                 * @access protected
456                 */
457                 var $FontSize;
458
459                 /**
460                 * @var commands for drawing color
461                 * @access protected
462                 */
463                 var $DrawColor;
464
465                 /**
466                 * @var commands for filling color
467                 * @access protected
468                 */
469                 var $FillColor;
470
471                 /**
472                 * @var commands for text color
473                 * @access protected
474                 */
475                 var $TextColor;
476
477                 /**
478                 * @var indicates whether fill and text colors are different
479                 * @access protected
480                 */
481                 var $ColorFlag;
482
483                 /**
484                 * @var word spacing
485                 * @access protected
486                 */
487                 var $ws;
488
489                 /**
490                 * @var automatic page breaking
491                 * @access protected
492                 */
493                 var $AutoPageBreak;
494
495                 /**
496                 * @var threshold used to trigger page breaks
497                 * @access protected
498                 */
499                 var $PageBreakTrigger;
500
501                 /**
502                 * @var flag set when processing footer
503                 * @access protected
504                 */
505                 var $InFooter;
506
507                 /**
508                 * @var zoom display mode
509                 * @access protected
510                 */
511                 var $ZoomMode;
512
513                 /**
514                 * @var layout display mode
515                 * @access protected
516                 */
517                 var $LayoutMode;
518
519                 /**
520                 * @var title
521                 * @access protected
522                 */
523                 var $title;
524
525                 /**
526                 * @var subject
527                 * @access protected
528                 */
529                 var $subject;
530
531                 /**
532                 * @var author
533                 * @access protected
534                 */
535                 var $author;
536
537                 /**
538                 * @var keywords
539                 * @access protected
540                 */
541                 var $keywords;
542
543                 /**
544                 * @var creator
545                 * @access protected
546                 */
547                 var $creator;
548
549                 /**
550                 * @var alias for total number of pages
551                 * @access protected
552                 */
553                 var $AliasNbPages;
554
555                 /**
556                 * @var right-bottom corner X coordinate of inserted image
557                 * @since 2002-07-31
558                 * @author Nicola Asuni
559                 * @access protected
560                 */
561                 var $img_rb_x;
562
563                 /**
564                 * @var right-bottom corner Y coordinate of inserted image
565                 * @since 2002-07-31
566                 * @author Nicola Asuni
567                 * @access protected
568                 */
569                 var $img_rb_y;
570
571                 /**
572                 * @var image scale factor
573                 * @since 2004-06-14
574                 * @author Nicola Asuni
575                 * @access protected
576                 */
577                 var $imgscale = 1;
578
579                 /**
580                 * @var boolean set to true when the input text is unicode (require unicode fonts)
581                 * @since 2005-01-02
582                 * @author Nicola Asuni
583                 * @access protected
584                 */
585                 var $isunicode = false;
586
587                 /**
588                 * @var PDF version
589                 * @since 1.5.3
590                 * @access protected
591                 */
592                 var $PDFVersion = "1.7";
593
594
595                 // ----------------------
596
597                 /**
598                  * @var Minimum distance between header and top page margin.
599                  * @access protected
600                  */
601                 var $header_margin;
602
603                 /**
604                  * @var Minimum distance between footer and bottom page margin.
605                  * @access protected
606                  */
607                 var $footer_margin;
608
609                 /**
610                  * @var original left margin value
611                  * @access protected
612                  * @since 1.53.0.TC013
613                  */
614                 var $original_lMargin;
615
616                 /**
617                  * @var original right margin value
618                  * @access protected
619                  * @since 1.53.0.TC013
620                  */
621                 var $original_rMargin;
622
623                 /**
624                  * @var Header font.
625                  * @access protected
626                  */
627                 var $header_font;
628
629                 /**
630                  * @var Footer font.
631                  * @access protected
632                  */
633                 var $footer_font;
634
635                 /**
636                  * @var Language templates.
637                  * @access protected
638                  */
639                 var $l;
640
641                 /**
642                  * @var Barcode to print on page footer (only if set).
643                  * @access protected
644                  */
645                 var $barcode = false;
646
647                 /**
648                  * @var If true prints header
649                  * @access protected
650                  */
651                 var $print_header = true;
652
653                 /**
654                  * @var If true prints footer.
655                  * @access protected
656                  */
657                 var $print_footer = true;
658
659                 /**
660                  * @var Header image logo.
661                  * @access protected
662                  */
663                 var $header_logo = "";
664
665                 /**
666                  * @var Header image logo width in mm.
667                  * @access protected
668                  */
669                 var $header_logo_width = 30;
670
671                 /**
672                  * @var String to print as title on document header.
673                  * @access protected
674                  */
675                 var $header_title = "";
676
677                 /**
678                  * @var String to print on document header.
679                  * @access protected
680                  */
681                 var $header_string = "";
682
683                 /**
684                  * @var Default number of columns for html table.
685                  * @access protected
686                  */
687                 var $default_table_columns = 4;
688
689
690                 // variables for html parser
691
692                 /**
693                  * @var HTML PARSER: store current link.
694                  * @access protected
695                  */
696                 var $HREF;
697
698                 /**
699                  * @var store available fonts list.
700                  * @access protected
701                  */
702                 var $fontlist = array();
703
704                 /**
705                  * @var current foreground color
706                  * @access protected
707                  */
708                 var $fgcolor;
709
710                 /**
711                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
712                  * @access protected
713                  */
714                 var $listordered = array();
715
716                 /**
717                  * @var HTML PARSER: array count list items on nested lists.
718                  * @access protected
719                  */
720                 var $listcount = array();
721
722                 /**
723                  * @var HTML PARSER: current list nesting level.
724                  * @access protected
725                  */
726                 var $listnum = 0;
727
728                 /**
729                  * @var HTML PARSER: indent amount for lists.
730                  * @access protected
731                  */
732                 var $listindent;
733
734                 /**
735                  * @var current background color
736                  * @access protected
737                  */
738                 var $bgcolor;
739
740                 /**
741                  * @var Store temporary font size in points.
742                  * @access protected
743                  */
744                 var $tempfontsize = 10;
745
746                 /**
747                  * @var Bold font style status.
748                  * @access protected
749                  */
750                 var $b;
751
752                 /**
753                  * @var Underlined font style status.
754                  * @access protected
755                  */
756                 var $u;
757
758                 /**
759                  * @var Italic font style status.
760                  * @access protected
761                  */
762                 var $i;
763
764                 /**
765                  * @var Line through font style status.
766                  * @access protected
767                  * @since 2.8.000 (2008-03-19)
768                  */
769                 var $d;
770
771                 /**
772                  * @var spacer for LI tags.
773                  * @access protected
774                  */
775                 var $lispacer = "";
776
777                 /**
778                  * @var default encoding
779                  * @access protected
780                  * @since 1.53.0.TC010
781                  */
782                 var $encoding = "UTF-8";
783
784                 /**
785                  * @var PHP internal encoding
786                  * @access protected
787                  * @since 1.53.0.TC016
788                  */
789                 var $internal_encoding;
790
791                 /**
792                  * @var indicates if the document language is Right-To-Left
793                  * @access protected
794                  * @since 2.0.000
795                  */
796                 var $rtl = false;
797
798                 /**
799                  * @var used to force RTL or LTR string inversion
800                  * @access protected
801                  * @since 2.0.000
802                  */
803                 var $tmprtl = false;
804
805                 // --- Variables used for document encryption:
806
807                 /**
808                  * Indicates whether document is protected
809                  * @access protected
810                  * @since 2.0.000 (2008-01-02)
811                  */
812                 var $encrypted;
813
814                 /**
815                  * U entry in pdf document
816                  * @access protected
817                  * @since 2.0.000 (2008-01-02)
818                  */
819                 var $Uvalue;
820
821                 /**
822                  * O entry in pdf document
823                  * @access protected
824                  * @since 2.0.000 (2008-01-02)
825                  */
826                 var $Ovalue;
827
828                 /**
829                  * P entry in pdf document
830                  * @access protected
831                  * @since 2.0.000 (2008-01-02)
832                  */
833                 var $Pvalue;
834
835                 /**
836                  * encryption object id
837                  * @access protected
838                  * @since 2.0.000 (2008-01-02)
839                  */
840                 var $enc_obj_id;
841
842                 /**
843                  * last RC4 key encrypted (cached for optimisation)
844                  * @access protected
845                  * @since 2.0.000 (2008-01-02)
846                  */
847                 var $last_rc4_key;
848
849                 /**
850                  * last RC4 computed key
851                  * @access protected
852                  * @since 2.0.000 (2008-01-02)
853                  */
854                 var $last_rc4_key_c;
855
856                 // --- bookmark ---
857
858                 /**
859                  * Outlines for bookmark
860                  * @access protected
861                  * @since 2.1.002 (2008-02-12)
862                  */
863                 var $outlines = array();
864
865                 /**
866                  * Outline root for bookmark
867                  * @access protected
868                  * @since 2.1.002 (2008-02-12)
869                  */
870                 var $OutlineRoot;
871
872
873                 // --- javascript and form ---
874
875                 /**
876                  * javascript code
877                  * @access protected
878                  * @since 2.1.002 (2008-02-12)
879                  */
880                 var $javascript = "";
881
882                 /**
883                  * javascript counter
884                  * @access protected
885                  * @since 2.1.002 (2008-02-12)
886                  */
887                 var $n_js;
888
889                 /**
890                  * line trough state
891                  * @access protected
892                  * @since 2.8.000 (2008-03-19)
893                  */
894                 var $linethrough;
895
896                 // --- Variables used for User's Rights ---
897                 // See PDF reference chapter 8.7 Digital Signatures
898
899                 /**
900                  * If true enables user's rights on PDF reader
901                  * @access protected
902                  * @since 2.9.000 (2008-03-26)
903                  */
904                 var $ur;
905
906                 /**
907                  * Names specifying additional document-wide usage rights for the document.
908                  * @access protected
909                  * @since 2.9.000 (2008-03-26)
910                  */
911                 var $ur_document;
912
913                 /**
914                  * Names specifying additional annotation-related usage rights for the document.
915                  * @access protected
916                  * @since 2.9.000 (2008-03-26)
917                  */
918                 var $ur_annots;
919
920                 /**
921                  * Names specifying additional form-field-related usage rights for the document.
922                  * @access protected
923                  * @since 2.9.000 (2008-03-26)
924                  */
925                 var $ur_form;
926
927                 /**
928                  * Names specifying additional signature-related usage rights for the document.
929                  * @access protected
930                  * @since 2.9.000 (2008-03-26)
931                  */
932                 var $ur_signature;
933
934                 /**
935                  * Dot Per Inch Document Resolution (do not change)
936                  * @access protected
937                  * @since 3.0.000 (2008-03-27)
938                  */
939                 var $dpi = 72;
940
941                 /**
942                  * Indicates whether a new page group was requested
943                  * @access protected
944                  * @since 3.0.000 (2008-03-27)
945                  */
946                 var $newpagegroup;
947
948                 /**
949                  * Contains the number of pages of the groups
950                  * @access protected
951                  * @since 3.0.000 (2008-03-27)
952                  */
953                 var $pagegroups;
954
955                 /**
956                  * Contains the alias of the current page group
957                  * @access protected
958                  * @since 3.0.000 (2008-03-27)
959                  */
960                 var $currpagegroup;
961
962                 /**
963                  * Restrict the rendering of some elements to screen or printout.
964                  * @access protected
965                  * @since 3.0.000 (2008-03-27)
966                  */
967                 var $visibility="all";
968
969                 /**
970                  * Print visibility.
971                  * @access protected
972                  * @since 3.0.000 (2008-03-27)
973                  */
974                 var $n_ocg_print;
975
976                 /**
977                  * View visibility.
978                  * @access protected
979                  * @since 3.0.000 (2008-03-27)
980                  */
981                 var $n_ocg_view;
982
983                 /**
984                  * Array of transparency objects and parameters.
985                  * @access protected
986                  * @since 3.0.000 (2008-03-27)
987                  */
988                 var $extgstates;
989
990                 /**
991                  * Set the default JPEG compression quality (1-100)
992                  * @access protected
993                  * @since 3.0.000 (2008-03-27)
994                  */
995                 var $jpeg_quality;
996
997                 /**
998                  * Default cell height ratio.
999                  * @access protected
1000                  * @since 3.0.014 (2008-05-23)
1001                  */
1002                 var $cell_height_ratio = K_CELL_HEIGHT_RATIO;
1003
1004                 /**
1005                  * PDF viewer preferences.
1006                  * @access protected
1007                  * @since 3.1.000 (2008-06-09)
1008                  */
1009                 var $viewer_preferences;
1010
1011                 /**
1012                  * A name object specifying how the document should be displayed when opened.
1013                  * @access protected
1014                  * @since 3.1.000 (2008-06-09)
1015                  */
1016                 var $PageMode;
1017
1018                 /**
1019                  * Array for storing gradient information.
1020                  * @access protected
1021                  * @since 3.1.000 (2008-06-09)
1022                  */
1023                 var $gradients = array();
1024
1025                 /**
1026                  * Array used to store positions inside the pages buffer.
1027                  * keys are the page numbers
1028                  * @access protected
1029                  * @since 3.2.000 (2008-06-26)
1030                  */
1031                 var $intmrk = array();
1032
1033                 /**
1034                  * Array used to store footer positions of each page.
1035                  * @access protected
1036                  * @since 3.2.000 (2008-07-01)
1037                  */
1038                 var $footerpos = array();
1039
1040
1041                 /**
1042                  * Array used to store footer lenght of each page.
1043                  * @access protected
1044                  * @since 4.0.014 (2008-07-29)
1045                  */
1046                 var $footerlen = array();
1047
1048                 /**
1049                  * True if a newline is created.
1050                  * @access protected
1051                  * @since 3.2.000 (2008-07-01)
1052                  */
1053                 var $newline = true;
1054
1055                 /**
1056                  * End position of the latest inserted line
1057                  * @access protected
1058                  * @since 3.2.000 (2008-07-01)
1059                  */
1060                 var $endlinex = 0;
1061
1062                 /**
1063                  * PDF string for last line width
1064                  * @access protected
1065                  * @since 4.0.006 (2008-07-16)
1066                  */
1067                 var $linestyleWidth = "";
1068
1069                 /**
1070                  * PDF string for last line width
1071                  * @access protected
1072                  * @since 4.0.006 (2008-07-16)
1073                  */
1074                 var $linestyleCap = "0 J";
1075
1076                 /**
1077                  * PDF string for last line width
1078                  * @access protected
1079                  * @since 4.0.006 (2008-07-16)
1080                  */
1081                 var $linestyleJoin = "0 j";
1082
1083                 /**
1084                  * PDF string for last line width
1085                  * @access protected
1086                  * @since 4.0.006 (2008-07-16)
1087                  */
1088                 var $linestyleDash = "[] 0 d";
1089
1090                 /**
1091                  * True if marked-content sequence is open
1092                  * @access protected
1093                  * @since 4.0.013 (2008-07-28)
1094                  */
1095                 var $openMarkedContent = false;
1096
1097                 /**
1098                  * Count the latest inserted vertical spaces on HTML.
1099                  * @access protected
1100                  * @since 4.0.021 (2008-08-24)
1101                  */
1102                 var $htmlvspace = 0;
1103                 
1104                 /**
1105                  * Array of Spot colors
1106                  * @access protected
1107                  * @since 4.0.024 (2008-09-12)
1108                  */
1109                 var $spot_colors = array();
1110
1111                 //------------------------------------------------------------
1112                 // METHODS
1113                 //------------------------------------------------------------
1114
1115                 /**
1116                  * This is the class constructor.
1117                  * It allows to set up the page format, the orientation and
1118                  * the measure unit used in all the methods (except for the font sizes).
1119                  * @since 1.0
1120                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1121                  * @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.
1122                  * @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>
1123                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1124                  * @param String $encoding charset encoding; default is UTF-8
1125                  */
1126                 function __construct($orientation='P', $unit='mm', $format='A4', $uni=true, $encoding="UTF-8") {
1127                         if ($uni) // Fix for FrontAccounting
1128                         {
1129                                 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
1130                                 include_once(dirname(__FILE__)."/unicode_data2.php");
1131                         }
1132                         /* Set internal character encoding to ASCII */
1133                         if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) {
1134                                 $this->internal_encoding = mb_internal_encoding();
1135                                 mb_internal_encoding("ASCII");
1136                         }
1137                         // set language direction
1138
1139                         $this->rtl = @$this->l['a_meta_dir']=='rtl' ? true : false;
1140                         $this->tmprtl = false;
1141                         //Some checks
1142                         $this->_dochecks();
1143                         //Initialization of properties
1144                         $this->isunicode = $uni;
1145                         $this->page = 0;
1146                         $this->pagedim = array();
1147                         $this->n = 2;
1148                         $this->buffer = '';
1149                         $this->pages = array();
1150                         $this->state = 0;
1151                         $this->fonts = array();
1152                         $this->FontFiles = array();
1153                         $this->diffs = array();
1154                         $this->images = array();
1155                         $this->links = array();
1156                         $this->gradients = array();
1157                         $this->InFooter = false;
1158                         $this->lasth = 0;
1159                         $this->FontFamily = 'helvetica';
1160                         $this->FontStyle = '';
1161                         $this->FontSizePt = 12;
1162                         $this->underline = false;
1163                         $this->linethrough = false;
1164                         $this->DrawColor = '0 G';
1165                         $this->FillColor = '0 g';
1166                         $this->TextColor = '0 g';
1167                         $this->ColorFlag = false;
1168                         $this->ws = 0;
1169                         // encryption values
1170                         $this->encrypted = false;
1171                         $this->last_rc4_key = '';
1172                         $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";
1173                         //Standard Unicode fonts
1174                         $this->CoreFonts = array(
1175                                 'courier'=>'Courier',
1176                                 'courierB'=>'Courier-Bold',
1177                                 'courierI'=>'Courier-Oblique',
1178                                 'courierBI'=>'Courier-BoldOblique',
1179                                 'helvetica'=>'Helvetica',
1180                                 'helveticaB'=>'Helvetica-Bold',
1181                                 'helveticaI'=>'Helvetica-Oblique',
1182                                 'helveticaBI'=>'Helvetica-BoldOblique',
1183                                 'times'=>'Times-Roman',
1184                                 'timesB'=>'Times-Bold',
1185                                 'timesI'=>'Times-Italic',
1186                                 'timesBI'=>'Times-BoldItalic',
1187                                 'symbol'=>'Symbol',
1188                                 'zapfdingbats'=>'ZapfDingbats'
1189                         );
1190                         //Set scale factor
1191                         $this->setPageUnit($unit);
1192                         // set page format and orientation
1193                         $this->setPageFormat($format, $orientation);
1194                         //Page margins (1 cm)
1195                         $margin = 28.35 / $this->k;
1196                         $this->SetMargins($margin,$margin);
1197                         //Interior cell margin (1 mm)
1198                         $this->cMargin = $margin / 10;
1199                         //Line width (0.2 mm)
1200                         $this->LineWidth = 0.57 / $this->k;
1201                         $this->linestyleWidth = sprintf('%.2f w', ($this->LineWidth * $this->k));
1202                         $this->linestyleCap = "0 J";
1203                         $this->linestyleJoin = "0 j";
1204                         $this->linestyleDash = "[] 0 d";
1205                         //Automatic page break
1206                         $this->SetAutoPageBreak(true, 2*$margin);
1207                         //Full width display mode
1208                         $this->SetDisplayMode('fullwidth');
1209                         //Compression
1210                         $this->SetCompression(true);
1211                         //Set default PDF version number
1212                         $this->PDFVersion = "1.7";
1213                         $this->encoding = $encoding;
1214                         $this->HREF = '';
1215                         $this->getFontsList();
1216                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1217                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1218                         $this->extgstates = array();
1219                         // user's rights
1220                         $this->ur = false;
1221                         $this->ur_document = "/FullSave";
1222                         $this->ur_annots = "/Create/Delete/Modify/Copy/Import/Export";
1223                         $this->ur_form = "/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate";
1224                         $this->ur_signature = "/Modify";
1225                         // set default JPEG quality
1226                         $this->jpeg_quality = 75;
1227                         // initialize some settings
1228                         $this->utf8Bidi(array(""));
1229                 }
1230
1231                 /**
1232                  * Default destructor.
1233                  * @since 1.53.0.TC016
1234                  */
1235                 function TCPDFDestruct() {
1236                         // restore internal encoding
1237                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1238                                 mb_internal_encoding($this->internal_encoding);
1239                         }
1240                 }
1241
1242                 /**
1243                 * Set the units of measure for the document.
1244                 * @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.
1245                 * @since 3.0.015 (2008-06-06)
1246                 */
1247                 function setPageUnit($unit) {
1248                 //Set scale factor
1249                         switch (strtolower($unit)) {
1250                                 // points
1251                                 case 'pt': {
1252                                         $this->k = 1;
1253                                         break;
1254                                 }
1255                                 // millimeters
1256                                 case 'mm': {
1257                                         $this->k = $this->dpi / 25.4;
1258                                         break;
1259                                 }
1260                                 // centimeters
1261                                 case 'cm': {
1262                                         $this->k = $this->dpi / 2.54;
1263                                         break;
1264                                 }
1265                                 // inches
1266                                 case 'in': {
1267                                         $this->k = $this->dpi;
1268                                         break;
1269                                 }
1270                                 // unsupported unit
1271                                 default : {
1272                                         $this->Error('Incorrect unit: '.$unit);
1273                                         break;
1274                                 }
1275                         }
1276                         if (isset($this->CurOrientation)) {
1277                                         $this->setPageOrientation($this->CurOrientation);
1278                         }
1279                 }
1280
1281                 /**
1282                 * Set the page format
1283                 * @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>
1284                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1285                 * @since 3.0.015 (2008-06-06)
1286                 */
1287                 function setPageFormat($format, $orientation="P") {
1288                         //Page format
1289                         if (is_string($format)) {
1290                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1291                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1292                                 switch (strtoupper($format)){
1293                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1294                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1295                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1296                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1297                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1298                                         case 'A3': {$format = array(841.89,1190.55); break;}
1299                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1300                                         case 'A5': {$format = array(419.53,595.28); break;}
1301                                         case 'A6': {$format = array(297.64,419.53); break;}
1302                                         case 'A7': {$format = array(209.76,297.64); break;}
1303                                         case 'A8': {$format = array(147.40,209.76); break;}
1304                                         case 'A9': {$format = array(104.88,147.40); break;}
1305                                         case 'A10': {$format = array(73.70,104.88); break;}
1306                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1307                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1308                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1309                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1310                                         case 'B4': {$format = array(708.66,1000.63); break;}
1311                                         case 'B5': {$format = array(498.90,708.66); break;}
1312                                         case 'B6': {$format = array(354.33,498.90); break;}
1313                                         case 'B7': {$format = array(249.45,354.33); break;}
1314                                         case 'B8': {$format = array(175.75,249.45); break;}
1315                                         case 'B9': {$format = array(124.72,175.75); break;}
1316                                         case 'B10': {$format = array(87.87,124.72); break;}
1317                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1318                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1319                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1320                                         case 'C3': {$format = array(918.43,1298.27); break;}
1321                                         case 'C4': {$format = array(649.13,918.43); break;}
1322                                         case 'C5': {$format = array(459.21,649.13); break;}
1323                                         case 'C6': {$format = array(323.15,459.21); break;}
1324                                         case 'C7': {$format = array(229.61,323.15); break;}
1325                                         case 'C8': {$format = array(161.57,229.61); break;}
1326                                         case 'C9': {$format = array(113.39,161.57); break;}
1327                                         case 'C10': {$format = array(79.37,113.39); break;}
1328                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1329                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1330                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1331                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1332                                         case 'RA4': {$format = array(609.45,864.57); break;}
1333                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1334                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1335                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1336                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1337                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1338                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1339                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1340                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1341                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1342                                 }
1343                                 $this->fwPt = $format[0];
1344                                 $this->fhPt = $format[1];
1345                         }
1346                         else {
1347                                 $this->fwPt = $format[0] * $this->k;
1348                                 $this->fhPt = $format[1] * $this->k;
1349                         }
1350                         $this->setPageOrientation($orientation);
1351                 }
1352
1353
1354                 /**
1355                 * Set page orientation.
1356                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1357                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1358                 * @param float $bottommargin bottom margin of the page.
1359                 * @since 3.0.015 (2008-06-06)
1360                 */
1361                 function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1362                         $orientation = strtoupper($orientation);
1363                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1364                                 $this->CurOrientation = 'P';
1365                                 $this->wPt = $this->fwPt;
1366                                 $this->hPt = $this->fhPt;
1367                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1368                                 $this->CurOrientation = 'L';
1369                                 $this->wPt = $this->fhPt;
1370                                 $this->hPt = $this->fwPt;
1371                         }
1372                         else {
1373                                 $this->Error('Incorrect orientation: '.$orientation);
1374                         }
1375                         $this->w = $this->wPt / $this->k;
1376                         $this->h = $this->hPt / $this->k;
1377                         if (empty($autopagebreak)) {
1378                                 if (isset($this->AutoPageBreak)) {
1379                                         $autopagebreak = $this->AutoPageBreak;
1380                                 } else {
1381                                         $autopagebreak = true;
1382                                 }
1383                         }
1384                         if (empty($bottommargin)) {
1385                                 if (isset($this->bMargin)) {
1386                                         $bottommargin = $this->bMargin;
1387                                 } else {
1388                                         // default value = 2 cm
1389                                         $bottommargin = 2 * 28.35 / $this->k;
1390                                 }
1391                         }
1392                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1393                         // store page dimensions
1394                         $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);
1395                 }
1396
1397                 /**
1398                  * Enable or disable Right-To-Left language mode
1399                  * @param Boolean $enable if true enable Right-To-Left language mode.
1400                  * @since 2.0.000 (2008-01-03)
1401                  */
1402                 function setRTL($enable) {
1403                         $this->rtl = $enable ? true : false;
1404                         $this->tmprtl = false;
1405                 }
1406
1407                 /**
1408                  * Return the RTL status
1409                  * @return boolean
1410                  * @since 4.0.012 (2008-07-24)
1411                  */
1412                 function getRTL() {
1413                         return $this->rtl;
1414                 }
1415
1416                 /**
1417                 * Force temporary RTL language direction
1418                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1419                 * @since 2.1.000 (2008-01-09)
1420                 */
1421                 function setTempRTL($mode) {
1422                         switch ($mode) {
1423                                 case false:
1424                                 case 'L':
1425                                 case 'R': {
1426                                         $this->tmprtl = $mode;
1427                                 }
1428                         }
1429                 }
1430
1431                 /**
1432                 * Set the last cell height.
1433                 * @param float $h cell height.
1434                 * @author Nicola Asuni
1435                 * @since 1.53.0.TC034
1436                 */
1437                 function setLastH($h) {
1438                         $this->lasth = $h;
1439                 }
1440
1441                 /**
1442                 * Get the last cell height.
1443                 * @return last cell height
1444                 * @since 4.0.017 (2008-08-05)
1445                 */
1446                 function getLastH() {
1447                         return $this->lasth;
1448                 }
1449
1450                 /**
1451                 * Set the image scale.
1452                 * @param float $scale image scale.
1453                 * @author Nicola Asuni
1454                 * @since 1.5.2
1455                 */
1456                 function setImageScale($scale) {
1457                         $this->imgscale = $scale;
1458                 }
1459
1460                 /**
1461                 * Returns the image scale.
1462                 * @return float image scale.
1463                 * @author Nicola Asuni
1464                 * @since 1.5.2
1465                 */
1466                 function getImageScale() {
1467                         return $this->imgscale;
1468                 }
1469
1470                 /**
1471                 * Returns the page width in units.
1472                 * @return int page width.
1473                 * @author Nicola Asuni
1474                 * @since 1.5.2
1475                 */
1476                 function getPageWidth() {
1477                         return $this->w;
1478                 }
1479
1480                 /**
1481                 * Returns the page height in units.
1482                 * @return int page height.
1483                 * @author Nicola Asuni
1484                 * @since 1.5.2
1485                 */
1486                 function getPageHeight() {
1487                         return $this->h;
1488                 }
1489
1490                 /**
1491                 * Returns the page break margin.
1492                 * @return int page break margin.
1493                 * @author Nicola Asuni
1494                 * @since 1.5.2
1495                 */
1496                 function getBreakMargin() {
1497                         return $this->bMargin;
1498                 }
1499
1500                 /**
1501                 * Returns the scale factor (number of points in user unit).
1502                 * @return int scale factor.
1503                 * @author Nicola Asuni
1504                 * @since 1.5.2
1505                 */
1506                 function getScaleFactor() {
1507                         return $this->k;
1508                 }
1509
1510                 /**
1511                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1512                 * @param float $left Left margin.
1513                 * @param float $top Top margin.
1514                 * @param float $right Right margin. Default value is the left one.
1515                 * @since 1.0
1516                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1517                 */
1518                 function SetMargins($left, $top, $right=-1) {
1519                         //Set left, top and right margins
1520                         $this->lMargin = $left;
1521                         $this->tMargin = $top;
1522                         if ($right == -1) {
1523                                 $right = $left;
1524                         }
1525                         $this->rMargin = $right;
1526                 }
1527
1528                 /**
1529                 * 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.
1530                 * @param float $margin The margin.
1531                 * @since 1.4
1532                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1533                 */
1534                 function SetLeftMargin($margin) {
1535                         //Set left margin
1536                         $this->lMargin=$margin;
1537                         if (($this->page > 0) AND ($this->x < $margin)) {
1538                                 $this->x = $margin;
1539                         }
1540                 }
1541
1542                 /**
1543                 * Defines the top margin. The method can be called before creating the first page.
1544                 * @param float $margin The margin.
1545                 * @since 1.5
1546                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1547                 */
1548                 function SetTopMargin($margin) {
1549                         //Set top margin
1550                         $this->tMargin=$margin;
1551                         if (($this->page > 0) AND ($this->y < $margin)) {
1552                                 $this->y = $margin;
1553                         }
1554                 }
1555
1556                 /**
1557                 * Defines the right margin. The method can be called before creating the first page.
1558                 * @param float $margin The margin.
1559                 * @since 1.5
1560                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1561                 */
1562                 function SetRightMargin($margin) {
1563                         $this->rMargin=$margin;
1564                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1565                                 $this->x = $this->w - $margin;
1566                         }
1567                 }
1568
1569                 /**
1570                 * Set the internal Cell padding.
1571                 * @param float $pad internal padding.
1572                 * @since 2.1.000 (2008-01-09)
1573                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1574                 */
1575                 function SetCellPadding($pad) {
1576                         $this->cMargin = $pad;
1577                 }
1578
1579                 /**
1580                 * 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.
1581                 * @param boolean $auto Boolean indicating if mode should be on or off.
1582                 * @param float $margin Distance from the bottom of the page.
1583                 * @since 1.0
1584                 * @see Cell(), MultiCell(), AcceptPageBreak()
1585                 */
1586                 function SetAutoPageBreak($auto, $margin=0) {
1587                         //Set auto page break mode and triggering margin
1588                         $this->AutoPageBreak = $auto;
1589                         $this->bMargin = $margin;
1590                         $this->PageBreakTrigger = $this->h - $margin;
1591                 }
1592
1593                 /**
1594                 * Defines the way the document is to be displayed by the viewer.
1595                 * @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>
1596                 * @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>
1597                 * @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>
1598                 * @since 1.2
1599                 */
1600                 function SetDisplayMode($zoom, $layout='SinglePage', $mode="UseNone") {
1601                         //Set display mode in viewer
1602                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1603                                 $this->ZoomMode = $zoom;
1604                         } else {
1605                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1606                         }
1607                         switch ($layout) {
1608                                 case "default":
1609                                 case "single":
1610                                 case "SinglePage": {
1611                                         $this->LayoutMode = "SinglePage";
1612                                         break;
1613                                 }
1614                                 case "continuous":
1615                                 case "OneColumn": {
1616                                         $this->LayoutMode = "OneColumn";
1617                                         break;
1618                                 }
1619                                 case "two":
1620                                 case "TwoColumnLeft": {
1621                                         $this->LayoutMode = "TwoColumnLeft";
1622                                         break;
1623                                 }
1624                                 case "TwoColumnRight": {
1625                                         $this->LayoutMode = "TwoColumnRight";
1626                                         break;
1627                                 }
1628                                 case "TwoPageLeft": {
1629                                         $this->LayoutMode = "TwoPageLeft";
1630                                         break;
1631                                 }
1632                                 case "TwoPageRight": {
1633                                         $this->LayoutMode = "TwoPageRight";
1634                                         break;
1635                                 }
1636                                 default: {
1637                                         $this->LayoutMode = "SinglePage";
1638                                 }
1639                         }
1640                         switch ($mode) {
1641                                 case "UseNone": {
1642                                         $this->PageMode = "UseNone";
1643                                         break;
1644                                 }
1645                                 case "UseOutlines": {
1646                                         $this->PageMode = "UseOutlines";
1647                                         break;
1648                                 }
1649                                 case "UseThumbs": {
1650                                         $this->PageMode = "UseThumbs";
1651                                         break;
1652                                 }
1653                                 case "FullScreen": {
1654                                         $this->PageMode = "FullScreen";
1655                                         break;
1656                                 }
1657                                 case "UseOC": {
1658                                         $this->PageMode = "UseOC";
1659                                         break;
1660                                 }
1661                                 case "": {
1662                                         $this->PageMode = "UseAttachments";
1663                                         break;
1664                                 }
1665                                 default: {
1666                                         $this->PageMode = "UseNone";
1667                                 }
1668                         }
1669                 }
1670
1671                 /**
1672                 * 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.
1673                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1674                 * @param boolean $compress Boolean indicating if compression must be enabled.
1675                 * @since 1.4
1676                 */
1677                 function SetCompression($compress) {
1678                         //Set page compression
1679                         if (function_exists('gzcompress')) {
1680                                 $this->compress = $compress;
1681                         } else {
1682                                 $this->compress = false;
1683                         }
1684                 }
1685
1686                 /**
1687                 * Defines the title of the document.
1688                 * @param string $title The title.
1689                 * @since 1.2
1690                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1691                 */
1692                 function SetTitle($title) {
1693                         //Title of document
1694                         $this->title = $title;
1695                 }
1696
1697                 /**
1698                 * Defines the subject of the document.
1699                 * @param string $subject The subject.
1700                 * @since 1.2
1701                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1702                 */
1703                 function SetSubject($subject) {
1704                         //Subject of document
1705                         $this->subject = $subject;
1706                 }
1707
1708                 /**
1709                 * Defines the author of the document.
1710                 * @param string $author The name of the author.
1711                 * @since 1.2
1712                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
1713                 */
1714                 function SetAuthor($author) {
1715                         //Author of document
1716                         $this->author = $author;
1717                 }
1718
1719                 /**
1720                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
1721                 * @param string $keywords The list of keywords.
1722                 * @since 1.2
1723                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
1724                 */
1725                 function SetKeywords($keywords) {
1726                         //Keywords of document
1727                         $this->keywords = $keywords;
1728                 }
1729
1730                 /**
1731                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
1732                 * @param string $creator The name of the creator.
1733                 * @since 1.2
1734                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
1735                 */
1736                 function SetCreator($creator) {
1737                         //Creator of document
1738                         $this->creator = $creator;
1739                 }
1740
1741                 /**
1742                 * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
1743                 * @param string $alias The alias. Default value: {nb}.
1744                 * @since 1.4
1745                 * @see getAliasNbPages(), PageNo(), Footer()
1746                 */
1747                 function AliasNbPages($alias='{nb}') {
1748                         //Define an alias for total number of pages
1749                         $this->AliasNbPages = $alias;
1750                 }
1751                 
1752                 /**
1753                  * Returns the string alias used for the total number of pages.
1754          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
1755                  * @return string
1756                  * @since 4.0.018 (2008-08-08)
1757                  * @see AliasNbPages(), PageNo(), Footer()
1758                 */
1759                 function getAliasNbPages() {
1760                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
1761                                 return "{".$this->AliasNbPages."}";
1762             }
1763                         return $this->AliasNbPages;
1764                 }
1765
1766                 /**
1767                 * 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.
1768                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
1769                 * @param string $msg The error message
1770                 * @since 1.0
1771                 */
1772                 function Error($msg) {
1773                         //Fatal error
1774                         display_error('<strong>TCPDF error: </strong>'.$msg);
1775                         exit;
1776                 }
1777
1778                 /**
1779                 * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
1780                 * Note: no page is created by this method
1781                 * @since 1.0
1782                 * @see AddPage(), Close()
1783                 */
1784                 function Open() {
1785                         //Begin document
1786                         $this->state = 1;
1787                 }
1788
1789                 /**
1790                 * 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.
1791                 * @since 1.0
1792                 * @see Open(), Output()
1793                 */
1794                 function Close() {
1795                         //Terminate document
1796                         if ($this->state == 3) {
1797                                 return;
1798                         }
1799                         if ($this->page == 0) {
1800                                 $this->AddPage();
1801                         }
1802                         //Page footer
1803                         $this->setFooter();
1804                         //Close page
1805                         $this->_endpage();
1806                         //Close document
1807                         $this->_enddoc();
1808                 }
1809
1810                 /**
1811                 * Move pointer at the specified document page and update page dimensions.
1812                 * @param int $pnum page number
1813                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
1814                 * @since 2.1.000 (2008-01-07)
1815                 * @see getPage(), lastpage(), getNumPages()
1816                 */
1817                 function setPage($pnum, $resetmargins=false) {
1818                         if (($pnum > 0) AND ($pnum <= count($this->pages))) {
1819                                 $this->page = $pnum;
1820                                 $this->wPt = $this->pagedim[$this->page]['w'];
1821                                 $this->hPt = $this->pagedim[$this->page]['h'];
1822                                 $this->w = $this->wPt / $this->k;
1823                                 $this->h = $this->hPt / $this->k;
1824                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
1825                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
1826                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
1827                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
1828                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
1829                                 if ($resetmargins) {
1830                                         $this->lMargin = $this->pagedim[$this->page]['lm'];
1831                                         $this->rMargin = $this->pagedim[$this->page]['rm'];
1832                                         $this->SetY($this->tMargin);
1833                                 }
1834                         } else {
1835                                 $this->Error('Wrong page number on setPage() function.');
1836                         }
1837                 }
1838
1839                 /**
1840                 * Reset pointer to the last document page.
1841                 * @since 2.0.000 (2008-01-04)
1842                 * @see setPage(), getPage(), getNumPages()
1843                 */
1844                 function lastPage() {
1845                         $this->setPage($this->getNumPages());
1846                 }
1847
1848                 /**
1849                 * Get current document page number.
1850                 * @return int page number
1851                 * @since 2.1.000 (2008-01-07)
1852                 * @see setPage(), lastpage(), getNumPages()
1853                 */
1854                 function getPage() {
1855                         return $this->page;
1856                 }
1857
1858
1859                 /**
1860                 * Get the total number of insered pages.
1861                 * @return int number of pages
1862                 * @since 2.1.000 (2008-01-07)
1863                 * @see setPage(), getPage(), lastpage()
1864                 */
1865                 function getNumPages() {
1866                         return count($this->pages);
1867                 }
1868
1869                 /**
1870                 * 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.
1871                 * 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.
1872                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
1873                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1874                 * @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>
1875                 * @since 1.0
1876                 * @see TCPDF(), Header(), Footer(), SetMargins()
1877                 */
1878                 function AddPage($orientation='', $format='') {
1879                         if (!isset($this->original_lMargin)) {
1880                                 $this->original_lMargin = $this->lMargin;
1881                         }
1882                         if (!isset($this->original_rMargin)) {
1883                                 $this->original_rMargin = $this->rMargin;
1884                         }
1885                         if (count($this->pages) > $this->page) {
1886                                 // this page has been already added
1887                                 $this->setPage(($this->page + 1));
1888                                 $this->SetY($this->tMargin);
1889                                 return;
1890                         }
1891                         //Start a new page
1892                         if ($this->state == 0) {
1893                                 $this->Open();
1894                         }
1895                         // save current settings
1896                         $font_family = $this->FontFamily;
1897                         $font_style = $this->FontStyle.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
1898                         $font_size = $this->FontSizePt;
1899                         $prev_rMargin = $this->rMargin;
1900                         $prev_lMargin = $this->lMargin;
1901                         $prev_cMargin = $this->cMargin;
1902                         $prev_linestyleWidth = $this->linestyleWidth;
1903                         $prev_linestyleCap = $this->linestyleCap;
1904                         $prev_linestyleJoin = $this->linestyleJoin;
1905                         $prev_linestyleDash = $this->linestyleDash;
1906                         $prev_DrawColor = $this->DrawColor;
1907                         $prev_FillColor = $this->FillColor;
1908                         $prev_TextColor = $this->TextColor;
1909                         $prev_ColorFlag = $this->ColorFlag;
1910                         if ($this->page > 0) {
1911                                 //Page footer
1912                                 $this->setFooter();
1913                                 //Close page
1914                                 $this->_endpage();
1915                         }
1916                         //Start new page
1917                         $this->_beginpage($orientation, $format);
1918                         // restore graphic styles
1919                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1920                         if (!empty($font_family)) {
1921                                 $this->SetFont($font_family, $font_style, $font_size);
1922                         }
1923                         //Page header
1924                         $this->setHeader();
1925                         // restore graphic styles
1926                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1927                         if (!empty($font_family)) {
1928                                 $this->SetFont($font_family, $font_style, $font_size);
1929                         }
1930                         // restore settings
1931                         $this->FontFamily = $font_family;
1932                         $this->FontStyle = $font_style;
1933                         $this->FontSizePt = $font_size;
1934                         $this->rMargin = $prev_rMargin;
1935                         $this->lMargin = $prev_lMargin;
1936                         $this->cMargin = $prev_cMargin;
1937                         $this->linestyleWidth = $prev_linestyleWidth;
1938                         $this->linestyleCap = $prev_linestyleCap;
1939                         $this->linestyleJoin = $prev_linestyleJoin;
1940                         $this->linestyleDash = $prev_linestyleDash;
1941                         $this->DrawColor = $prev_DrawColor;
1942                         $this->FillColor = $prev_FillColor;
1943                         $this->TextColor = $prev_TextColor;
1944                         $this->ColorFlag = $prev_ColorFlag;
1945                         // mark this point
1946                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1947                 }
1948
1949                 /**
1950                  * Set start-writing mark on current page for multicell borders and fills.
1951                  * This function must be called after calling Image() function for a background image.
1952                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
1953                  * @access public
1954                  * @since 4.0.016 (2008-07-30)
1955                  */
1956                 function setPageMark() {
1957                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1958                 }
1959
1960                 /**
1961                  * Set header data.
1962                  * @param string $ln header image logo
1963                  * @param string $lw header image logo width in mm
1964                  * @param string $ht string to print as title on document header
1965                  * @param string $hs string to print on document header
1966                 */
1967                 function setHeaderData($ln="", $lw=0, $ht="", $hs="") {
1968                         $this->header_logo = $ln;
1969                         $this->header_logo_width = $lw;
1970                         $this->header_title = $ht;
1971                         $this->header_string = $hs;
1972                 }
1973
1974                 /**
1975                  * Returns header data:
1976                  * <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>
1977                  * @return array()
1978                  * @since 4.0.012 (2008-07-24)
1979                  */
1980                 function getHeaderData() {
1981                         $ret = array();
1982                         $ret['logo'] = $this->header_logo;
1983                         $ret['logo_width'] = $this->header_logo_width;
1984                         $ret['title'] = $this->header_title;
1985                         $ret['string'] = $this->header_string;
1986                         return $ret;
1987                 }
1988
1989                 /**
1990                  * Set header margin.
1991                  * (minimum distance between header and top page margin)
1992                  * @param int $hm distance in user units
1993                 */
1994                 function setHeaderMargin($hm=10) {
1995                         $this->header_margin = $hm;
1996                 }
1997
1998                 /**
1999                  * Returns header margin in user units.
2000                  * @return float
2001                  * @since 4.0.012 (2008-07-24)
2002                 */
2003                 function getHeaderMargin() {
2004                         return $this->header_margin;
2005                 }
2006
2007                 /**
2008                  * Set footer margin.
2009                  * (minimum distance between footer and bottom page margin)
2010                  * @param int $fm distance in user units
2011                 */
2012                 function setFooterMargin($fm=10) {
2013                         $this->footer_margin = $fm;
2014                 }
2015
2016                 /**
2017                  * Returns footer margin in user units.
2018                  * @return float
2019                  * @since 4.0.012 (2008-07-24)
2020                 */
2021                 function getFooterMargin() {
2022                         return $this->footer_margin;
2023                 }
2024                 /**
2025                  * Set a flag to print page header.
2026                  * @param boolean $val set to true to print the page header (default), false otherwise.
2027                  */
2028                 function setPrintHeader($val=true) {
2029                         $this->print_header = $val;
2030                 }
2031
2032                 /**
2033                  * Set a flag to print page footer.
2034                  * @param boolean $value set to true to print the page footer (default), false otherwise.
2035                  */
2036                 function setPrintFooter($val=true) {
2037                         $this->print_footer = $val;
2038                 }
2039
2040                 /**
2041                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2042                  * @return float
2043                  */
2044                 function getImageRBX() {
2045                         return $this->img_rb_x;
2046                 }
2047
2048                 /**
2049                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2050                  * @return float
2051                  */
2052                 function getImageRBY() {
2053                         return $this->img_rb_y;
2054                 }
2055
2056                 /**
2057                  * This method is used to render the page header.
2058                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2059                  */
2060                 function Header1() {
2061                         $ormargins = $this->getOriginalMargins();
2062                         $headerfont = $this->getHeaderFont();
2063                         $headerdata = $this->getHeaderData();
2064                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2065                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2066                                 $imgy = $this->getImageRBY();
2067                         } else {
2068                                 $imgy = $this->GetY();
2069                         }
2070                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2071                         // set starting margin for text data cell
2072                         if ($this->getRTL()) {
2073                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2074                         } else {
2075                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2076                         }
2077                         $this->SetTextColor(0, 0, 0);
2078                         // header title
2079                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2080                         $this->SetX($header_x);
2081                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '');
2082                         // header string
2083                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2084                         $this->SetX($header_x);
2085                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, 0, 0, true, 0);
2086                         // print an ending header line
2087                         $this->SetLineStyle(array("width" => 0.85 / $this->getScaleFactor(), "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2088                         $this->SetY(1 + max($imgy, $this->GetY()));
2089                         if ($this->getRTL()) {
2090                                 $this->SetX($ormargins['right']);
2091                         } else {
2092                                 $this->SetX($ormargins['left']);
2093                         }
2094                         $this->Cell(0, 0, '', 'T', 0, 'C');
2095                 }
2096
2097                 /**
2098                  * This method is used to render the page footer.
2099                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2100                  */
2101                 function Footer() {
2102                         $cur_y = $this->GetY();
2103                         $ormargins = $this->getOriginalMargins();
2104                         $this->SetTextColor(0, 0, 0);
2105                         //set style for cell border
2106                         $line_width = 0.85 / $this->getScaleFactor();
2107                         $this->SetLineStyle(array("width" => $line_width, "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2108                         //print document barcode
2109                         $barcode = $this->getBarcode();
2110                         if (!empty($barcode)) {
2111                                 $this->Ln();
2112                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2113                                 $this->write1DBarcode($barcode, "C128B", $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2114                         }
2115                         $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / '.$this->getAliasNbPages();
2116                         $this->SetY($cur_y);
2117                         //Print page number
2118                         if ($this->getRTL()) {
2119                                 $this->SetX($ormargins['right']);
2120                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2121                         } else {
2122                                 $this->SetX($ormargins['left']);
2123                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2124                         }
2125                 }
2126
2127                 /**
2128                  * This method is used to render the page header.
2129                  * @access protected
2130                  * @since 4.0.012 (2008-07-24)
2131                  */
2132                 function setHeader() {
2133                         if ($this->print_header) {
2134                                 $lasth = $this->lasth;
2135                                 $this->_out("q");
2136                                 $this->rMargin = $this->original_rMargin;
2137                                 $this->lMargin = $this->original_lMargin;
2138                                 //set current position
2139                                 if ($this->rtl) {
2140                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2141                                 } else {
2142                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2143                                 }
2144                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2145                                 $this->Header1();
2146                                 //restore position
2147                                 if ($this->rtl) {
2148                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2149                                 } else {
2150                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2151                                 }
2152                                 $this->_out("Q");
2153                                 $this->lasth = $lasth;
2154                         }
2155                 }
2156
2157                 /**
2158                  * This method is used to render the page footer.
2159                  * @access protected
2160                  * @since 4.0.012 (2008-07-24)
2161                  */
2162                 function setFooter() {
2163                         //Page footer
2164                         $this->InFooter = true;
2165                         // mark this point
2166                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
2167                         if ($this->print_footer) {
2168                                 $lasth = $this->lasth;
2169                                 $this->_out("q");
2170                                 $this->rMargin = $this->original_rMargin;
2171                                 $this->lMargin = $this->original_lMargin;
2172                                 //set current position
2173                                 $footer_y = $this->h - $this->footer_margin;
2174                                 if ($this->rtl) {
2175                                         $this->SetXY($this->original_rMargin, $footer_y);
2176                                 } else {
2177                                         $this->SetXY($this->original_lMargin, $footer_y);
2178                                 }
2179                                 $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]);
2180                                 $this->Footer();
2181                                 //restore position
2182                                 if ($this->rtl) {
2183                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2184                                 } else {
2185                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2186                                 }
2187                                 $this->_out("Q");
2188                                 $this->lasth = $lasth;
2189                         }
2190                         $this->footerlen[$this->page] = strlen($this->pages[$this->page]) - $this->footerpos[$this->page];
2191                         $this->InFooter = false;
2192                 }
2193
2194                 /**
2195                 * Returns the current page number.
2196                 * @return int page number
2197                 * @since 1.0
2198                 * @see AliasNbPages(), getAliasNbPages()
2199                 */
2200                 function PageNo() {
2201                         return $this->page;
2202                 }
2203
2204                 /**
2205                 * Defines a new spot color. 
2206                 * It can be expressed in RGB components or gray scale. 
2207                 * The method can be called before the first page is created and the value is retained from page to page.
2208                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2209                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2210                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2211                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2212                 * @since 4.0.024 (2008-09-12)
2213                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2214                 */
2215                 function AddSpotColor($name, $c, $m, $y, $k) {
2216                         if (!isset($this->spot_colors[$name])) {
2217                                 $i = 1 + count($this->spot_colors);
2218                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2219                         }
2220                 }
2221
2222                 /**
2223                 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
2224                 * It can be expressed in RGB components or gray scale.
2225                 * The method can be called before the first page is created and the value is retained from page to page.
2226                 * @param array $color array of colors
2227                 * @since 3.1.000 (2008-06-11)
2228                 * @see SetDrawColor()
2229                 */
2230                 function SetDrawColorArray($color) {
2231                         if (isset($color)) {
2232                                 $color = array_values($color);
2233                                 $r = isset($color[0]) ? $color[0] : -1;
2234                                 $g = isset($color[1]) ? $color[1] : -1;
2235                                 $b = isset($color[2]) ? $color[2] : -1;
2236                                 $k = isset($color[3]) ? $color[3] : -1;
2237                                 if ($r >= 0) {
2238                                         $this->SetDrawColor($r, $g, $b, $k);
2239                                 }
2240                         }
2241                 }
2242
2243                 /**
2244                 * 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.
2245                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2246                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2247                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2248                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2249                 * @since 1.3
2250                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2251                 */
2252                 function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2253                         // set default values
2254                         if (!is_numeric($col1)) {
2255                                 $col1 = 0;
2256                         }
2257                         if (!is_numeric($col2)) {
2258                                 $col2 = -1;
2259                         }
2260                         if (!is_numeric($col3)) {
2261                                 $col3 = -1;
2262                         }
2263                         if (!is_numeric($col4)) {
2264                                 $col4 = -1;
2265                         }
2266                         //Set color for all stroking operations
2267                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2268                                 // Grey scale
2269                                 $this->DrawColor = sprintf('%.3f G', $col1/255);
2270                         } elseif ($col4 == -1) {
2271                                 // RGB
2272                                 $this->DrawColor = sprintf('%.3f %.3f %.3f RG', $col1/255, $col2/255, $col3/255);
2273                         } else {
2274                                 // CMYK
2275                                 $this->DrawColor = sprintf('%.3f %.3f %.3f %.3f K', $col1/100, $col2/100, $col3/100, $col4/100);
2276                         }
2277                         if ($this->page > 0) {
2278                                 $this->_out($this->DrawColor);
2279                         }
2280                 }
2281                 
2282                 /**
2283                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2284                 * @param string $name name of the spot color
2285                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2286                 * @since 4.0.024 (2008-09-12)
2287                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2288                 */
2289                 function SetDrawSpotColor($name, $tint=100) {
2290                         if (!isset($this->spot_colors[$name])) {
2291                                 $this->Error('Undefined spot color: '.$name);
2292                         }
2293                         $this->DrawColor = sprintf('/CS%d CS %.3f SCN', $this->spot_colors[$name]['i'], $tint/100);
2294                         if ($this->page > 0) {
2295                                 $this->_out($this->DrawColor);
2296                         }
2297                 }
2298
2299                 /**
2300                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
2301                 * It can be expressed in RGB components or gray scale.
2302                 * The method can be called before the first page is created and the value is retained from page to page.
2303                 * @param array $color array of colors
2304                 * @since 3.1.000 (2008-6-11)
2305                 * @see SetFillColor()
2306                 */
2307                 function SetFillColorArray($color) {
2308                         if (isset($color)) {
2309                                 $color = array_values($color);
2310                                 $r = isset($color[0]) ? $color[0] : -1;
2311                                 $g = isset($color[1]) ? $color[1] : -1;
2312                                 $b = isset($color[2]) ? $color[2] : -1;
2313                                 $k = isset($color[3]) ? $color[3] : -1;
2314                                 if ($r >= 0) {
2315                                         $this->SetFillColor($r, $g, $b, $k);
2316                                 }
2317                         }
2318                 }
2319
2320                 /**
2321                 * 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.
2322                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2323                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2324                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2325                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2326                 * @since 1.3
2327                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2328                 */
2329                 function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2330                         // set default values
2331                         if (!is_numeric($col1)) {
2332                                 $col1 = 0;
2333                         }
2334                         if (!is_numeric($col2)) {
2335                                 $col2 = -1;
2336                         }
2337                         if (!is_numeric($col3)) {
2338                                 $col3 = -1;
2339                         }
2340                         if (!is_numeric($col4)) {
2341                                 $col4 = -1;
2342                         }
2343                         //Set color for all filling operations
2344                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2345                                 // Grey scale
2346                                 $this->FillColor = sprintf('%.3f g', $col1/255);
2347                                 $this->bgcolor = array('G' => $col1);
2348                         } elseif ($col4 == -1) {
2349                                 // RGB
2350                                 $this->FillColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2351                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2352                         } else {
2353                                 // CMYK
2354                                 $this->FillColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2355                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2356                         }
2357                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2358                         if ($this->page > 0) {
2359                                 $this->_out($this->FillColor);
2360                         }
2361                 }
2362                 
2363                 /**
2364                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2365                 * @param string $name name of the spot color
2366                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2367                 * @since 4.0.024 (2008-09-12)
2368                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2369                 */
2370                 function SetFillSpotColor($name, $tint=100) {
2371                         if (!isset($this->spot_colors[$name])) {
2372                                 $this->Error('Undefined spot color: '.$name);
2373                         }
2374                         $this->FillColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2375                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2376                         if ($this->page > 0) {
2377                                 $this->_out($this->FillColor);
2378                         }
2379                 }
2380
2381                 /**
2382                 * Defines the color used for text. It can be expressed in RGB components or gray scale.
2383                 * The method can be called before the first page is created and the value is retained from page to page.
2384                 * @param array $color array of colors
2385                 * @since 3.1.000 (2008-6-11)
2386                 * @see SetFillColor()
2387                 */
2388                 function SetTextColorArray($color) {
2389                         if (isset($color)) {
2390                                 $color = array_values($color);
2391                                 $r = isset($color[0]) ? $color[0] : -1;
2392                                 $g = isset($color[1]) ? $color[1] : -1;
2393                                 $b = isset($color[2]) ? $color[2] : -1;
2394                                 $k = isset($color[3]) ? $color[3] : -1;
2395                                 if ($r >= 0) {
2396                                         $this->SetTextColor($r, $g, $b, $k);
2397                                 }
2398                         }
2399                 }
2400
2401                 /**
2402                 * 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.
2403                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2404                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2405                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2406                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2407                 * @since 1.3
2408                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2409                 */
2410                 function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2411                         // set default values
2412                         if (!is_numeric($col1)) {
2413                                 $col1 = 0;
2414                         }
2415                         if (!is_numeric($col2)) {
2416                                 $col2 = -1;
2417                         }
2418                         if (!is_numeric($col3)) {
2419                                 $col3 = -1;
2420                         }
2421                         if (!is_numeric($col4)) {
2422                                 $col4 = -1;
2423                         }
2424                         //Set color for text
2425                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2426                                 // Grey scale
2427                                 $this->TextColor = sprintf('%.3f g', $col1/255);
2428                                 $this->fgcolor = array('G' => $col1);
2429                         } elseif ($col4 == -1) {
2430                                 // RGB
2431                                 $this->TextColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2432                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2433                         } else {
2434                                 // CMYK
2435                                 $this->TextColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2436                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2437                         }
2438                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2439                 }
2440
2441                 /**
2442                 * Defines the spot color used for text.
2443                 * @param string $name name of the spot color
2444                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2445                 * @since 4.0.024 (2008-09-12)
2446                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2447                 */
2448                 function SetTextSpotColor($name, $tint=100) {
2449                         if (!isset($this->spot_colors[$name])) {
2450                                 $this->Error('Undefined spot color: '.$name);
2451                         }
2452                         $this->TextColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2453                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2454                         if ($this->page > 0) {
2455                                 $this->_out($this->TextColor);
2456                         }
2457                 }
2458
2459                 /**
2460                 * Returns the length of a string in user unit. A font must be selected.<br>
2461                 * @param string $s The string whose 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 1.2
2468                 */
2469                 function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2470                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $this->tmprtl), $fontname, $fontstyle, $fontsize);
2471                 }
2472
2473                 /**
2474                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2475                 * @param string $arr The array of chars whose total length is to be computed
2476                 * @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.
2477                 * @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.
2478                 * @param float $fontsize Font size in points. The default value is the current size.
2479                 * @return int string length
2480                 * @author Nicola Asuni
2481                 * @since 2.4.000 (2008-03-06)
2482                 */
2483                 function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2484                         // store current values
2485                         if (!empty($fontname)) {
2486                                 $prev_FontFamily = $this->FontFamily;
2487                                 $prev_FontStyle = $this->FontStyle;
2488                                 $prev_FontSizePt = $this->FontSizePt;
2489                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2490                         }
2491                         $w = 0;
2492                         foreach($sa as $char) {
2493                                 $w += $this->GetCharWidth($char);
2494                         }
2495                         // restore previous values
2496                         if (!empty($fontname)) {
2497                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2498                         }
2499                         return $w;
2500                 }
2501
2502                 /**
2503                 * Returns the length of the char in user unit for the current font.<br>
2504                 * @param int $char The char code whose length is to be returned
2505                 * @return int char width
2506                 * @author Nicola Asuni
2507                 * @since 2.4.000 (2008-03-06)
2508                 */
2509                 function GetCharWidth($char) {
2510                         $cw = &$this->CurrentFont['cw'];
2511                         if (isset($cw[$char])) {
2512                                 $w = $cw[$char];
2513                                 /*
2514                         } elseif (isset($cw[ord($char)])) {
2515                                 $w = $cw[ord($char)];
2516                         } elseif (isset($cw[chr($char)])) {
2517                                 $w = $cw[chr($char)];
2518                                 */
2519                         } elseif (isset($this->CurrentFont['dw'])) {
2520                                 $w = $this->CurrentFont['dw'];
2521                         } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
2522                                 $w = $this->CurrentFont['desc']['MissingWidth']; // set default size
2523                         } else {
2524                                 $w = 500; // default width
2525                         }
2526                         return ($w * $this->FontSize / 1000);
2527                 }
2528
2529                 /**
2530                 * Returns the numbero of characters in a string.
2531                 * @param string $s The input string.
2532                 * @return int number of characters
2533                 * @since 2.0.0001 (2008-01-07)
2534                 */
2535                 function GetNumChars($s) {
2536                         if ($this->isunicode) {
2537                                 return count($this->UTF8StringToArray($s));
2538                         }
2539                         return strlen($s);
2540                 }
2541
2542                 /**
2543                 * Fill the list of available fonts ($this->fontlist).
2544                 * @access protected
2545                 * @since 4.0.013 (2008-07-28)
2546                 */
2547                 function getFontsList() {
2548                         $fontsdir = opendir($this->_getfontpath());
2549                         while (($file = readdir($fontsdir)) !== false) {
2550                                 if (substr($file, -4) == ".php") {
2551                                                 array_push($this->fontlist, strtolower(basename($file, ".php")));
2552                                 }
2553                         }
2554                         closedir($fontsdir);
2555                 }
2556
2557                 /**
2558                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2559                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
2560                 * 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.
2561                 * Changed to support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
2562                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2563                 * @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>
2564                 * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space.
2565                 * @return array containing the font data, or false in case of error.
2566                 * @since 1.5
2567                 * @see SetFont()
2568                 */
2569                 function AddFont($family, $style='', $file='') {
2570                         if (empty($family)) {
2571                                 if (!empty($this->FontFamily)) {
2572                                         $family = $this->FontFamily;
2573                                 } else {
2574                                         $this->Error('Empty font family');
2575                                 }
2576                         }
2577                         $family = strtolower($family);
2578                         if ((!$this->isunicode) AND ($family == 'arial')) {
2579                                 $family = 'helvetica';
2580                         }
2581                         if (($family == "symbol") OR ($family == "zapfdingbats")) {
2582                                 $style = '';
2583                         }
2584                         $style = strtoupper($style);
2585                         // underline
2586                         if (strpos($style,'U') !== false) {
2587                                 $this->underline = true;
2588                                 $style = str_replace('U', '', $style);
2589                         } else {
2590                                 $this->underline = false;
2591                         }
2592                         //line through (deleted)
2593                         if (strpos($style,'D') !== false) {
2594                                 $this->linethrough = true;
2595                                 $style = str_replace('D', '', $style);
2596                         } else {
2597                                 $this->linethrough = false;
2598                         }
2599                         if ($style == 'IB') {
2600                                 $style = 'BI';
2601                         }
2602                         $fontkey = $family.$style;
2603                         $fontdata = array("fontkey" => $fontkey, "family" => $family, "style" => $style);
2604                         // check if the font has been already added
2605                         if (isset($this->fonts[$fontkey])) {
2606                                 return $fontdata;
2607                         }
2608                         if ($file == '') {
2609                                 $file = str_replace(' ', '', $family).strtolower($style).'.php';
2610                         }
2611                         if (!file_exists($this->_getfontpath().$file)) {
2612                                 // try to load the basic file without styles
2613                                 $file = str_replace(' ', '', $family).'.php';
2614                         }
2615                         if (isset($type)) {
2616                                 unset($type);
2617                         }
2618                         if (isset($cw)) {
2619                                 unset($cw);
2620                         }
2621                         @include($this->_getfontpath().$file);
2622                         if ((!isset($type)) OR (!isset($cw))) {
2623                                 $this->Error("Could not include font definition file: ".$file);
2624                         }
2625                         $i = count($this->fonts) + 1;
2626                         // register CID font (all styles at once)
2627                         if ($type == 'cidfont0') {
2628                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
2629                                 foreach ($styles as $skey => $qual) {
2630                                         $sname = $name.$qual;
2631                                         $sfontkey = $family.$skey;
2632                                         $this->fonts[$sfontkey] = array('i' => $i, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc);
2633                                         $i = count($this->fonts) + 1;
2634                                 }
2635                                 $file = '';
2636                         } elseif ($type == 'core') {
2637                                 $def_width = $cw[ord('?')];
2638                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $def_width);
2639                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
2640                                 if (!isset($file)) {
2641                                         $file = '';
2642                                 }
2643                                 if (!isset($enc)) {
2644                                         $enc = '';
2645                                 }
2646                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'file' => $file, 'enc' => $enc, 'desc' => $desc);
2647                         } elseif ($type == 'TrueTypeUnicode') {
2648                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg);
2649                         } else {
2650                                 $this->Error('Unknow font type');
2651                         }
2652                         if (isset($diff) AND (!empty($diff))) {
2653                                 //Search existing encodings
2654                                 $d = 0;
2655                                 $nb = count($this->diffs);
2656                                 for($i=1; $i <= $nb; $i++) {
2657                                         if ($this->diffs[$i] == $diff) {
2658                                                 $d = $i;
2659                                                 break;
2660                                         }
2661                                 }
2662                                 if ($d == 0) {
2663                                         $d = $nb + 1;
2664                                         $this->diffs[$d] = $diff;
2665                                 }
2666                                 $this->fonts[$fontkey]['diff'] = $d;
2667                         }
2668                         if (!empty($file)) {
2669                                 if ((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) {
2670                                         $this->FontFiles[$file] = array('length1' => $originalsize);
2671                                 } elseif ($type != 'core') {
2672                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2);
2673                                 }
2674                         }
2675                         return $fontdata;
2676                 }
2677
2678                 /**
2679                 * Sets the font used to print character strings.
2680                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
2681                 * The method can be called before the first page is created and the font is retained from page to page.
2682                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
2683                 * 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 />
2684                 * @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.
2685                 * @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.
2686                 * @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
2687                 * @since 1.0
2688                 * @see AddFont(), SetFontSize()
2689                 */
2690                 function SetFont($family, $style='', $size=0) {
2691                         //Select a font; size given in points
2692                         if ($size == 0) {
2693                                 $size = $this->FontSizePt;
2694                         }
2695                         // try to add font (if not already added)
2696                         $fontdata =  $this->AddFont($family, $style);
2697                         $this->FontFamily = $fontdata['family'];
2698                         $this->FontStyle = $fontdata['style'];
2699                         $this->CurrentFont = &$this->fonts[$fontdata['fontkey']];
2700                         $this->SetFontSize($size);
2701                 }
2702
2703                 /**
2704                 * Defines the size of the current font.
2705                 * @param float $size The size (in points)
2706                 * @since 1.0
2707                 * @see SetFont()
2708                 */
2709                 function SetFontSize($size) {
2710                         //Set font size in points
2711                         $this->FontSizePt = $size;
2712                         $this->FontSize = $size / $this->k;
2713                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
2714                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
2715                         } else {
2716                                 $this->FontAscent = 0.8 * $this->FontSize;
2717                         }
2718                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
2719                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
2720                         } else {
2721                                 $this->FontDescent = 0.2 * $this->FontSize;
2722                         }
2723                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
2724                                 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2725                         }
2726                 }
2727
2728                 /**
2729                 * 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 />
2730                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
2731                 * @since 1.5
2732                 * @see Cell(), Write(), Image(), Link(), SetLink()
2733                 */
2734                 function AddLink() {
2735                         //Create a new internal link
2736                         $n = count($this->links) + 1;
2737                         $this->links[$n] = array(0, 0);
2738                         return $n;
2739                 }
2740
2741                 /**
2742                 * Defines the page and position a link points to.
2743                 * @param int $link The link identifier returned by AddLink()
2744                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
2745                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
2746                 * @since 1.5
2747                 * @see AddLink()
2748                 */
2749                 function SetLink($link, $y=0, $page=-1) {
2750                         if ($y == -1) {
2751                                 $y = $this->y;
2752                         }
2753                         if ($page == -1) {
2754                                 $page = $this->page;
2755                         }
2756                         $this->links[$link] = array($page, $y);
2757                 }
2758
2759                 /**
2760                 * Puts a link on a rectangular area of the page.
2761                 * 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.
2762                 * @param float $x Abscissa of the upper-left corner of the rectangle
2763                 * @param float $y Ordinate of the upper-left corner of the rectangle
2764                 * @param float $w Width of the rectangle
2765                 * @param float $h Height of the rectangle
2766                 * @param mixed $link URL or identifier returned by AddLink()
2767                 * @since 1.5
2768                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
2769                 */
2770                 function Link($x, $y, $w, $h, $link) {
2771                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'));
2772                 }
2773                 
2774                 /**
2775                 * Puts a text annotation on a rectangular area of the page.
2776                 * !!!! THIS FUNCTION IS NOT YET FULLY IMPLEMENTED !!!!
2777                 * @param float $x Abscissa of the upper-left corner of the rectangle
2778                 * @param float $y Ordinate of the upper-left corner of the rectangle
2779                 * @param float $w Width of the rectangle
2780                 * @param float $h Height of the rectangle
2781                 * @param string $text annotation text
2782                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
2783                 * @since 4.0.018 (2008-08-06)
2784                 */
2785                 function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text')) {
2786                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt);
2787                 }
2788
2789                 /**
2790                 * 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.
2791                 * @param float $x Abscissa of the origin
2792                 * @param float $y Ordinate of the origin
2793                 * @param string $txt String to print
2794                 * @param int $stroke outline size in points (0 = disable)
2795                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
2796                 * @since 1.0
2797                 * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
2798                 */
2799                 function Text($x, $y, $txt, $stroke=0, $clip=false) {
2800                         //Output a string
2801                         if ($this->rtl) {
2802                                 // bidirectional algorithm (some chars may be changed affecting the line length)
2803                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $this->tmprtl);
2804                                 $l = $this->GetArrStringWidth($s);
2805                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
2806                         } else {
2807                                 $xr = $x;
2808                         }
2809                         $opt = "";
2810                         if (($stroke > 0) AND (!$clip)) {
2811                                 $opt .= "1 Tr ".intval($stroke)." w ";
2812                         } elseif (($stroke > 0) AND $clip) {
2813                                 $opt .= "5 Tr ".intval($stroke)." w ";
2814                         } elseif ($clip) {
2815                                 $opt .= "7 Tr ";
2816                         }
2817                         $s = sprintf('BT %.2f %.2f Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
2818                         if ($this->underline AND ($txt!='')) {
2819                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
2820                         }
2821                         if ($this->linethrough AND ($txt!='')) {
2822                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt);
2823                         }
2824                         if ($this->ColorFlag AND (!$clip)) {
2825                                 $s='q '.$this->TextColor.' '.$s.' Q';
2826                         }
2827                         $this->_out($s);
2828                 }
2829
2830                 /**
2831                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
2832                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
2833                 * This method is called automatically and should not be called directly by the application.
2834                 * @return boolean
2835                 * @since 1.4
2836                 * @see SetAutoPageBreak()
2837                 */
2838                 function AcceptPageBreak() {
2839                         return $this->AutoPageBreak;
2840                 }
2841
2842                 /**
2843                 * Add page if needed.
2844                 * @param float $h Cell height. Default value: 0.
2845                 * @since 3.2.000 (2008-07-01)
2846                 * @access protected
2847                 */
2848                 function checkPageBreak($h) {
2849                         if ((($this->y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
2850                                 $rs = "";
2851                                 //Automatic page break
2852                                 $x = $this->x;
2853                                 $ws = $this->ws;
2854                                 if ($ws > 0) {
2855                                         $this->ws = 0;
2856                                         $rs .= '0 Tw';
2857                                 }
2858                                 $this->AddPage($this->CurOrientation);
2859                                 if ($ws > 0) {
2860                                         $this->ws = $ws;
2861                                         $rs .= sprintf('%.3f Tw', $ws * $k);
2862                                 }
2863                                 $this->_out($rs);
2864                                 $this->y = $this->tMargin;
2865                                 $this->x = $x;
2866                         }
2867                 }
2868
2869                 /**
2870                 * 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 />
2871                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2872                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2873                 * @param float $h Cell height. Default value: 0.
2874                 * @param string $txt String to print. Default value: empty string.
2875                 * @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>
2876                 * @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>
2877                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2878                 * @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>
2879                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2880                 * @param mixed $link URL or identifier returned by AddLink().
2881                 * @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>
2882                 * @since 1.0
2883                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
2884                 */
2885                 function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2886                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
2887                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2888                         if ($h < $min_cell_height) {
2889                                 $h = $min_cell_height;
2890                         }
2891                         $this->checkPageBreak($h);
2892                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch));
2893                 }
2894
2895                 /**
2896                 * 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 />
2897                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2898                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2899                 * @param float $h Cell height. Default value: 0.
2900                 * @param string $txt String to print. Default value: empty string.
2901                 * @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>
2902                 * @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.
2903                 * @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>
2904                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2905                 * @param mixed $link URL or identifier returned by AddLink().
2906                 * @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>
2907                 * @since 1.0
2908                 * @see Cell()
2909                 */
2910                 function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2911                         $rs = ""; //string to be returned
2912                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2913                         if ($h < $min_cell_height) {
2914                                 $h = $min_cell_height;
2915                         }
2916                         $k = $this->k;
2917                         if (empty($w) OR ($w <= 0)) {
2918                                 if ($this->rtl) {
2919                                         $w = $this->x - $this->lMargin;
2920                                 } else {
2921                                         $w = $this->w - $this->rMargin - $this->x;
2922                                 }
2923                         }
2924                         $s = '';
2925                         if (($fill == 1) OR ($border == 1)) {
2926                                 if ($fill == 1) {
2927                                         $op = ($border == 1) ? 'B' : 'f';
2928                                 } else {
2929                                         $op = 'S';
2930                                 }
2931                                 if ($this->rtl) {
2932                                         $xk = (($this->x  - $w) * $k);
2933                                 } else {
2934                                         $xk = ($this->x * $k);
2935                                 }
2936                                 $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
2937                         }
2938
2939                         if (is_string($border)) {
2940                                 $x = $this->x;
2941                                 $y = $this->y;
2942                                 if (strpos($border,'L') !== false) {
2943                                         if ($this->rtl) {
2944                                                 $xk = ($x - $w) * $k;
2945                                         } else {
2946                                                 $xk = $x * $k;
2947                                         }
2948                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h)) * $k));
2949                                 }
2950                                 if (strpos($border,'T') !== false) {
2951                                         if ($this->rtl) {
2952                                                 $xk = ($x - $w) * $k;
2953                                                 $xwk = $x * $k;
2954                                         } else {
2955                                                 $xk = $x * $k;
2956                                                 $xwk = ($x + $w) * $k;
2957                                         }
2958                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
2959                                 }
2960                                 if (strpos($border,'R') !== false) {
2961                                         if ($this->rtl) {
2962                                                 $xk = $x * $k;
2963                                         } else {
2964                                                 $xk = ($x + $w) * $k;
2965                                         }
2966                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h))* $k));
2967                                 }
2968                                 if (strpos($border,'B') !== false) {
2969                                         if ($this->rtl) {
2970                                                 $xk = ($x - $w) * $k;
2971                                                 $xwk = $x * $k;
2972                                         } else {
2973                                                 $xk = $x * $k;
2974                                                 $xwk = ($x + $w) * $k;
2975                                         }
2976                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
2977                                 }
2978                         }
2979                         if ($txt != '') {
2980                                 // text lenght
2981                                 $width = $this->GetStringWidth($txt);
2982                                 // ratio between cell lenght and text lenght
2983                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
2984
2985                                 // stretch text if required
2986                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
2987                                         if ($stretch > 2) {
2988                                                 // spacing
2989                                                 //Calculate character spacing in points
2990                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
2991                                                 //Set character spacing
2992                                                 $rs .= sprintf('BT %.2f Tc ET ', $char_space);
2993                                         } else {
2994                                                 // scaling
2995                                                 //Calculate horizontal scaling
2996                                                 $horiz_scale = $ratio * 100.0;
2997                                                 //Set horizontal scaling
2998                                                 $rs .= sprintf('BT %.2f Tz ET ', $horiz_scale);
2999                                         }
3000                                         $align = '';
3001                                         $width = $w - (2 * $this->cMargin);
3002                                 } else {
3003                                         $stretch == 0;
3004                                 }
3005                                 if ($align == 'L') {
3006                                         if ($this->rtl) {
3007                                                 $dx = $w - $width - $this->cMargin;
3008                                         } else {
3009                                                 $dx = $this->cMargin;
3010                                         }
3011                                 } elseif ($align == 'R') {
3012                                         if ($this->rtl) {
3013                                                 $dx = $this->cMargin;
3014                                         } else {
3015                                                 $dx = $w - $width - $this->cMargin;
3016                                         }
3017                                 } elseif ($align == 'C') {
3018                                         $dx = ($w - $width) / 2;
3019                                 } elseif ($align == 'J') {
3020                                         if ($this->rtl) {
3021                                                 $dx = $w - $width - $this->cMargin;
3022                                         } else {
3023                                                 $dx = $this->cMargin;
3024                                         }
3025                                 } else {
3026                                         $dx = $this->cMargin;
3027                                 }
3028                                 if ($this->ColorFlag) {
3029                                         $s .= 'q '.$this->TextColor.' ';
3030                                 }
3031                                 $txt2 = $this->_escapetext($txt);
3032                                 if ($this->rtl) {
3033                                         $xdk = ($this->x - $dx - $width) * $k;
3034                                 } else {
3035                                         $xdk = ($this->x + $dx) * $k;
3036                                 }
3037                                 // Justification
3038                                 if ($align == 'J') {
3039                                         // count number of spaces
3040                                         $ns = substr_count($txt, ' ');
3041                                         //if ($this->isunicode) {
3042                                         if (($this->CurrentFont['type'] == "TrueTypeUnicode") OR ($this->CurrentFont['type'] == "cidfont0")) {
3043                                                 // get string width without spaces
3044                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3045                                                 // calculate average space width
3046                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3047                                                 // set word position to be used with TJ operator
3048                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3049                                         } else {
3050                                                 // get string width
3051                                                 $width = $this->GetStringWidth($txt);
3052                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3053                                                 $rs .= sprintf('BT %.3f Tw ET ', $spacewidth);
3054                                         }
3055                                 }
3056                                 // calculate approximate position of the font base line
3057                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3058                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3059                                 // print text
3060                                 $s .= sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3061                                 if ($this->rtl) {
3062                                         $xdx = $this->x - $dx - $width;
3063                                 } else {
3064                                         $xdx = $this->x + $dx;
3065                                 }
3066                                 if ($this->underline)  {
3067                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3068                                 }
3069                                 if ($this->linethrough) {
3070                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3071                                 }
3072                                 if ($this->ColorFlag) {
3073                                         $s .= ' Q';
3074                                 }
3075                                 if ($link) {
3076                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link);
3077                                 }
3078                         }
3079                         // output cell
3080                         if ($s) {
3081                                 // output cell
3082                                 $rs .= $s;
3083                                 // reset text stretching
3084                                 if ($stretch > 2) {
3085                                         //Reset character horizontal spacing
3086                                         $rs .= ' BT 0 Tc ET';
3087                                 } elseif ($stretch > 0) {
3088                                         //Reset character horizontal scaling
3089                                         $rs .= ' BT 100 Tz ET';
3090                                 }
3091                         }
3092                         // reset word spacing
3093                         if ((!$this->isunicode) AND ($align == 'J')) {
3094                                 $rs .= ' BT 0 Tw ET';
3095                         }
3096                         $this->lasth = $h;
3097                         if ($ln > 0) {
3098                                 //Go to the beginning of the next line
3099                                 $this->y += $h;
3100                                 if ($ln == 1) {
3101                                         if ($this->rtl) {
3102                                                 $this->x = $this->w - $this->rMargin;
3103                                         } else {
3104                                                 $this->x = $this->lMargin;
3105                                         }
3106                                 }
3107                         } else {
3108                                 // go left or right by case
3109                                 if ($this->rtl) {
3110                                         $this->x -= $w;
3111                                 } else {
3112                                         $this->x += $w;
3113                                 }
3114                         }
3115                         $gstyles = $this->linestyleWidth." ".$this->linestyleCap." ".$this->linestyleJoin." ".$this->linestyleDash." ".$this->DrawColor." ".$this->FillColor."\n";
3116                         $rs = $gstyles.$rs;
3117                         return $rs;
3118                 }
3119
3120                 /**
3121                 * This method allows printing text with line breaks.
3122                 * 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 />
3123                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3124                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3125                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3126                 * @param string $txt String to print
3127                 * @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>
3128                 * @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>
3129                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3130                 * @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>
3131                 * @param int $x x position in user units
3132                 * @param int $y y position in user units
3133                 * @param boolean $reseth if true reset the last cell height (default true).
3134                 * @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>
3135                 * @param boolean $ishtml se to true if $txt is HTML content (default = false).
3136                 * @return int Return the number of cells or 1 for html mode.
3137                 * @since 1.3
3138                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3139                 */
3140                 function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false) {
3141                         if ((empty($this->lasth))OR ($reseth)) {
3142                                 //set row height
3143                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3144                         }
3145                         if (!empty($y)) {
3146                                 $this->SetY($y);
3147                         } else {
3148                                 $y = $this->GetY();
3149                         }
3150                         // check for page break
3151                         $this->checkPageBreak($h);
3152                         $y = $this->GetY();
3153                         // get current page number
3154                         $startpage = $this->page;
3155                         if (!empty($x)) {
3156                                 $this->SetX($x);
3157                         } else {
3158                                 $x = $this->GetX();
3159                         }
3160                         if (empty($w) OR ($w <= 0)) {
3161                                 if ($this->rtl) {
3162                                         $w = $this->x - $this->lMargin;
3163                                 } else {
3164                                         $w = $this->w - $this->rMargin - $this->x;
3165                                 }
3166                         }
3167                         // store original margin values
3168                         $lMargin = $this->lMargin;
3169                         $rMargin = $this->rMargin;
3170                         if ($this->rtl) {
3171                                 $this->SetRightMargin($this->w - $this->x);
3172                                 $this->SetLeftMargin($this->x - $w);
3173                         } else {
3174                                 $this->SetLeftMargin($this->x);
3175                                 $this->SetRightMargin($this->w - $this->x - $w);
3176                         }
3177                         // calculate remaining vertical space on first page ($startpage)
3178                         $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
3179                         // Adjust internal padding
3180                         if ($this->cMargin < ($this->LineWidth / 2)) {
3181                                 $this->cMargin = ($this->LineWidth / 2);
3182                         }
3183                         // Add top space if needed
3184                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3185                                 $this->y += $this->LineWidth / 2;
3186                         }
3187                         // add top padding
3188                         $this->y += $this->cMargin;
3189                         if ($ishtml) {
3190                                 // Write HTML text
3191                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3192                                 $nl = 1;
3193                         } else {
3194                                 // Write text
3195                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false);
3196                         }
3197                         // add bottom padding
3198                         $this->y += $this->cMargin;
3199                         // Add bottom space if needed
3200                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3201                                 $this->y += $this->LineWidth / 2;
3202                         }
3203                         // Get end-of-text Y position
3204                         $currentY = $this->GetY();
3205                         // get latest page number
3206                         $endpage = $this->page;
3207                         // check if a new page has been created
3208                         if ($endpage > $startpage) {
3209                                 // design borders around HTML cells.
3210                                 for ($page=$startpage; $page <= $endpage; $page++) {
3211                                         $this->setPage($page);
3212                                         if ($page == $startpage) {
3213                                                 $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
3214                                                 $h = $restspace;
3215                                         } elseif ($page == $endpage) {
3216                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3217                                                 $h = $currentY - $this->tMargin;
3218                                         } else {
3219                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3220                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3221                                         }
3222                                         $this->SetX($x);
3223                                         $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3224                                         if ($border OR $fill) {
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                         } else {
3232                                 $h = max($h, ($currentY - $y));
3233                                 // put cursor at the beginning of text
3234                                 $this->SetY($y);
3235                                 $this->SetX($x);
3236                                 $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3237                                 if ($border OR $fill) {
3238                                         // design a cell around the text
3239                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3240                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3241                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3242                                         $this->intmrk[$this->page] += strlen($ccode."\n");
3243                                 }
3244                         }
3245                         // Get end-of-cell Y position
3246                         $currentY = $this->GetY();
3247                         // restore original margin values
3248                         $this->SetLeftMargin($lMargin);
3249                         $this->SetRightMargin($rMargin);
3250                         if ($ln > 0) {
3251                                 //Go to the beginning of the next line
3252                                 $this->SetY($currentY);
3253                                 if ($ln == 2) {
3254                                         $this->SetX($x + $w);
3255                                 }
3256                         } else {
3257                                 // go left or right by case
3258                                 $this->setPage($startpage);
3259                                 $this->y = $y;
3260                                 $this->SetX($x + $w);
3261                         }
3262                         return $nl;
3263                 }
3264
3265                 /**
3266                 * This method prints text from the current position.<br />
3267                 * @param float $h Line height
3268                 * @param string $txt String to print
3269                 * @param mixed $link URL or identifier returned by AddLink()
3270                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3271                 * @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>
3272                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3273                 * @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>
3274                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3275                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3276                 * @since 1.5
3277                 */
3278                 function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false) {
3279                         // remove carriage returns
3280                         $s = str_replace("\r", '', $txt);
3281                         // check if string contains arabic text
3282                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3283                                 $arabic = true;
3284                         } else {
3285                                 $arabic = false;
3286                         }
3287                         // get array of chars
3288                         $chars = $this->UTF8StringToArray($s);
3289                         // get the number of characters
3290                         $nb = count($chars);
3291                         // handle single space character
3292                         if (($nb == 1) AND preg_match("/[\s]/u", $s)) {
3293                                 if ($this->rtl) {
3294                                         $this->x -= $this->GetStringWidth($s);
3295                                 } else {
3296                                         $this->x += $this->GetStringWidth($s);
3297                                 }
3298                                 return;
3299                         }
3300                         // store current position
3301                         $prevx = $this->x;
3302                         $prevy = $this->y;
3303                         // calculate remaining line width ($w)
3304                         if ($this->rtl) {
3305                                 $w = $this->x - $this->lMargin;
3306                         } else {
3307                                 $w = $this->w - $this->rMargin - $this->x;
3308                         }
3309                         // max column width
3310                         $wmax = $w - (2 * $this->cMargin);
3311                         $i = 0; // character position
3312                         $j = 0; // current starting position
3313                         $sep = -1; // position of the last blank space
3314                         $l = 0; // current string lenght
3315                         $nl = 0; //number of lines
3316                         $linebreak = false;
3317                         // for each character
3318                         while ($i < $nb) {
3319                                 //Get the current character
3320                                 $c = $chars[$i];
3321                                 if ($c == 10) { // 10 = "\n" = new line
3322                                         //Explicit line break
3323                                         if ($align == "J") {
3324                                                 if ($this->rtl) {
3325                                                         $talign = "R";
3326                                                 } else {
3327                                                         $talign = "L";
3328                                                 }
3329                                         } else {
3330                                                 $talign = $align;
3331                                         }
3332                                         if ($firstline) {
3333                                                 $startx = $this->x;
3334                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3335                                                 if ($this->rtl) {
3336                                                         $this->endlinex = $startx - $linew;
3337                                                 } else {
3338                                                         $this->endlinex = $startx + $linew;
3339                                                 }
3340                                                 $w = $linew;
3341                                                 $tmpcmargin = $this->cMargin;
3342                                                 $this->cMargin = 0;
3343                                         }
3344                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $talign, $fill, $link, $stretch);
3345                                         if ($firstline) {
3346                                                 $this->cMargin = $tmpcmargin;
3347                                                 return ($this->UTF8ArrSubString($chars, $i));
3348                                         }
3349                                         $nl++;
3350                                         $j = $i + 1;
3351                                         $l = 0;
3352                                         $sep = -1;
3353                                         $w = $this->getRemainingWidth();
3354                                         $wmax = $w - (2 * $this->cMargin);
3355                                 } else {
3356                                         if (preg_match("/[\s]/u", $this->unichr($c))) {
3357                                                 // update last blank space position
3358                                                 $sep = $i;
3359                                         }
3360                                         // update string length
3361                                         if (($this->isunicode) AND ($arabic)) {
3362                                                 // with bidirectional algorithm some chars may be changed affecting the line length
3363                                                 // *** very slow ***
3364                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), $this->tmprtl));
3365                                         } else {
3366                                                 $l += $this->GetCharWidth($c);
3367                                         }
3368                                         if ($l > $wmax) {
3369                                                 // we have reached the end of column
3370                                                 if ($sep == -1) {
3371                                                         // check if the line was already started
3372                                                         if (($this->rtl AND ($this->x < ($this->w - $this->rMargin)))
3373                                                                 OR ((!$this->rtl) AND ($this->x > $this->lMargin))) {
3374                                                                 // print a void cell and go to next line
3375                                                                 $this->Cell($w, $h, "", 0, 1);
3376                                                                 $linebreak = true;
3377                                                                 if ($firstline) {
3378                                                                         return ($this->UTF8ArrSubString($chars, $j));
3379                                                                 }
3380                                                         } else {
3381                                                                 // truncate the word because do not fit on column
3382                                                                 if ($firstline) {
3383                                                                         $startx = $this->x;
3384                                                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3385                                                                         if ($this->rtl) {
3386                                                                                 $this->endlinex = $startx - $linew;
3387                                                                         } else {
3388                                                                                 $this->endlinex = $startx + $linew;
3389                                                                         }
3390                                                                         $w = $linew;
3391                                                                         $tmpcmargin = $this->cMargin;
3392                                                                         $this->cMargin = 0;
3393                                                                 }
3394                                                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $align, $fill, $link, $stretch);
3395                                                                 if ($firstline) {
3396                                                                         $this->cMargin = $tmpcmargin;
3397                                                                         return ($this->UTF8ArrSubString($chars, $i));
3398                                                                 }
3399                                                                 $j = $i;
3400                                                                 $i--;
3401                                                         }
3402                                                 } else {
3403                                                         // word wrapping
3404                                                         if ($firstline) {
3405                                                                 $startx = $this->x;
3406                                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $sep), $this->tmprtl));
3407                                                                 if ($this->rtl) {
3408                                                                         $this->endlinex = $startx - $linew;
3409                                                                 } else {
3410                                                                         $this->endlinex = $startx + $linew;
3411                                                                 }
3412                                                                 $w = $linew;
3413                                                                 $tmpcmargin = $this->cMargin;
3414                                                                 $this->cMargin = 0;
3415                                                         }
3416                                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $sep), 0, 1, $align, $fill, $link, $stretch);
3417                                                         if ($firstline) {
3418                                                                 $this->cMargin = $tmpcmargin;
3419                                                                 return ($this->UTF8ArrSubString($chars, $sep));
3420                                                         }
3421                                                         $i = $sep;
3422                                                         $sep = -1;
3423                                                         $j = ($i+1);
3424                                                 }
3425                                                 $w = $this->getRemainingWidth();
3426                                                 $wmax = $w - (2 * $this->cMargin);
3427                                                 if ($linebreak) {
3428                                                         $linebreak = false;
3429                                                 } else {
3430                                                         $nl++;
3431                                                         $l = 0;
3432                                                 }
3433                                         }
3434                                 }
3435                                 $i++;
3436                         } // end while i < nb
3437                         // print last substring (if any)
3438                         if ($l > 0) {
3439                                 switch ($align) {
3440                                         case "J":
3441                                         case "C": {
3442                                                 $w = $w;
3443                                                 break;
3444                                         }
3445                                         case "L": {
3446                                                 if ($this->rtl) {
3447                                                         $w = $w;
3448                                                 } else {
3449                                                         $w = $l;
3450                                                 }
3451                                                 break;
3452                                         }
3453                                         case "R": {
3454                                                 if ($this->rtl) {
3455                                                         $w = $l;
3456                                                 } else {
3457                                                         $w = $w;
3458                                                 }
3459                                                 break;
3460                                         }
3461                                         default: {
3462                                                 $w = $l;
3463                                                 break;
3464                                         }
3465                                 }
3466                                 if ($firstline) {
3467                                         $startx = $this->x;
3468                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $nb), $this->tmprtl));
3469                                         if ($this->rtl) {
3470                                                 $this->endlinex = $startx - $linew;
3471                                         } else {
3472                                                 $this->endlinex = $startx + $linew;
3473                                         }
3474                                         $w = $linew;
3475                                         $tmpcmargin = $this->cMargin;
3476                                         $this->cMargin = 0;
3477                                 }
3478                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $nb), 0, $ln, $align, $fill, $link, $stretch);
3479                                 if ($firstline) {
3480                                         $this->cMargin = $tmpcmargin;
3481                                         return ($this->UTF8ArrSubString($chars, $nb));
3482                                 }
3483                                 $nl++;
3484                         }
3485                         if ($firstline) {
3486                                 return "";
3487                         }
3488                         return $nl;
3489                 }
3490
3491                 /**
3492                 * Returns the remaining width between the current position and margins.
3493                 * @return int Return the remaining width
3494                 * @access protected
3495                 */
3496                 function getRemainingWidth() {
3497                         if ($this->rtl) {
3498                                 return ($this->x - $this->lMargin);
3499                         } else {
3500                                 return ($this->w - $this->rMargin - $this->x);
3501                         }
3502                 }
3503
3504          /**
3505                 * Extract a slice of the $strarr array and return it as string.
3506                 * @param string $strarr The input array of characters.
3507                 * @param int $start the starting element of $strarr.
3508                 * @param int $end first element that will not be returned.
3509                 * @return Return part of a string
3510                 */
3511                 function UTF8ArrSubString($strarr, $start='', $end='') {
3512                         if (strlen($start) == 0) {
3513                                 $start = 0;
3514                         }
3515                         if (strlen($end) == 0) {
3516                                 $end = count($strarr);
3517                         }
3518                         $string = "";
3519                         for ($i=$start; $i < $end; $i++) {
3520                                 $string .= $this->unichr($strarr[$i]);
3521                         }
3522                         return $string;
3523                 }
3524
3525                 /**
3526                 * Returns the unicode caracter specified by UTF-8 code
3527                 * @param int $c UTF-8 code
3528                 * @return Returns the specified character.
3529                 * @author Miguel Perez, Nicola Asuni
3530                 * @since 2.3.000 (2008-03-05)
3531                 */
3532                 function unichr($c) {
3533                         if (!$this->isunicode) {
3534                                 return chr($c);
3535                         } elseif ($c == '') {
3536                                 return '';
3537                         } elseif ($c <= 0x7F) {
3538                                 // one byte
3539                                 return chr($c);
3540                         } elseif ($c <= 0x7FF) {
3541                                 // two bytes
3542                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
3543                         } elseif ($c <= 0xFFFF) {
3544                                 // three bytes
3545                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3546                         } elseif ($c <= 0x10FFFF) {
3547                                 // four bytes
3548                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3549                         } else {
3550                                 return "";
3551                         }
3552                 }
3553
3554                 /**
3555                 * Puts an image in the page.
3556                 * The upper-left corner must be given.
3557                 * The dimensions can be specified in different ways:<ul>
3558                 * <li>explicit width and height (expressed in user unit)</li>
3559                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
3560                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
3561                 * 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;
3562                 * The format can be specified explicitly or inferred from the file extension.<br />
3563                 * It is possible to put a link on the image.<br />
3564                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
3565                 * @param string $file Name of the file containing the image.
3566                 * @param float $x Abscissa of the upper-left corner.
3567                 * @param float $y Ordinate of the upper-left corner.
3568                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
3569                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
3570                 * @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.
3571                 * @param mixed $link URL or identifier returned by AddLink().
3572                 * @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>
3573                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
3574                 * @param int $dpi dot-per-inch resolution used on resize
3575                 * @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>
3576                 * @since 1.1
3577                 * @see AddLink()
3578                 */
3579                 function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
3580                         // get image size
3581                         list($pixw, $pixh) = getimagesize($file);
3582                         // calculate image width and height on document
3583                         if (($w <= 0) AND ($h <= 0)) {
3584                                 // convert image size to document unit
3585                                 $w = $pixw / ($this->imgscale * $this->k);
3586                                 $h = $pixh / ($this->imgscale * $this->k);
3587                         } elseif ($w <= 0) {
3588                                 $w = $h * $pixw / $pixh;
3589                         } elseif ($h <= 0) {
3590                                 $h = $w * $pixh / $pixw;
3591                         }
3592                         // calculate new minimum dimensions in pixels
3593                         $neww = round($w * $this->k * $dpi / $this->dpi);
3594                         $newh = round($h * $this->k * $dpi / $this->dpi);
3595                         // check if resize is necessary (resize is used only to reduce the image)
3596                         if (($neww * $newh) >= ($pixw * $pixh)) {
3597                                 $resize = false;
3598                         }
3599                         // check if image has been already added on document
3600                         if (!isset($this->images[$file])) {
3601                                 //First use of image, get info
3602                                 if ($type == '') {
3603                                         $fileinfo = pathinfo($file);
3604                                         if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
3605                                                 $type = $fileinfo['extension'];
3606                                         } else {
3607                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
3608                                         }
3609                                 }
3610                                 $type = strtolower($type);
3611                                 if ($type == "jpg") {
3612                                         $type = "jpeg";
3613                                 }
3614                                 $mqr = ini_get('magic_quotes_runtime');
3615                                 ini_set('magic_quotes_runtime', 0);
3616                                 // Specific image handlers
3617                                 $mtd = '_parse'.$type;
3618                                 // GD image handler function
3619                                 $gdfunction = "imagecreatefrom".$type;
3620                                 $info = false;
3621                                 if ((method_exists($this,$mtd)) AND (!($resize AND function_exists($gdfunction)))) {
3622                                         $info = $this->$mtd($file);
3623                                 }
3624                                 if (!$info) {
3625                                         if (function_exists($gdfunction)) {
3626                                                 $img = $gdfunction($file);
3627                                                 if ($resize) {
3628                                                         $imgr = imagecreatetruecolor($neww, $newh);
3629                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
3630                                                         $info = $this->_toJPEG($imgr);
3631                                                 } else {
3632                                                         $info = $this->_toJPEG($img);
3633                                                 }
3634                                         } else {
3635                                                 return;
3636                                         }
3637                                 }
3638                                 if ($info === false) {
3639                                         //If false, we cannot process image
3640                                         return;
3641                                 }
3642                                 ini_set('magic_quotes_runtime', $mqr);
3643                                 $info['i'] = count($this->images) + 1;
3644                                 // add image to document
3645                                 $this->images[$file] = $info;
3646                         } else {
3647                                 $info = $this->images[$file];
3648                         }
3649                         // Check whether we need a new page first as this does not fit
3650                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
3651                                 // Automatic page break
3652                                 $this->AddPage($this->CurOrientation);
3653                                 // Reset coordinates to top fo next page
3654                                 //$x = $this->GetX();
3655                                 $y = $this->GetY() + $this->cMargin;
3656                         }
3657                         // 2007-10-19 Warren Sherliker: End Edit
3658                         // set bottomcoordinates
3659                         $this->img_rb_y = $y + $h;
3660                         // set alignment
3661                         if ($this->rtl) {
3662                                 if ($palign == 'L') {
3663                                         $ximg = $this->lMargin;
3664                                         // set right side coordinate
3665                                         $this->img_rb_x = $ximg + $w;
3666                                 } elseif ($palign == 'C') {
3667                                         $ximg = ($this->w - $x - $w) / 2;
3668                                         // set right side coordinate
3669                                         $this->img_rb_x = $ximg + $w;
3670                                 } else {
3671                                         $ximg = $this->w - $x - $w;
3672                                         // set left side coordinate
3673                                         $this->img_rb_x = $ximg;
3674                                 }
3675                         } else {
3676                                 if ($palign == 'R') {
3677                                         $ximg = $this->w - $this->rMargin - $w;
3678                                         // set left side coordinate
3679                                         $this->img_rb_x = $ximg;
3680                                 } elseif ($palign == 'C') {
3681                                         $ximg = ($this->w - $x - $w) / 2;
3682                                         // set right side coordinate
3683                                         $this->img_rb_x = $ximg + $w;
3684                                 } else {
3685                                         $ximg = $x;
3686                                         // set right side coordinate
3687                                         $this->img_rb_x = $ximg + $w;
3688                                 }
3689                         }
3690                         $xkimg = $ximg * $this->k;
3691                         $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']));
3692                         if ($link) {
3693                                 $this->Link($ximg, $y, $w, $h, $link);
3694                         }
3695                         // set pointer to align the successive text/objects
3696                         switch($align) {
3697                                 case 'T': {
3698                                         $this->y = $y;
3699                                         $this->x = $this->img_rb_x;
3700                                         break;
3701                                 }
3702                                 case 'M': {
3703                                         $this->y = $y + round($h/2);
3704                                         $this->x = $this->img_rb_x;
3705                                         break;
3706                                 }
3707                                 case 'B': {
3708                                         $this->y = $this->img_rb_y;
3709                                         $this->x = $this->img_rb_x;
3710                                         break;
3711                                 }
3712                                 case 'N': {
3713                                         $this->SetY($this->img_rb_y);
3714                                         break;
3715                                 }
3716                                 default:{
3717                                         break;
3718                                 }
3719                         }
3720                         $this->endlinex = $this->img_rb_x;
3721                         return $info;
3722                 }
3723
3724                 /**
3725                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
3726                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
3727                 * @param string $file Image file name.
3728                 * @param image $image Image object.
3729                 * return image JPEG image object.
3730                 * @access protected
3731                 */
3732                 function _toJPEG($image) {
3733                         $tempname = tempnam(K_PATH_CACHE,'jpg');
3734                         imagejpeg($image, $tempname, $this->jpeg_quality);
3735                         imagedestroy($image);
3736                         $retvars = $this->_parsejpeg($tempname);
3737                         // tidy up by removing temporary image
3738                         unlink($tempname);
3739                         return $retvars;
3740                 }
3741
3742                 /**
3743                 * Extract info from a JPEG file without using the GD library.
3744                 * @param string $file image file to parse
3745                 * @return array structure containing the image data
3746                 * @access protected
3747                 */
3748                 function _parsejpeg($file) {
3749                         $a = getimagesize($file);
3750                         if (empty($a)) {
3751                                 $this->Error('Missing or incorrect image file: '.$file);
3752                         }
3753                         if ($a[2] != 2) {
3754                                 $this->Error('Not a JPEG file: '.$file);
3755                         }
3756                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
3757                                 $colspace = 'DeviceRGB';
3758                         } elseif ($a['channels'] == 4) {
3759                                 $colspace = 'DeviceCMYK';
3760                         }       else {
3761                                 $colspace = 'DeviceGray';
3762                         }
3763                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
3764                         $data = file_get_contents($file);
3765                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
3766                 }
3767
3768                 /**
3769                 * Extract info from a PNG file without using the GD library.
3770                 * @param string $file image file to parse
3771                 * @return array structure containing the image data
3772                 * @access protected
3773                 */
3774                 function _parsepng($file) {
3775                         $f = fopen($file,'rb');
3776                         if (empty($f)) {
3777                                 $this->Error('Can\'t open image file: '.$file);
3778                         }
3779                         //Check signature
3780                         if (fread($f,8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3781                                 $this->Error('Not a PNG file: '.$file);
3782                         }
3783                         //Read header chunk
3784                         fread($f,4);
3785                         if (fread($f,4) != 'IHDR') {
3786                                 $this->Error('Incorrect PNG file: '.$file);
3787                         }
3788                         $w = $this->_freadint($f);
3789                         $h = $this->_freadint($f);
3790                         $bpc = ord(fread($f,1));
3791                         if ($bpc > 8) {
3792                                 //$this->Error('16-bit depth not supported: '.$file);
3793                                 return false;
3794                         }
3795                         $ct = ord(fread($f,1));
3796                         if ($ct == 0) {
3797                                 $colspace = 'DeviceGray';
3798                         } elseif ($ct == 2) {
3799                                 $colspace = 'DeviceRGB';
3800                         } elseif ($ct == 3) {
3801                                 $colspace = 'Indexed';
3802                         } else {
3803                                 //$this->Error('Alpha channel not supported: '.$file);
3804                                 return false;
3805                         }
3806                         if (ord(fread($f,1)) != 0) {
3807                                 //$this->Error('Unknown compression method: '.$file);
3808                                 return false;
3809                         }
3810                         if (ord(fread($f,1)) != 0) {
3811                                 //$this->Error('Unknown filter method: '.$file);
3812                                 return false;
3813                         }
3814                         if (ord(fread($f,1)) != 0) {
3815                                 //$this->Error('Interlacing not supported: '.$file);
3816                                 return false;
3817                         }
3818                         fread($f,4);
3819                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3820                         //Scan chunks looking for palette, transparency and image data
3821                         $pal = '';
3822                         $trns = '';
3823                         $data = '';
3824                         do {
3825                                 $n = $this->_freadint($f);
3826                                 $type = fread($f,4);
3827                                 if ($type == 'PLTE') {
3828                                         //Read palette
3829                                         $pal = fread($f,$n);
3830                                         fread($f,4);
3831                                 } elseif ($type == 'tRNS') {
3832                                         //Read transparency info
3833                                         $t = fread($f,$n);
3834                                         if ($ct == 0) {
3835                                                 $trns = array(ord(substr($t,1,1)));
3836                                         }
3837                                         elseif ($ct == 2) {
3838                                                 $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
3839                                         } else {
3840                                                 $pos = strpos($t,chr(0));
3841                                                 if ($pos !== false) {
3842                                                         $trns = array($pos);
3843                                                 }
3844                                         }
3845                                         fread($f, 4);
3846                                 } elseif ($type == 'IDAT') {
3847                                         //Read image data block
3848                                         $data .= fread($f,$n);
3849                                         fread($f, 4);
3850                                 } elseif ($type == 'IEND') {
3851                                         break;
3852                                 } else {
3853                                         fread($f, $n+4);
3854                                 }
3855                         }
3856                         while ($n);
3857                         if (($colspace == 'Indexed') AND (empty($pal))) {
3858                                 //$this->Error('Missing palette in '.$file);
3859                                 return false;
3860                         }
3861                         fclose($f);
3862                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
3863                 }
3864
3865                 /**
3866                 * Performs a line break.
3867                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
3868                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
3869                 * @param boolean $cell if true add a cMargin to the x coordinate
3870                 * @since 1.0
3871                 * @see Cell()
3872                 */
3873                 function Ln($h='', $cell=false) {
3874                         //Line feed; default value is last cell height
3875                         if ($cell) {
3876                                 $cellmargin = $this->cMargin;
3877                         } else {
3878                                 $cellmargin = 0;
3879                         }
3880                         if ($this->rtl) {
3881                                 $this->x = $this->w - $this->rMargin - $cellmargin;
3882                         } else {
3883                                 $this->x = $this->lMargin + $cellmargin;
3884                         }
3885                         if (is_string($h)) {
3886                                 $this->y += $this->lasth;
3887                         } else {
3888                                 $this->y += $h;
3889                         }
3890                         $this->newline = true;
3891                 }
3892
3893                 /**
3894                 * Returns the relative X value of current position.
3895                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
3896                 * @return float
3897                 * @since 1.2
3898                 * @see SetX(), GetY(), SetY()
3899                 */
3900                 function GetX() {
3901                         //Get x position
3902                         if ($this->rtl) {
3903                                 return ($this->w - $this->x);
3904                         } else {
3905                                 return $this->x;
3906                         }
3907                 }
3908
3909                 /**
3910                 * Returns the absolute X value of current position.
3911                 * @return float
3912                 * @since 1.2
3913                 * @see SetX(), GetY(), SetY()
3914                 */
3915                 function GetAbsX() {
3916                         return $this->x;
3917                 }
3918
3919                 /**
3920                 * Returns the ordinate of the current position.
3921                 * @return float
3922                 * @since 1.0
3923                 * @see SetY(), GetX(), SetX()
3924                 */
3925                 function GetY() {
3926                         //Get y position
3927                         return $this->y;
3928                 }
3929
3930                 /**
3931                 * Defines the abscissa of the current position.
3932                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
3933                 * @param float $x The value of the abscissa.
3934                 * @since 1.2
3935                 * @see GetX(), GetY(), SetY(), SetXY()
3936                 */
3937                 function SetX($x) {
3938                         //Set x position
3939                         if ($this->rtl) {
3940                                 if ($x >= 0) {
3941                                         $this->x = $this->w - $x;
3942                                 } else {
3943                                         $this->x = abs($x);
3944                                 }
3945                         } else {
3946                                 if ($x >= 0) {
3947                                         $this->x = $x;
3948                                 } else {
3949                                         $this->x = $this->w + $x;
3950                                 }
3951                         }
3952                 }
3953
3954                 /**
3955                 * Moves the current abscissa back to the left margin and sets the ordinate.
3956                 * If the passed value is negative, it is relative to the bottom of the page.
3957                 * @param float $y The value of the ordinate.
3958                 * @since 1.0
3959                 * @see GetX(), GetY(), SetY(), SetXY()
3960                 */
3961                 function SetY($y) {
3962                         //Set y position and reset x
3963                         if ($this->rtl) {
3964                                 $this->x = $this->w - $this->rMargin;
3965                         } else {
3966                                 $this->x = $this->lMargin;
3967                         }
3968                         if ($y >= 0) {
3969                                 $this->y = $y;
3970                         } else {
3971                                 $this->y = $this->h + $y;
3972                         }
3973                 }
3974
3975                 /**
3976                 * Defines the abscissa and ordinate of the current position.
3977                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
3978                 * @param float $x The value of the abscissa
3979                 * @param float $y The value of the ordinate
3980                 * @since 1.2
3981                 * @see SetX(), SetY()
3982                 */
3983                 function SetXY($x, $y) {
3984                         //Set x and y positions
3985                         $this->SetY($y);
3986                         $this->SetX($x);
3987                 }
3988
3989                 /**
3990                 * Send the document to a given destination: string, local file or browser.
3991                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
3992                 * The method first calls Close() if necessary to terminate the document.
3993                 * @param string $name The name of the file when saved.
3994                 * @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>
3995                 * @since 1.0
3996                 * @see Close()
3997                 */
3998                 function Output($name='doc.pdf', $dest='I') {
3999                         //Output PDF to some destination
4000                         //Finish document if necessary
4001                         if ($this->state < 3) {
4002                                 $this->Close();
4003                         }
4004                         //Normalize parameters
4005                         if (is_bool($dest)) {
4006                                 $dest = $dest ? 'D' : 'F';
4007                         }
4008                         $dest = strtoupper($dest);
4009                         if ($dest != 'F') {
4010                                 $name = str_replace("+", "%20", urlencode($name));
4011                                 $name = preg_replace('/[\r\n]+\s*/', '' , $name);
4012                         }
4013                         switch($dest) {
4014                                 case 'I': {
4015                                         //Send to standard output
4016                                         if (ob_get_contents()) {
4017                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4018                                         }
4019                                         if (php_sapi_name() != 'cli') {
4020                                                 //We send to a browser
4021                                                 header('Content-Type: application/pdf');
4022                                                 if (headers_sent()) {
4023                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
4024                                                 }
4025                                                 header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4026                                                 header("Pragma: public");
4027                                                 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4028                                                 header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");      
4029                                                 header('Content-Length: '.strlen($this->buffer));
4030                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
4031                                         }
4032                                         echo $this->buffer;
4033                                         break;
4034                                 }
4035                                 case 'D': {
4036                                         //Download file
4037                                         if (ob_get_contents()) {
4038                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4039                                         }
4040                                         header('Content-Description: File Transfer');
4041                                         if (headers_sent()) {
4042                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
4043                                         }
4044                                         header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4045                                         header("Pragma: public");
4046                                         header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4047                                         header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
4048                                         // force download dialog
4049                                         header("Content-Type: application/force-download");
4050                                         header("Content-Type: application/octet-stream", false);
4051                                         header("Content-Type: application/download", false);
4052                                         // use the Content-Disposition header to supply a recommended filename
4053                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
4054                                         header("Content-Transfer-Encoding: binary");
4055                                         header("Content-Length: ".strlen($this->buffer));
4056                                         echo $this->buffer;
4057                                         break;
4058                                 }
4059                                 case 'F': {
4060                                         //Save to local file
4061                                         $f = fopen($name, 'wb');
4062                                         if (!$f) {
4063                                                 $this->Error('Unable to create output file: '.$name);
4064                                         }
4065                                         fwrite($f, $this->buffer,strlen($this->buffer));
4066                                         fclose($f);
4067                                         break;
4068                                 }
4069                                 case 'S': {
4070                                         //Return as a string
4071                                         return $this->buffer;
4072                                 }
4073                                 default: {
4074                                         $this->Error('Incorrect output destination: '.$dest);
4075                                 }
4076                         }
4077                         return '';
4078                 }
4079
4080                 /**
4081                 * Check for locale-related bug
4082                 * @access protected
4083                 */
4084                 function _dochecks() {
4085                         //Check for locale-related bug
4086                         if (1.1 == 1) {
4087                                 $this->Error('Don\'t alter the locale before including class file');
4088                         }
4089                         //Check for decimal separator
4090                         if (sprintf('%.1f', 1.0) != '1.0') {
4091                                 setlocale(LC_NUMERIC, 'C');
4092                         }
4093                 }
4094
4095                 /**
4096                 * Return fonts path
4097                 * @return string
4098                 * @access protected
4099                 */
4100                 function _getfontpath() {
4101                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
4102                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
4103                         }
4104                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
4105                 }
4106
4107                 /**
4108                 * Output pages.
4109                 * @access protected
4110                 */
4111                 function _putpages() {
4112                         $nb = count($this->pages);
4113                         if (!empty($this->pagegroups)) {
4114                                 // do page number replacement
4115                                 foreach ($this->pagegroups as $k => $v) {
4116                                         $vu = $this->UTF8ToUTF16BE($v, false);
4117                                         $alias_a = $this->_escape($k);
4118                                         $alias_au = $this->_escape("{".$k."}");
4119                                         if ($this->isunicode) {
4120                                                 $alias_b = $this->_escape($this->UTF8ToLatin1($k));
4121                                                 $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$k."}"));
4122                                                 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
4123                                                 $alias_cu = $this->_escape($this->utf8StrRev("{".$k."}", false, $this->tmprtl));
4124                                         }
4125                                         for ($n = 1; $n <= $nb; $n++) {
4126                                                 $this->pages[$n] = str_replace($alias_au, $vu, $this->pages[$n]);
4127                                                 if ($this->isunicode) {
4128                                                         $this->pages[$n] = str_replace($alias_bu, $vu, $this->pages[$n]);
4129                                                         $this->pages[$n] = str_replace($alias_cu, $vu, $this->pages[$n]);
4130                                                         $this->pages[$n] = str_replace($alias_b, $v, $this->pages[$n]);
4131                                                         $this->pages[$n] = str_replace($alias_c, $v, $this->pages[$n]);
4132                                                 }
4133                                                 $this->pages[$n] = str_replace($alias_a, $v, $this->pages[$n]);
4134                                         }
4135                                 }
4136                         }
4137                         if (!empty($this->AliasNbPages)) {
4138                                 $nbu = $this->UTF8ToUTF16BE($nb, false); // replacement for unicode font
4139                                 $alias_a = $this->_escape($this->AliasNbPages);
4140                                 $alias_au = $this->_escape("{".$this->AliasNbPages."}");
4141                                 if ($this->isunicode) {
4142                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
4143                                         $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$this->AliasNbPages."}"));
4144                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
4145                                         $alias_cu = $this->_escape($this->utf8StrRev("{".$this->AliasNbPages."}", false, $this->tmprtl));
4146                                 }
4147                                 //Replace number of pages
4148                                 for($n = 1; $n <= $nb; $n++) {
4149                                         $this->pages[$n] = str_replace($alias_au, $nbu, $this->pages[$n]);
4150                                         if ($this->isunicode) {
4151                                                 $this->pages[$n] = str_replace($alias_bu, $nbu, $this->pages[$n]);
4152                                                 $this->pages[$n] = str_replace($alias_cu, $nbu, $this->pages[$n]);
4153                                                 $this->pages[$n] = str_replace($alias_b, $nb, $this->pages[$n]);
4154                                                 $this->pages[$n] = str_replace($alias_c, $nb, $this->pages[$n]);
4155                                         }
4156                                         $this->pages[$n] = str_replace($alias_a, $nb, $this->pages[$n]);
4157                                 }
4158                         }
4159                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4160                         for($n=1; $n <= $nb; $n++) {
4161                                 //Page
4162                                 $this->_newobj();
4163                                 $this->_out('<</Type /Page');
4164                                 $this->_out('/Parent 1 0 R');
4165                                 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
4166                                 $this->_out('/Resources 2 0 R');
4167                                 $this->_putannots($n);
4168                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
4169                                 $this->_out('endobj');
4170                                 //Page content
4171                                 $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
4172                                 $this->_newobj();
4173                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
4174                                 $this->_putstream($p);
4175                                 $this->_out('endobj');
4176                         }
4177                         //Pages root
4178                         $this->offsets[1] = strlen($this->buffer);
4179                         $this->_out('1 0 obj');
4180                         $this->_out('<</Type /Pages');
4181                         $kids='/Kids [';
4182                         for($i=0; $i < $nb; $i++) {
4183                                 $kids .= (3+2*$i).' 0 R ';
4184                         }
4185                         $this->_out($kids.']');
4186                         $this->_out('/Count '.$nb);
4187                         //$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
4188                         $this->_out('>>');
4189                         $this->_out('endobj');
4190                 }
4191
4192                 /**
4193                 * Output Page Annotations.
4194                 * See section 8.4 of PDF reference.
4195                 * @param int $n page number
4196                 * @access protected
4197                 * @author Nicola Asuni
4198                 * @since 4.0.018 (2008-08-06)
4199                 */
4200                 function _putannots($n) {
4201                         if (isset($this->PageAnnots[$n])) {
4202                                 $annots = '/Annots [';
4203                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
4204                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
4205                                         $a = $pl['x'] * $this->k;
4206                                         $b = $this->hPt - $pl['y'] * $this->k;
4207                                         $c = $pl['w'] * $this->k;
4208                                         $d = $pl['h'] * $this->k;
4209                                         $rect = sprintf('%.2f %.2f %.2f %.2f', $a, $b, $a+$c, $b-$d);
4210                                         $annots .= '<</Type /Annot';
4211                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
4212                                         $annots .= ' /Rect ['.$rect.']';
4213                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
4214                                         //$annots .= ' /P ';
4215                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
4216                                         $annots .= ' /M '.$this->_datestring('D:'.date('YmdHis'));
4217                                         if (isset($pl['opt']['f'])) {
4218                                                 $val = 0;
4219                                                 if (is_array($pl['opt']['f'])) {
4220                                                         foreach ($pl['opt']['f'] as $f) {
4221                                                                 switch (strtolower($f)) {
4222                                                                         case 'invisible': {
4223                                                                                 $val += 1 << 0;
4224                                                                                 break;
4225                                                                         }
4226                                                                         case 'hidden': {
4227                                                                                 $val += 1 << 1;
4228                                                                                 break;
4229                                                                         }
4230                                                                         case 'print': {
4231                                                                                 $val += 1 << 2;
4232                                                                                 break;
4233                                                                         }
4234                                                                         case 'nozoom': {
4235                                                                                 $val += 1 << 3;
4236                                                                                 break;
4237                                                                         }
4238                                                                         case 'norotate': {
4239                                                                                 $val += 1 << 4;
4240                                                                                 break;
4241                                                                         }
4242                                                                         case 'noview': {
4243                                                                                 $val += 1 << 5;
4244                                                                                 break;
4245                                                                         }
4246                                                                         case 'readonly': {
4247                                                                                 $val += 1 << 6;
4248                                                                                 break;
4249                                                                         }
4250                                                                         case 'locked': {
4251                                                                                 $val += 1 << 8;
4252                                                                                 break;
4253                                                                         }
4254                                                                         case 'togglenoview': {
4255                                                                                 $val += 1 << 9;
4256                                                                                 break;
4257                                                                         }
4258                                                                         case 'lockedcontents': {
4259                                                                                 $val += 1 << 10;
4260                                                                                 break;
4261                                                                         }
4262                                                                         default: {
4263                                                                                 break;
4264                                                                         }
4265                                                                 }
4266                                                         }
4267                                                 }
4268                                                 $annots .= ' /F '.intval($val);
4269                                         }
4270                                         //$annots .= ' /AP ';
4271                                         //$annots .= ' /AS ';
4272                                         $annots .= ' /Border [';
4273                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
4274                                                 $annots .= intval($pl['opt']['border'][0]).' ';
4275                                                 $annots .= intval($pl['opt']['border'][1]).' ';
4276                                                 $annots .= intval($pl['opt']['border'][2]);
4277                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
4278                                                         $annots .= ' [';
4279                                                         foreach ($pl['opt']['border'][3] as $dash) {
4280                                                                 $annots .= intval($dash).' ';
4281                                                         }
4282                                                         $annots .= ']';
4283                                                 }
4284                                         } else {
4285                                                 $annots .= '0 0 0';
4286                                         }
4287                                         $annots .= ']';
4288                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
4289                                                 $annots .= ' /BS <<Type /Border';
4290                                                 if (isset($pl['opt']['bs']['w'])) {
4291                                                         $annots .= ' /W '.sprintf("%.4f", floatval($pl['opt']['bs']['w']));
4292                                                 }
4293                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
4294                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $markups)) {
4295                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4296                                                 }
4297                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
4298                                                         $annots .= ' /D [';
4299                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
4300                                                                 $cord = floatval($cord);
4301                                                                 $annots .= sprintf(" %.4f", $cord);
4302                                                         }
4303                                                         $annots .= ']';
4304                                                 }
4305                                                 $annots .= '>>';
4306                                         }
4307                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
4308                                                 $annots .= ' /BE <<';
4309                                                 $bstyles = array('S', 'C');
4310                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
4311                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4312                                                 } else {
4313                                                         $annots .= ' /S /S';
4314                                                 }
4315                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
4316                                                         $annots .= ' /I '.sprintf(" %.4f", $pl['opt']['be']['i']);
4317                                                 }
4318                                                 $annots .= '>>';
4319                                         }
4320                                         $annots .= ' /C [';
4321                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
4322                                                 foreach ($pl['opt']['c'] as $col) {
4323                                                         $col = intval($col);
4324                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
4325                                                         $annots .= sprintf(" %.4f", $color);
4326                                                 }
4327                                         }
4328                                         $annots .= ']';
4329                                         //$annots .= ' /StructParent ';
4330                                         //$annots .= ' /OC ';
4331                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
4332                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
4333                                                 // this is a markup type
4334                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
4335                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
4336                                                 }
4337                                                 //$annots .= ' /Popup ';
4338                                                 if (isset($pl['opt']['ca'])) {
4339                                                         $annots .= ' /CA '.sprintf("%.4f", floatval($pl['opt']['ca']));
4340                                                 }
4341                                                 if (isset($pl['opt']['rc'])) {
4342                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4343                                                 }
4344                                                 $annots .= ' /CreationDate '.$this->_datestring('D:'.date('YmdHis'));
4345                                                 //$annots .= ' /IRT ';
4346                                                 if (isset($pl['opt']['subj'])) {
4347                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
4348                                                 }
4349                                                 //$annots .= ' /RT ';
4350                                                 //$annots .= ' /IT ';
4351                                                 //$annots .= ' /ExData ';
4352                                         }
4353                                         switch (strtolower($pl['opt']['subtype'])) {
4354                                                 case 'text': {
4355                                                         if (isset($pl['opt']['open'])) {
4356                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
4357                                                         }
4358                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
4359                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
4360                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
4361                                                         } else {
4362                                                                 $annots .= ' /Name /Note';
4363                                                         }
4364                                                         $statemodels = array('Marked', 'Review');
4365                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
4366                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4367                                                         } else {
4368                                                                 $pl['opt']['statemodel'] = 'Marked';
4369                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4370                                                         }
4371                                                         if ($pl['opt']['statemodel'] == 'Marked') {
4372                                                                 $states = array('Accepted', 'Unmarked');
4373                                                         } else {
4374                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
4375                                                         }
4376                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
4377                                                                 $annots .= ' /State /'.$pl['opt']['state'];
4378                                                         } else {
4379                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
4380                                                                         $annots .= ' /State /Unmarked';
4381                                                                 } else {
4382                                                                         $annots .= ' /State /None';
4383                                                                 }
4384                                                         }
4385                                                         break;
4386                                                 }
4387                                                 case 'link': {
4388                                                         $annots .= ' /A <</S /URI /URI '.$this->_uristring($pl['txt']).'>>';
4389                                                         $hmodes = array('N', 'I', 'O', 'P');
4390                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
4391                                                                 $annots .= ' /H /'.$pl['opt']['h'];
4392                                                         } else {
4393                                                                 $annots .= ' /H /I';
4394                                                         }
4395                                                         //$annots .= ' /Dest ';
4396                                                         //$annots .= ' /PA ';
4397                                                         //$annots .= ' /Quadpoints ';
4398                                                         break;
4399                                                 }
4400                                                 case 'freetext': {
4401                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
4402                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
4403                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
4404                                                         }
4405                                                         if (isset($pl['opt']['rc'])) {
4406                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4407                                                         }
4408                                                         if (isset($pl['opt']['ds'])) {
4409                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
4410                                                         }
4411                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
4412                                                                 $annots .= ' /CL [';
4413                                                                 foreach ($pl['opt']['cl'] as $cl) {
4414                                                                         $annots .= sprintf("%.4f ", $cl * $this->k);
4415                                                                 }
4416                                                                 $annots .= ']';
4417                                                         }
4418                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
4419                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
4420                                                                 $annots .= ' /IT '.$pl['opt']['it'];
4421                                                         }
4422                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
4423                                                                 $l = $pl['opt']['rd'][0] * $this->k;
4424                                                                 $r = $pl['opt']['rd'][1] * $this->k;
4425                                                                 $t = $pl['opt']['rd'][2] * $this->k;
4426                                                                 $b = $pl['opt']['rd'][3] * $this->k;
4427                                                                 $annots .= ' /RD ['.sprintf('%.2f %.2f %.2f %.2f', $l, $r, $t, $b).']';
4428                                                         }
4429                                                         //$annots .= ' /LE ';
4430                                                         break;
4431                                                 }
4432                                                 // ... to be completed ...
4433                                                 case 'line': {
4434                                                         break;
4435                                                 }
4436                                                 case 'square': {
4437                                                         break;
4438                                                 }
4439                                                 case 'circle': {
4440                                                         break;
4441                                                 }
4442                                                 case 'polygon': {
4443                                                         break;
4444                                                 }
4445                                                 case 'polyline': {
4446                                                         break;
4447                                                 }
4448                                                 case 'highlight': {
4449                                                         break;
4450                                                 }
4451                                                 case 'underline': {
4452                                                         break;
4453                                                 }
4454                                                 case 'squiggly': {
4455                                                         break;
4456                                                 }
4457                                                 case 'strikeout': {
4458                                                         break;
4459                                                 }
4460                                                 case 'stamp': {
4461                                                         break;
4462                                                 }
4463                                                 case 'caret': {
4464                                                         break;
4465                                                 }
4466                                                 case 'ink': {
4467                                                         break;
4468                                                 }
4469                                                 case 'popup': {
4470                                                         break;
4471                                                 }
4472                                                 case 'fileattachment': {
4473                                                         break;
4474                                                 }
4475                                                 case 'sound': {
4476                                                         break;
4477                                                 }
4478                                                 case 'movie': {
4479                                                         break;
4480                                                 }
4481                                                 case 'widget': {
4482                                                         break;
4483                                                 }
4484                                                 case 'screen': {
4485                                                         break;
4486                                                 }
4487                                                 case 'printermark': {
4488                                                         break;
4489                                                 }
4490                                                 case 'trapnet': {
4491                                                         break;
4492                                                 }
4493                                                 case 'watermark': {
4494                                                         break;
4495                                                 }
4496                                                 case '3d': {
4497                                                         break;
4498                                                 }
4499                                                 default: {
4500                                                         break;
4501                                                 }
4502                                         }
4503                                         
4504                                 $annots .= '>>';
4505                                 }
4506                                 $this->_out($annots.']');
4507                         }
4508                 }
4509
4510                 /**
4511                 * Output fonts.
4512                 * _putfonts
4513                 * @access protected
4514                 */
4515                 function _putfonts() {
4516                         $nf = $this->n;
4517                         foreach($this->diffs as $diff) {
4518                                 //Encodings
4519                                 $this->_newobj();
4520                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
4521                                 $this->_out('endobj');
4522                         }
4523                         $mqr = ini_get('magic_quotes_runtime');
4524                         ini_set('magic_quotes_runtime', 0);
4525                         foreach($this->FontFiles as $file => $info) {
4526                                 //Font file embedding
4527                                 $this->_newobj();
4528                                 $this->FontFiles[$file]['n'] = $this->n;
4529                                 $font = file_get_contents($this->_getfontpath().strtolower($file));
4530                                 $compressed = (substr($file,-2)=='.z');
4531                                 if ((!$compressed) AND (isset($info['length2']))) {
4532                                         $header = (ord($font[0]) == 128);
4533                                         if ($header) {
4534                                                 //Strip first binary header
4535                                                 $font = substr($font,6);
4536                                         }
4537                                         if ($header AND (ord($font[$info['length1']]) == 128)) {
4538                                                 //Strip second binary header
4539                                                 $font = substr($font, 0, $info['length1']).substr($font, $info['length1']+6);
4540                                         }
4541                                 }
4542                                 $this->_out('<</Length '.strlen($font));
4543                                 if ($compressed) {
4544                                         $this->_out('/Filter /FlateDecode');
4545                                 }
4546                                 $this->_out('/Length1 '.$info['length1']);
4547                                 if (isset($info['length2'])) {
4548                                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
4549                                 }
4550                                 $this->_out('>>');
4551                                 $this->_putstream($font);
4552                                 $this->_out('endobj');
4553                         }
4554                         ini_set('magic_quotes_runtime', $mqr);
4555                         foreach($this->fonts as $k => $font) {
4556                                 //Font objects
4557                                 $this->fonts[$k]['n'] = $this->n + 1;
4558                                 $type = $font['type'];
4559                                 $name = $font['name'];
4560                                 if ($type == 'core') {
4561                                         //Standard font
4562                                         $this->_newobj();
4563                                         $this->_out('<</Type /Font');
4564                                         $this->_out('/BaseFont /'.$name);
4565                                         $this->_out('/Subtype /Type1');
4566                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
4567                                                 $this->_out('/Encoding /WinAnsiEncoding');
4568                                         }
4569                                         $this->_out('>>');
4570                                         $this->_out('endobj');
4571                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
4572                                         //Additional Type1 or TrueType font
4573                                         $this->_newobj();
4574                                         $this->_out('<</Type /Font');
4575                                         $this->_out('/BaseFont /'.$name);
4576                                         $this->_out('/Subtype /'.$type);
4577                                         $this->_out('/FirstChar 32 /LastChar 255');
4578                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
4579                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
4580                                         if ($font['enc']) {
4581                                                 if (isset($font['diff'])) {
4582                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
4583                                                 } else {
4584                                                         $this->_out('/Encoding /WinAnsiEncoding');
4585                                                 }
4586                                         }
4587                                         $this->_out('>>');
4588                                         $this->_out('endobj');
4589                                         //Widths
4590                                         $this->_newobj();
4591                                         $cw = &$font['cw'];
4592                                         $s = '[';
4593                                         for($i=32; $i <= 255; $i++) {
4594                                                 //$s .= $cw[chr($i)].' ';
4595                                                 $s .= $cw[$i].' ';
4596                                         }
4597                                         $this->_out($s.']');
4598                                         $this->_out('endobj');
4599                                         //Descriptor
4600                                         $this->_newobj();
4601                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4602                                         foreach($font['desc'] as $k => $v) {
4603                                                 $s .= ' /'.$k.' '.$v;
4604                                         }
4605                                         $file = $font['file'];
4606                                         if ($file) {
4607                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
4608                                         }
4609                                         $this->_out($s.'>>');
4610                                         $this->_out('endobj');
4611                                 } else {
4612                                         //Allow for additional types
4613                                         $mtd = '_put'.strtolower($type);
4614                                         if (!method_exists($this, $mtd)) {
4615                                                 $this->Error('Unsupported font type: '.$type);
4616                                         }
4617                                         $this->$mtd($font);
4618                                 }
4619                         }
4620                 }
4621
4622                 /**
4623                  * Output CID-0 fonts.
4624                  * @param array $font font data
4625                  * @access protected
4626                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
4627                  * @since 3.2.000 (2008-06-23)
4628                  */
4629                 function _putcidfont0($font) {
4630                         if (isset($font['cidinfo']['uni2cid'])) {
4631                                 // convert unicode to cid.
4632                                 $uni2cid = $font['cidinfo']['uni2cid'];
4633                                 $cw = array();
4634                                 foreach ($font['cw'] as $uni => $width) {
4635                                         if (isset($uni2cid[$uni])) {
4636                                                 $cw[($uni2cid[$uni] + 31)] = $width;
4637                                         } elseif ($uni <= 255) {
4638                                                 $cw[$uni] = $width;
4639                                         } // else unknown character
4640                                 }
4641                                 ksort($cw);
4642                                 $font = array_merge($font, array('cw'=>$cw));
4643                         }
4644                         $longname = $name = $font['name'];
4645                         $enc = $font['enc'];
4646                         if ($enc) {
4647                                 $longname .= "-$enc";
4648                         }
4649                         $this->_newobj();
4650                         $this->_out('<</Type /Font');
4651                         $this->_out('/BaseFont /'.$longname);
4652                         $this->_out('/Subtype /Type0');
4653                         if ($enc) {
4654                                 $this->_out('/Encoding /'.$enc);
4655                         }
4656                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4657                         $this->_out('>>');
4658                         $this->_out('endobj');
4659                         $this->_newobj();
4660                         $this->_out('<</Type /Font');
4661                         $this->_out('/BaseFont /'.$name);
4662                         $this->_out('/Subtype /CIDFontType0');
4663                         $cidinfo = '/Registry ('.$font['cidinfo']['Registry'].') ';
4664                         $cidinfo .= '/Ordering ('.$font['cidinfo']['Ordering'].') ';
4665                         $cidinfo .= '/Supplement '.$font['cidinfo']['Supplement'];
4666                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
4667                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
4668                         $codes = array_keys($font['cw']);
4669                         $first = current($codes);
4670                         $last = end($codes);
4671                         $this->_out('/DW '.$font['dw']);
4672                         $w = '/W [';
4673                         $ranges = array();
4674                         $currange = 0;
4675                         for($i = $first; $i <= $last; $i++) {
4676                                 if (isset($font['cw'][$i]) AND (!$currange)) {
4677                                         $currange = $i - 31;
4678                                 } elseif (!isset($font['cw'][$i])) {
4679                                         $currange = 0;
4680                                 }
4681                                 if ($currange) {
4682                                         $ranges[$currange][] = $font['cw'][$i];
4683                                 }
4684                         }
4685                         foreach($ranges as $k => $ws) {
4686                                 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
4687                         }
4688                         $w .= ' ]';
4689                         $this->_out($w);
4690                         $this->_out('>>');
4691                         $this->_out('endobj');
4692                         $this->_newobj();
4693                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4694                         foreach($font['desc'] as $k => $v) {
4695                                 $s .= ' /'.$k.' '.$v;
4696                         }
4697                         $this->_out($s.'>>');
4698                         $this->_out('endobj');
4699                 }
4700
4701                 /**
4702                  * Output images.
4703                  * @access protected
4704                  */
4705                 function _putimages() {
4706                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4707                         reset($this->images);
4708                         foreach ($this->images as $file => $info) {
4709                                 $this->_newobj();
4710                                 $this->images[$file]['n'] = $this->n;
4711                                 $this->_out('<</Type /XObject');
4712                                 $this->_out('/Subtype /Image');
4713                                 $this->_out('/Width '.$info['w']);
4714                                 $this->_out('/Height '.$info['h']);
4715                                 if (isset($info["masked"])) {
4716                                         $this->_out('/SMask '.($this->n-1).' 0 R');
4717                                 }
4718                                 if ($info['cs'] == 'Indexed') {
4719                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]');
4720                                 } else {
4721                                         $this->_out('/ColorSpace /'.$info['cs']);
4722                                         if ($info['cs'] == 'DeviceCMYK') {
4723                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
4724                                         }
4725                                 }
4726                                 $this->_out('/BitsPerComponent '.$info['bpc']);
4727                                 if (isset($info['f'])) {
4728                                         $this->_out('/Filter /'.$info['f']);
4729                                 }
4730                                 if (isset($info['parms'])) {
4731                                         $this->_out($info['parms']);
4732                                 }
4733                                 if (isset($info['trns']) and is_array($info['trns'])) {
4734                                         $trns='';
4735                                         for($i=0; $i < count($info['trns']); $i++) {
4736                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
4737                                         }
4738                                         $this->_out('/Mask ['.$trns.']');
4739                                 }
4740                                 $this->_out('/Length '.strlen($info['data']).'>>');
4741                                 $this->_putstream($info['data']);
4742                                 unset($this->images[$file]['data']);
4743                                 $this->_out('endobj');
4744                                 //Palette
4745                                 if ($info['cs'] == 'Indexed') {
4746                                         $this->_newobj();
4747                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
4748                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
4749                                         $this->_putstream($pal);
4750                                         $this->_out('endobj');
4751                                 }
4752                         }
4753                 }
4754
4755                 /**
4756                 * Output Spot Colors Resources.
4757                 * @access protected
4758                 * @since 4.0.024 (2008-09-12)
4759                 */
4760                 function _putspotcolors() {
4761                         foreach ($this->spot_colors as $name => $color) {
4762                                 $this->_newobj();
4763                                 $this->spot_colors[$name]['n'] = $this->n;
4764                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
4765                                 $this->_out('/DeviceCMYK <<');
4766                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
4767                                 $this->_out(sprintf('/C1 [%.4f %.4f %.4f %.4f] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
4768                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
4769                                 $this->_out('endobj');
4770                         }
4771                 }
4772
4773                 /**
4774                 * Output object dictionary for images.
4775                 * @access protected
4776                 */
4777                 function _putxobjectdict() {
4778                         foreach($this->images as $image) {
4779                                 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
4780                         }
4781                 }
4782
4783                 /**
4784                 * Output Resources Dictionary.
4785                 * @access protected
4786                 */
4787                 function _putresourcedict(){
4788                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
4789                         $this->_out('/Font <<');
4790                         foreach($this->fonts as $font) {
4791                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
4792                         }
4793                         $this->_out('>>');
4794                         $this->_out('/XObject <<');
4795                         $this->_putxobjectdict();
4796                         $this->_out('>>');
4797                         // visibility
4798                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
4799                         // transparency
4800                         $this->_out('/ExtGState <<');
4801                         foreach($this->extgstates as $k => $extgstate) {
4802                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
4803                         }
4804                         $this->_out('>>');
4805                         // gradients
4806                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
4807                                 $this->_out('/Shading <<');
4808                                 foreach($this->gradients as $id => $grad) {
4809                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
4810                                 }
4811                                 $this->_out('>>');
4812                         }
4813                         // spot colors
4814                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
4815                                 $this->_out('/ColorSpace <<');
4816                                 foreach ($this->spot_colors as $color) {
4817                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
4818                                 }
4819                                 $this->_out('>>');
4820                         }
4821                 }
4822
4823                 /**
4824                 * Output Resources.
4825                 * @access protected
4826                 */
4827                 function _putresources() {
4828                         $this->_putextgstates();
4829                         $this->_putocg();
4830                         $this->_putfonts();
4831                         $this->_putimages();
4832                         $this->_putspotcolors();
4833                         $this->_putshaders();
4834                         //Resource dictionary
4835                         $this->offsets[2] = strlen($this->buffer);
4836                         $this->_out('2 0 obj');
4837                         $this->_out('<<');
4838                         $this->_putresourcedict();
4839                         $this->_out('>>');
4840                         $this->_out('endobj');
4841                         $this->_putjavascript();
4842                         $this->_putbookmarks();
4843                         // encryption
4844                         if ($this->encrypted) {
4845                                 $this->_newobj();
4846                                 $this->enc_obj_id = $this->n;
4847                                 $this->_out('<<');
4848                                 $this->_putencryption();
4849                                 $this->_out('>>');
4850                                 $this->_out('endobj');
4851                         }
4852                 }
4853
4854                 /**
4855                 * Adds some Metadata information
4856                 * (see Chapter 10.2 of PDF Reference)
4857                 * @access protected
4858                 */
4859                 function _putinfo() {
4860                         if (!empty($this->title)) {
4861                                 $this->_out('/Title '.$this->_textstring($this->title));
4862                         }
4863                         if (!empty($this->author)) {
4864                                 $this->_out('/Author '.$this->_textstring($this->author));
4865                         }
4866                         if (!empty($this->subject)) {
4867                                 $this->_out('/Subject '.$this->_textstring($this->subject));
4868                         }
4869                         if (!empty($this->keywords)) {
4870                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
4871                         }
4872                         if (!empty($this->creator)) {
4873                                 $this->_out('/Creator '.$this->_textstring($this->creator));
4874                         }
4875                         if (defined('PDF_PRODUCER')) {
4876                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
4877                         }
4878                         $this->_out('/CreationDate '.$this->_datestring('D:'.date('YmdHis')));
4879                         $this->_out('/ModDate '.$this->_datestring('D:'.date('YmdHis')));
4880                 }
4881
4882                 /**
4883                 * Format a date string for meta information
4884                 * @param string $s date string to escape.
4885                 * @return string escaped string.
4886                 * @access protected
4887                 */
4888                 function _datestring($s) {
4889                         if ($this->encrypted) {
4890                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4891                         }
4892                         return '('. $this->_escape($s).')';
4893                 }
4894
4895                 /**
4896                 * Output Catalog.
4897                 * @access protected
4898                 */
4899                 function _putcatalog() {
4900                         $this->_out('/Type /Catalog');
4901                         $this->_out('/Pages 1 0 R');
4902
4903                         if ($this->ZoomMode == 'fullpage') {
4904                                 $this->_out('/OpenAction [3 0 R /Fit]');
4905                         } elseif ($this->ZoomMode == 'fullwidth') {
4906                                 $this->_out('/OpenAction [3 0 R /FitH null]');
4907                         } elseif ($this->ZoomMode == 'real') {
4908                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
4909                         } elseif (!is_string($this->ZoomMode)) {
4910                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
4911                         }
4912                         if (isset($this->LayoutMode) AND (!empty($this->LayoutMode))) {
4913                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
4914                         }
4915                         if (isset($this->PageMode) AND (!empty($this->PageMode))) {
4916                                 $this->_out('/PageMode /'.$this->PageMode);
4917                         }
4918                         if (isset($this->l['a_meta_language'])) {
4919                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
4920                         }
4921                         if (!empty($this->javascript)) {
4922                                 $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
4923                         }
4924                         if (count($this->outlines) > 0) {
4925                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
4926                                 $this->_out('/PageMode /UseOutlines');
4927                         }
4928                         $this->_putviewerpreferences();
4929                         $p = $this->n_ocg_print.' 0 R';
4930                         $v = $this->n_ocg_view.' 0 R';
4931                         $as = "<</Event /Print /OCGs [".$p." ".$v."] /Category [/Print]>> <</Event /View /OCGs [".$p." ".$v."] /Category [/View]>>";
4932                         $this->_out("/OCProperties <</OCGs [".$p." ".$v."] /D <</ON [".$p."] /OFF [".$v."] /AS [".$as."]>>>>");
4933                         $this->_putuserrights();
4934                 }
4935
4936                 /**
4937                 * Output viewer preferences.
4938                 * @author Nicola asuni
4939                 * @since 3.1.000 (2008-06-09)
4940                 * @access protected
4941                 */
4942                 function _putviewerpreferences() {
4943                         $this->_out('/ViewerPreferences<<');
4944                         if ($this->rtl) {
4945                                 $this->_out('/Direction /R2L');
4946                         } else {
4947                                 $this->_out('/Direction /L2R');
4948                         }
4949                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
4950                                 $this->_out('/HideToolbar true');
4951                         }
4952                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
4953                                 $this->_out('/HideMenubar true');
4954                         }
4955                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
4956                                 $this->_out('/HideWindowUI true');
4957                         }
4958                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
4959                                 $this->_out('/FitWindow true');
4960                         }
4961                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
4962                                 $this->_out('/CenterWindow true');
4963                         }
4964                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
4965                                 $this->_out('/DisplayDocTitle true');
4966                         }
4967                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
4968                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
4969                         }
4970                         if (isset($this->viewer_preferences['ViewArea'])) {
4971                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
4972                         }
4973                         if (isset($this->viewer_preferences['ViewClip'])) {
4974                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
4975                         }
4976                         if (isset($this->viewer_preferences['PrintArea'])) {
4977                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
4978                         }
4979                         if (isset($this->viewer_preferences['PrintClip'])) {
4980                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
4981                         }
4982                         if (isset($this->viewer_preferences['PrintScaling'])) {
4983                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
4984                         }
4985                         if (isset($this->viewer_preferences['Duplex']) AND (!empty($this->viewer_preferences['Duplex']))) {
4986                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
4987                         }
4988                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
4989                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
4990                                         $this->_out('/PickTrayByPDFSize true');
4991                                 } else {
4992                                         $this->_out('/PickTrayByPDFSize false');
4993                                 }
4994                         }
4995                         if (isset($this->viewer_preferences['PrintPageRange'])) {
4996                                 $PrintPageRangeNum = "";
4997                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
4998                                         $PrintPageRangeNum .= " ".($v-1)."";
4999                                 }
5000                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
5001                         }
5002                         if (isset($this->viewer_preferences['NumCopies'])) {
5003                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
5004                         }
5005                         $this->_out('>>');
5006                 }
5007
5008                 /**
5009                 * Output trailer.
5010                 * @access protected
5011                 */
5012                 function _puttrailer() {
5013                         $this->_out('/Size '.($this->n + 1));
5014                         $this->_out('/Root '.$this->n.' 0 R');
5015                         $this->_out('/Info '.($this->n - 1).' 0 R');
5016                         if ($this->encrypted) {
5017                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
5018                                 $this->_out('/ID [()()]');
5019                         }
5020                 }
5021
5022                 /**
5023                 * Output PDF header.
5024                 * @access protected
5025                 */
5026                 function _putheader() {
5027                         $this->buffer = '%PDF-'.$this->PDFVersion."\n".$this->buffer;
5028 //                      $this->_out('%PDF-'.$this->PDFVersion);
5029                 }
5030
5031                 /**
5032                 * Output end of document (EOF).
5033                 * @access protected
5034                 */
5035                 function _enddoc() {
5036                         $this->_putheader();
5037                         $this->_putpages();
5038                         $this->_putresources();
5039                         //Info
5040                         $this->_newobj();
5041                         $this->_out('<<');
5042                         $this->_putinfo();
5043                         $this->_out('>>');
5044                         $this->_out('endobj');
5045                         //Catalog
5046                         $this->_newobj();
5047                         $this->_out('<<');
5048                         $this->_putcatalog();
5049                         $this->_out('>>');
5050                         $this->_out('endobj');
5051                         //Cross-ref
5052                         $o = strlen($this->buffer);
5053                         $this->_out('xref');
5054                         $this->_out('0 '.($this->n + 1));
5055                         $this->_out('0000000000 65535 f ');
5056                         for($i=1; $i <= $this->n; $i++) {
5057                                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
5058                         }
5059                         //Trailer
5060                         $this->_out('trailer');
5061                         $this->_out('<<');
5062                         $this->_puttrailer();
5063                         $this->_out('>>');
5064                         $this->_out('startxref');
5065                         $this->_out($o);
5066                         $this->_out('%%EOF');
5067                         $this->state = 3;
5068                 }
5069
5070                 /**
5071                 * Initialize a new page.
5072                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
5073                 * @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>
5074                 * @access protected
5075                 */
5076                 function _beginpage($orientation='', $format='') {
5077                         $this->page++;
5078                         $this->pages[$this->page] = ""; // this mark should be removed before output
5079                         $this->state = 2;
5080                         if (empty($orientation)) {
5081                                 if (isset($this->CurOrientation)) {
5082                                         $orientation = $this->CurOrientation;
5083                                 } else {
5084                                         $orientation = 'P';
5085                                 }
5086                         }
5087                         if (!empty($format)) {
5088                                 $this->setPageFormat($format, $orientation);
5089                         } else {
5090                                 $this->setPageOrientation($orientation);
5091                         }
5092                         if ($this->rtl) {
5093                                 $this->x = $this->w - $this->rMargin;
5094                         } else {
5095                                 $this->x = $this->lMargin;
5096                         }
5097                         $this->y = $this->tMargin;
5098                         if ($this->newpagegroup){
5099                                 // start a new group
5100                                 $n = sizeof($this->pagegroups) + 1;
5101                                 $alias = "{nb".$n."}";
5102                                 $this->pagegroups[$alias] = 1;
5103                                 $this->currpagegroup = $alias;
5104                                 $this->newpagegroup = false;
5105                         } elseif ($this->currpagegroup) {
5106                                 $this->pagegroups[$this->currpagegroup]++;
5107                         }
5108                 }
5109
5110                 /**
5111                 * Mark end of page.
5112                 * @access protected
5113                 */
5114                 function _endpage() {
5115                         $this->setVisibility("all");
5116                         $this->state = 1;
5117                 }
5118
5119                 /**
5120                 * Begin a new object.
5121                 * @access protected
5122                 */
5123                 function _newobj() {
5124                         $this->n++;
5125                         $this->offsets[$this->n] = strlen($this->buffer);
5126                         $this->_out($this->n.' 0 obj');
5127                 }
5128
5129                 /**
5130                 * Underline text.
5131                 * @param int $x X coordinate
5132                 * @param int $y Y coordinate
5133                 * @param string $txt text to underline
5134                 * @access protected
5135                 */
5136                 function _dounderline($x, $y, $txt) {
5137                         $up = $this->CurrentFont['up'];
5138                         $ut = $this->CurrentFont['ut'];
5139                         $w = $this->GetStringWidth($txt);
5140                         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);
5141                 }
5142
5143                 /**
5144                 * Line through text.
5145                 * @param int $x X coordinate
5146                 * @param int $y Y coordinate
5147                 * @param string $txt text to underline
5148                 * @access protected
5149                 */
5150                 function _dolinethrough($x, $y, $txt) {
5151                         $up = $this->CurrentFont['up'];
5152                         $ut = $this->CurrentFont['ut'];
5153                         $w = $this->GetStringWidth($txt);
5154                         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);
5155                 }
5156
5157                 /**
5158                 * Read a 4-byte integer from file.
5159                 * @param string $f file name.
5160                 * @return 4-byte integer
5161                 * @access protected
5162                 */
5163                 function _freadint($f) {
5164                         $a = unpack('Ni', fread($f,4));
5165                         return $a['i'];
5166                 }
5167
5168                 /**
5169                 * Format a text string for meta information
5170                 * @param string $s string to escape.
5171                 * @return string escaped string.
5172                 * @access protected
5173                 */
5174                 function _textstring($s) {
5175                         if ($this->isunicode) {
5176                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5177                                         $s = $this->UTF8ToLatin1($s);
5178                                 } else {
5179                                         //Convert string to UTF-16BE
5180                                         $s = $this->UTF8ToUTF16BE($s, true);
5181                                 }
5182                         }
5183                         if ($this->encrypted) {
5184                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5185                         }
5186                         return '('. $this->_escape($s).')';
5187                 }
5188
5189                 /**
5190                 * Format an URI string
5191                 * @param string $s string to escape.
5192                 * @return string escaped string.
5193                 * @access protected
5194                 */
5195                 function _uristring($s) {
5196                         if ($this->encrypted) {
5197                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5198                         }
5199                         return '('.$this->_escape($s).')';
5200                 }
5201
5202                 /**
5203                 * Format a text string
5204                 * @param string $s string to escape.
5205                 * @return string escaped string.
5206                 * @access protected
5207                 */
5208                 function _escapetext($s) {
5209                         if ($this->isunicode) {
5210                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5211                                         $s = $this->UTF8ToLatin1($s);
5212                                 } else {
5213                                         //Convert string to UTF-16BE and reverse RTL language
5214                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
5215                                 }
5216                         }
5217                         return $this->_escape($s);
5218                 }
5219
5220                 /**
5221                 * Add "\" before "\", "(" and ")"
5222                 * @param string $s string to escape.
5223                 * @return string escaped string.
5224                 * @access protected
5225                 */
5226                 function _escape($s) {
5227                         // the chr(13) substitution fixes the Bugs item #1421290.
5228                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
5229                 }
5230
5231                 /**
5232                 * Output a stream.
5233                 * @param string $s string to output.
5234                 * @access protected
5235                 */
5236                 function _putstream($s) {
5237                         if ($this->encrypted) {
5238                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5239                         }
5240                         $this->_out('stream');
5241                         $this->_out($s);
5242                         $this->_out('endstream');
5243                 }
5244
5245                 /**
5246                 * Output a string to the document.
5247                 * @param string $s string to output.
5248                 * @access protected
5249                 */
5250                 function _out($s) {
5251                         if ($this->state == 2) {
5252                                 if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
5253                                         // puts data before page footer
5254                                         $page = substr($this->pages[$this->page], 0, -$this->footerlen[$this->page]);
5255                                         $footer = substr($this->pages[$this->page], -$this->footerlen[$this->page]);
5256                                         $this->pages[$this->page] = $page." ".$s."\n".$footer;
5257                                 } else {
5258                                         $this->pages[$this->page] .= $s."\n";
5259                                 }
5260                         } else {
5261                                 $this->buffer .= $s."\n";
5262                         }
5263                 }
5264
5265                 /**
5266                 * Adds unicode fonts.<br>
5267                 * Based on PDF Reference 1.3 (section 5)
5268                 * @access protected
5269                 * @author Nicola Asuni
5270                 * @since 1.52.0.TC005 (2005-01-05)
5271                 */
5272                 function _puttruetypeunicode($font) {
5273                         // Type0 Font
5274                         // A composite font composed of other fonts, organized hierarchically
5275                         $this->_newobj();
5276                         $this->_out('<</Type /Font');
5277                         $this->_out('/Subtype /Type0');
5278                         $this->_out('/BaseFont /'.$font['name'].'');
5279                         $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.
5280                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5281                         $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
5282                         $this->_out('>>');
5283                         $this->_out('endobj');
5284                         // CIDFontType2
5285                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5286                         $this->_newobj();
5287                         $this->_out('<</Type /Font');
5288                         $this->_out('/Subtype /CIDFontType2');
5289                         $this->_out('/BaseFont /'.$font['name'].'');
5290                         $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
5291                         $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
5292                         if (isset($font['desc']['MissingWidth'])){
5293                                 $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth
5294                         }
5295                         $w = "";
5296                         foreach ($font['cw'] as $cid => $width) {
5297                                 $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID
5298                         }
5299                         $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont
5300                         $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
5301                         $this->_out('>>');
5302                         $this->_out('endobj');
5303                         // ToUnicode
5304                         // is a stream object that contains the definition of the CMap
5305                         // (PDF Reference 1.3 chap. 5.9)
5306                         $this->_newobj();
5307                         $this->_out('<</Length 345>>');
5308                         $this->_out('stream');
5309                         $this->_out('/CIDInit /ProcSet findresource begin');
5310                         $this->_out('12 dict begin');
5311                         $this->_out('begincmap');
5312                         $this->_out('/CIDSystemInfo');
5313                         $this->_out('<</Registry (Adobe)');
5314                         $this->_out('/Ordering (UCS)');
5315                         $this->_out('/Supplement 0');
5316                         $this->_out('>> def');
5317                         $this->_out('/CMapName /Adobe-Identity-UCS def');
5318                         $this->_out('/CMapType 2 def');
5319                         $this->_out('1 begincodespacerange');
5320                         $this->_out('<0000> <FFFF>');
5321                         $this->_out('endcodespacerange');
5322                         $this->_out('1 beginbfrange');
5323                         $this->_out('<0000> <FFFF> <0000>');
5324                         $this->_out('endbfrange');
5325                         $this->_out('endcmap');
5326                         $this->_out('CMapName currentdict /CMap defineresource pop');
5327                         $this->_out('end');
5328                         $this->_out('end');
5329                         $this->_out('endstream');
5330                         $this->_out('endobj');
5331                         // CIDSystemInfo dictionary
5332                         // A dictionary containing entries that define the character collection of the CIDFont.
5333                         $this->_newobj();
5334                         $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections
5335                         $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry
5336                         $this->_out('/Supplement 0'); // The supplement number of the character collection.
5337                         $this->_out('>>');
5338                         $this->_out('endobj');
5339                         // Font descriptor
5340                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
5341                         $this->_newobj();
5342                         $this->_out('<</Type /FontDescriptor');
5343                         $this->_out('/FontName /'.$font['name']);
5344                         foreach ($font['desc'] as $key => $value) {
5345                                 $this->_out('/'.$key.' '.$value);
5346                         }
5347                         if ($font['file']) {
5348                                 // A stream containing a TrueType font program
5349                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
5350                         }
5351                         $this->_out('>>');
5352                         $this->_out('endobj');
5353                         // Embed CIDToGIDMap
5354                         // A specification of the mapping from CIDs to glyph indices
5355                         $this->_newobj();
5356                         $ctgfile = $this->_getfontpath().strtolower($font['ctg']);
5357                         if (!file_exists($ctgfile)) {
5358                                 $this->Error('Font file not found: '.$ctgfile);
5359                         }
5360                         $size = filesize($ctgfile);
5361                         $this->_out('<</Length '.$size.'');
5362                         if (substr($ctgfile, -2) == '.z') { // check file extension
5363                                 /* Decompresses data encoded using the public-domain
5364                                 zlib/deflate compression method, reproducing the
5365                                 original text or binary data */
5366                                 $this->_out('/Filter /FlateDecode');
5367                         }
5368                         $this->_out('>>');
5369                         $this->_putstream(file_get_contents($ctgfile));
5370                         $this->_out('endobj');
5371                 }
5372
5373                  /**
5374                  * Converts UTF-8 strings to codepoints array.<br>
5375                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
5376                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
5377                  * <pre>
5378                  *        Char. number range  |        UTF-8 octet sequence
5379                  *       (hexadecimal)    |              (binary)
5380                  *    --------------------+-----------------------------------------------
5381                  *    0000 0000-0000 007F | 0xxxxxxx
5382                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
5383                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
5384                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5385                  *    ---------------------------------------------------------------------
5386                  *
5387                  *   ABFN notation:
5388                  *   ---------------------------------------------------------------------
5389                  *   UTF8-octets = *( UTF8-char )
5390                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
5391                  *   UTF8-1      = %x00-7F
5392                  *   UTF8-2      = %xC2-DF UTF8-tail
5393                  *
5394                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
5395                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
5396                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
5397                  *                 %xF4 %x80-8F 2( UTF8-tail )
5398                  *   UTF8-tail   = %x80-BF
5399                  *   ---------------------------------------------------------------------
5400                  * </pre>
5401                  * @param string $str string to process.
5402                  * @return array containing codepoints (UTF-8 characters values)
5403                  * @access protected
5404                  * @author Nicola Asuni
5405                  * @since 1.53.0.TC005 (2005-01-05)
5406                  */
5407                 function UTF8StringToArray($str) {
5408                         if (!$this->isunicode) {
5409                                 // split string into array of equivalent codes
5410                                 $strarr = array();
5411                                 $strlen = strlen($str);
5412                                 for($i=0; $i < $strlen; $i++) {
5413                                         $strarr[] = ord($str[$i]);
5414                                 }
5415                                 return $strarr;
5416                         }
5417                         $unicode = array(); // array containing unicode values
5418                         $bytes  = array(); // array containing single character byte sequences
5419                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
5420                         $str .= ""; // force $str to be a string
5421                         $length = strlen($str);
5422                         for($i = 0; $i < $length; $i++) {
5423                                 $char = ord($str[$i]); // get one string character at time
5424                                 if (count($bytes) == 0) { // get starting octect
5425                                         if ($char <= 0x7F) {
5426                                                 $unicode[] = $char; // use the character "as is" because is ASCII
5427                                                 $numbytes = 1;
5428                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
5429                                                 $bytes[] = ($char - 0xC0) << 0x06;
5430                                                 $numbytes = 2;
5431                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
5432                                                 $bytes[] = ($char - 0xE0) << 0x0C;
5433                                                 $numbytes = 3;
5434                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
5435                                                 $bytes[] = ($char - 0xF0) << 0x12;
5436                                                 $numbytes = 4;
5437                                         } else {
5438                                                 // use replacement character for other invalid sequences
5439                                                 $unicode[] = 0xFFFD;
5440                                                 $bytes = array();
5441                                                 $numbytes = 1;
5442                                         }
5443                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
5444                                         $bytes[] = $char - 0x80;
5445                                         if (count($bytes) == $numbytes) {
5446                                                 // compose UTF-8 bytes to a single unicode value
5447                                                 $char = $bytes[0];
5448                                                 for($j = 1; $j < $numbytes; $j++) {
5449                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
5450                                                 }
5451                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
5452                                                         /* The definition of UTF-8 prohibits encoding character numbers between
5453                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
5454                                                         encoding form (as surrogate pairs) and do not directly represent
5455                                                         characters. */
5456                                                         $unicode[] = 0xFFFD; // use replacement character
5457                                                 } else {
5458                                                         $unicode[] = $char; // add char to array
5459                                                 }
5460                                                 // reset data for next char
5461                                                 $bytes = array();
5462                                                 $numbytes = 1;
5463                                         }
5464                                 } else {
5465                                         // use replacement character for other invalid sequences
5466                                         $unicode[] = 0xFFFD;
5467                                         $bytes = array();
5468                                         $numbytes = 1;
5469                                 }
5470                         }
5471                         return $unicode;
5472                 }
5473
5474                 /**
5475                  * Converts UTF-8 strings to UTF16-BE.<br>
5476                  * @param string $str string to process.
5477                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5478                  * @return string
5479                  * @access protected
5480                  * @author Nicola Asuni
5481                  * @since 1.53.0.TC005 (2005-01-05)
5482                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
5483                  */
5484                 function UTF8ToUTF16BE($str, $setbom=true) {
5485                         if (!$this->isunicode) {
5486                                 return $str; // string is not in unicode
5487                         }
5488                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5489                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
5490                 }
5491
5492                 /**
5493                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
5494                  * @param string $str string to process.
5495                  * @return string
5496                  * @author Andrew Whitehead, Nicola Asuni
5497                  * @access protected
5498                  * @since 3.2.000 (2008-06-23)
5499                  */
5500                 function UTF8ToLatin1($str) {
5501                         if (!$this->isunicode) {
5502                                 return $str; // string is not in unicode
5503                         }
5504                         $outstr = ""; // string to be returned
5505                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5506                         foreach ($unicode as $char) {
5507                                 if ($char == 0xFFFD) {
5508                                         // skip
5509                                 } elseif ($char == 0x2022) {
5510                                         // fix for middot
5511                                         $outstr .= chr(183);
5512                                 } elseif ($char < 256) {
5513                                         $outstr .= chr($char);
5514                                 } else {
5515                                         $outstr .= '?';
5516                                 }
5517                         }
5518                         return $outstr;
5519                 }
5520
5521                 /**
5522                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
5523                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
5524                  * <pre>
5525                  *   Encoding UTF-16:
5526                  *
5527                  *   Encoding of a single character from an ISO 10646 character value to
5528                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
5529                  *    than 0x10FFFF.
5530                  *
5531                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
5532                  *       terminate.
5533                  *
5534                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
5535                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
5536                  *       represented in 20 bits.
5537                  *
5538                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
5539                  *       0xDC00, respectively. These integers each have 10 bits free to
5540                  *       encode the character value, for a total of 20 bits.
5541                  *
5542                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
5543                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
5544                  *       bits of W2. Terminate.
5545                  *
5546                  *    Graphically, steps 2 through 4 look like:
5547                  *    U' = yyyyyyyyyyxxxxxxxxxx
5548                  *    W1 = 110110yyyyyyyyyy
5549                  *    W2 = 110111xxxxxxxxxx
5550                  * </pre>
5551                  * @param array $unicode array containing UTF-8 unicode values
5552                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5553                  * @return string
5554                  * @access protected
5555                  * @author Nicola Asuni
5556                  * @since 2.1.000 (2008-01-08)
5557                  * @see UTF8ToUTF16BE()
5558                  */
5559                 function arrUTF8ToUTF16BE($unicode, $setbom=true) {
5560                         $outstr = ""; // string to be returned
5561                         if ($setbom) {
5562                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
5563                         }
5564                         foreach($unicode as $char) {
5565                                 if ($char == 0xFFFD) {
5566                                         $outstr .= "\xFF\xFD"; // replacement character
5567                                 } elseif ($char < 0x10000) {
5568                                         $outstr .= chr($char >> 0x08);
5569                                         $outstr .= chr($char & 0xFF);
5570                                 } else {
5571                                         $char -= 0x10000;
5572                                         $w1 = 0xD800 | ($char >> 0x10);
5573                                         $w2 = 0xDC00 | ($char & 0x3FF);
5574                                         $outstr .= chr($w1 >> 0x08);
5575                                         $outstr .= chr($w1 & 0xFF);
5576                                         $outstr .= chr($w2 >> 0x08);
5577                                         $outstr .= chr($w2 & 0xFF);
5578                                 }
5579                         }
5580                         return $outstr;
5581                 }
5582                 // ====================================================
5583
5584                 /**
5585                  * Set header font.
5586                  * @param array $font font
5587                  * @since 1.1
5588                  */
5589                 function setHeaderFont($font) {
5590                         $this->header_font = $font;
5591                 }
5592
5593                 /**
5594                  * Get header font.
5595                  * @return array()
5596                  * @since 4.0.012 (2008-07-24)
5597                  */
5598                 function getHeaderFont() {
5599                         return $this->header_font;
5600                 }
5601
5602                 /**
5603                  * Set footer font.
5604                  * @param array $font font
5605                  * @since 1.1
5606                  */
5607                 function setFooterFont($font) {
5608                         $this->footer_font = $font;
5609                 }
5610
5611                 /**
5612                  * Get Footer font.
5613                  * @return array()
5614                  * @since 4.0.012 (2008-07-24)
5615                  */
5616                 function getFooterFont() {
5617                         return $this->footer_font;
5618                 }
5619
5620                 /**
5621                  * Set language array.
5622                  * @param array $language
5623                  * @since 1.1
5624                  */
5625                 function setLanguageArray($language) {
5626                         $this->l = $language;
5627                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
5628                 }
5629
5630                 /**
5631                  * Returns the PDF data.
5632                  */
5633                 function getPDFData() {
5634                         if ($this->state < 3) {
5635                                 $this->Close();
5636                         }
5637                         return $this->buffer;
5638                 }
5639
5640                 /**
5641                  * Sets font style.
5642                  * @param string $tag tag name in lowercase. Supported tags are:<ul>
5643                  * <li>b : bold text</li>
5644                  * <li>i : italic</li>
5645                  * <li>u : underlined</li>
5646                  * <li>d : line-through</li></ul>
5647                  * @param boolean $enable
5648                  * @access protected
5649                  */
5650                 function setStyle($tag, $enable) {
5651                         $this->$tag += ($enable ? 1 : -1);
5652                         $style = '';
5653                         foreach(array('b', 'i', 'u', 'd') as $s) {
5654                                 if ($this->$s > 0) {
5655                                         $style .= $s;
5656                                 }
5657                         }
5658                         $this->SetFont('', $style);
5659                 }
5660
5661                 /**
5662                  * Output anchor link.
5663                  * @param string $url link URL
5664                  * @param string $name link name
5665                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
5666                  * @param boolean $firstline if true prints only the first line and return the remaining string.
5667                  * @return the number of cells used or the remaining text if $firstline = true;
5668                  * @access public
5669                  */
5670                 function addHtmlLink($url, $name, $fill=0, $firstline=false) {
5671                         $prevcolor = $this->fgcolor;
5672                         $this->SetTextColor(0, 0, 255);
5673                         $this->setStyle('u', true);
5674                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
5675                         $this->setStyle('u', false);
5676                         $this->SetTextColorArray($prevcolor);
5677                         return $ret;
5678                 }
5679
5680                 /**
5681                  * 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).
5682                  * @param string $color html color
5683                  * @return array
5684                  * @access protected
5685                  */
5686                 function convertHTMLColorToDec($color="#000000") {
5687                         global $webcolor;
5688                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
5689                         // set default color to be returned in case of error
5690                         $returncolor = array ('R' => 0, 'G' => 0, 'B' => 0);
5691                         if (empty($color)) {
5692                                 return $returncolor;
5693                         }
5694                         if (substr(strtolower($color), 0, 3) == 'rgb') {
5695                                 $codes = substr($color, 4);
5696                                 $codes = str_replace(')', '', $codes);
5697                                 $returncolor = explode(',', $codes, 3);
5698                                 return $returncolor;    
5699                         }
5700                         if (substr($color, 0, 1) != "#") {
5701                                 // decode color name
5702                                 if (isset($webcolor[strtolower($color)])) {
5703                                         $color_code = $webcolor[strtolower($color)];
5704                                 } else {
5705                                         return $returncolor;
5706                                 }
5707                         } else {
5708                                 $color_code = substr($color, 1);
5709                         }
5710                         switch (strlen($color_code)) {
5711                                 case 3: {
5712                                         // three-digit hexadecimal representation
5713                                         $r = substr($color_code, 0, 1);
5714                                         $g = substr($color_code, 1, 1);
5715                                         $b = substr($color_code, 2, 1);
5716                                         $returncolor['R'] = hexdec($r.$r);
5717                                         $returncolor['G'] = hexdec($g.$g);
5718                                         $returncolor['B'] = hexdec($b.$b);
5719                                         break;
5720                                 }
5721                                 case 6: {
5722                                         // six-digit hexadecimal representation
5723                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
5724                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
5725                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
5726                                         break;
5727                                 }
5728                         }
5729                         return $returncolor;
5730                 }
5731
5732                 /**
5733                  * Converts pixels to Units.
5734                  * @param int $px pixels
5735                  * @return float millimeters
5736                  * @access public
5737                  */
5738                 function pixelsToUnits($px){
5739                         return $px / $this->k;
5740                 }
5741
5742                 /**
5743                  * Reverse function for htmlentities.
5744                  * Convert entities in UTF-8.
5745                  * @param $text_to_convert Text to convert.
5746                  * @return string converted
5747                  */
5748                 function unhtmlentities($text_to_convert) {
5749                         return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
5750                 }
5751
5752                 // ENCRYPTION METHODS ----------------------------------
5753                 // SINCE 2.0.000 (2008-01-02)
5754                 /**
5755                 * Compute encryption key depending on object number where the encrypted data is stored
5756                 * @param int $n object number
5757                 * @since 2.0.000 (2008-01-02)
5758                 */
5759                 function _objectkey($n) {
5760                         return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10);
5761                 }
5762
5763                 /**
5764                  * Put encryption on PDF document.
5765                  * @since 2.0.000 (2008-01-02)
5766                  */
5767                 function _putencryption() {
5768                         $this->_out('/Filter /Standard');
5769                         $this->_out('/V 1');
5770                         $this->_out('/R 2');
5771                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
5772                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
5773                         $this->_out('/P '.$this->Pvalue);
5774                 }
5775
5776                 /**
5777                 * Returns the input text exrypted using RC4 algorithm and the specified key.
5778                 * RC4 is the standard encryption algorithm used in PDF format
5779                 * @param string $key encryption key
5780                 * @param String $text input text to be encrypted
5781                 * @return String encrypted text
5782                 * @since 2.0.000 (2008-01-02)
5783                 * @author Klemen Vodopivec
5784                 */
5785                 function _RC4($key, $text) {
5786                         if ($this->last_rc4_key != $key) {
5787                                 $k = str_repeat($key, 256/strlen($key)+1);
5788                                 $rc4 = range(0,255);
5789                                 $j = 0;
5790                                 for ($i=0; $i < 256; $i++) {
5791                                         $t = $rc4[$i];
5792                                         $j = ($j + $t + ord($k[$i])) % 256;
5793                                         $rc4[$i] = $rc4[$j];
5794                                         $rc4[$j] = $t;
5795                                 }
5796                                 $this->last_rc4_key = $key;
5797                                 $this->last_rc4_key_c = $rc4;
5798                         } else {
5799                                 $rc4 = $this->last_rc4_key_c;
5800                         }
5801                         $len = strlen($text);
5802                         $a = 0;
5803                         $b = 0;
5804                         $out = '';
5805                         for ($i=0; $i < $len; $i++) {
5806                                 $a = ($a + 1) % 256;
5807                                 $t = $rc4[$a];
5808                                 $b = ($b + $t) % 256;
5809                                 $rc4[$a] = $rc4[$b];
5810                                 $rc4[$b] = $t;
5811                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
5812                                 $out .= chr(ord($text[$i]) ^ $k);
5813                         }
5814                         return $out;
5815                 }
5816
5817                 /**
5818                 * Encrypts a string using MD5 and returns it's value as a binary string.
5819                 * @param string $str input string
5820                 * @return String MD5 encrypted binary string
5821                 * @since 2.0.000 (2008-01-02)
5822                 * @author Klemen Vodopivec
5823                 */
5824                 function _md5_16($str) {
5825                         return pack('H*',md5($str));
5826                 }
5827
5828                 /**
5829                 * Compute O value (used for RC4 encryption)
5830                 * @param String $user_pass user password
5831                 * @param String $owner_pass user password
5832                 * @return String O value
5833                 * @since 2.0.000 (2008-01-02)
5834                 * @author Klemen Vodopivec
5835                 */
5836                 function _Ovalue($user_pass, $owner_pass) {
5837                         $tmp = $this->_md5_16($owner_pass);
5838                         $owner_RC4_key = substr($tmp,0,5);
5839                         return $this->_RC4($owner_RC4_key, $user_pass);
5840                 }
5841
5842                 /**
5843                 * Compute U value (used for RC4 encryption)
5844                 * @return String U value
5845                 * @since 2.0.000 (2008-01-02)
5846                 * @author Klemen Vodopivec
5847                 */
5848                 function _Uvalue() {
5849                         return $this->_RC4($this->encryption_key, $this->padding);
5850                 }
5851
5852                 /**
5853                 * Compute encryption key
5854                 * @param String $user_pass user password
5855                 * @param String $owner_pass user password
5856                 * @param String $protection protection type
5857                 * @since 2.0.000 (2008-01-02)
5858                 * @author Klemen Vodopivec
5859                 */
5860                 function _generateencryptionkey($user_pass, $owner_pass, $protection) {
5861                         // Pad passwords
5862                         $user_pass = substr($user_pass.$this->padding,0,32);
5863                         $owner_pass = substr($owner_pass.$this->padding,0,32);
5864                         // Compute O value
5865                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
5866                         // Compute encyption key
5867                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
5868                         $this->encryption_key = substr($tmp,0,5);
5869                         // Compute U value
5870                         $this->Uvalue = $this->_Uvalue();
5871                         // Compute P value
5872                         $this->Pvalue = -(($protection^255)+1);
5873                 }
5874
5875                 /**
5876                 * Set document protection
5877                 * The permission array is composed of values taken from the following ones:
5878                 * - copy: copy text and images to the clipboard
5879                 * - print: print the document
5880                 * - modify: modify it (except for annotations and forms)
5881                 * - annot-forms: add annotations and forms
5882                 * Remark: the protection against modification is for people who have the full Acrobat product.
5883                 * 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.
5884                 * 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.
5885                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
5886                 * @param String $user_pass user password. Empty by default.
5887                 * @param String $owner_pass owner password. If not specified, a random value is used.
5888                 * @since 2.0.000 (2008-01-02)
5889                 * @author Klemen Vodopivec
5890                 */
5891                 function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
5892                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
5893                         $protection = 192;
5894                         foreach($permissions as $permission) {
5895                                 if (!isset($options[$permission])) {
5896                                         $this->Error('Incorrect permission: '.$permission);
5897                                 }
5898                                 $protection += $options[$permission];
5899                         }
5900                         if ($owner_pass === null) {
5901                                 $owner_pass = uniqid(rand());
5902                         }
5903                         $this->encrypted = true;
5904                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
5905                 }
5906
5907                 // END OF ENCRYPTION FUNCTIONS -------------------------
5908
5909                 // START TRANSFORMATIONS SECTION -----------------------
5910                 // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni
5911
5912                 /**
5913                 * Starts a 2D tranformation saving current graphic state.
5914                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
5915                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5916                 * @since 2.1.000 (2008-01-07)
5917                 * @see StartTransform(), StopTransform()
5918                 */
5919                 function StartTransform() {
5920                         $this->_out('q');
5921                 }
5922
5923                 /**
5924                 * Stops a 2D tranformation restoring previous graphic state.
5925                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
5926                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5927                 * @since 2.1.000 (2008-01-07)
5928                 * @see StartTransform(), StopTransform()
5929                 */
5930                 function StopTransform() {
5931                         $this->_out('Q');
5932                 }
5933                 /**
5934                 * Horizontal Scaling.
5935                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5936                 * @param int $x abscissa of the scaling center. Default is current x position
5937                 * @param int $y ordinate of the scaling center. Default is current y position
5938                 * @since 2.1.000 (2008-01-07)
5939                 * @see StartTransform(), StopTransform()
5940                 */
5941                 function ScaleX($s_x, $x='', $y=''){
5942                         $this->Scale($s_x, 100, $x, $y);
5943                 }
5944
5945                 /**
5946                 * Vertical Scaling.
5947                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5948                 * @param int $x abscissa of the scaling center. Default is current x position
5949                 * @param int $y ordinate of the scaling center. Default is current y position
5950                 * @since 2.1.000 (2008-01-07)
5951                 * @see StartTransform(), StopTransform()
5952                 */
5953                 function ScaleY($s_y, $x='', $y=''){
5954                         $this->Scale(100, $s_y, $x, $y);
5955                 }
5956
5957                 /**
5958                 * Vertical and horizontal proportional Scaling.
5959                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
5960                 * @param int $x abscissa of the scaling center. Default is current x position
5961                 * @param int $y ordinate of the scaling center. Default is current y position
5962                 * @since 2.1.000 (2008-01-07)
5963                 * @see StartTransform(), StopTransform()
5964                 */
5965                 function ScaleXY($s, $x='', $y=''){
5966                         $this->Scale($s, $s, $x, $y);
5967                 }
5968
5969                 /**
5970                 * Vertical and horizontal non-proportional Scaling.
5971                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5972                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5973                 * @param int $x abscissa of the scaling center. Default is current x position
5974                 * @param int $y ordinate of the scaling center. Default is current y position
5975                 * @since 2.1.000 (2008-01-07)
5976                 * @see StartTransform(), StopTransform()
5977                 */
5978                 function Scale($s_x, $s_y, $x='', $y=''){
5979                         if ($x === '') {
5980                                 $x=$this->x;
5981                         }
5982                         if ($y === '') {
5983                                 $y=$this->y;
5984                         }
5985                         if ($this->rtl) {
5986                                 $x = $this->w - $x;
5987                         }
5988                         if (($s_x == 0) OR ($s_y == 0)) {
5989                                 $this->Error('Please do not use values equal to zero for scaling');
5990                         }
5991                         $y = ($this->h - $y) * $this->k;
5992                         $x *= $this->k;
5993                         //calculate elements of transformation matrix
5994                         $s_x /= 100;
5995                         $s_y /= 100;
5996                         $tm[0] = $s_x;
5997                         $tm[1] = 0;
5998                         $tm[2] = 0;
5999                         $tm[3] = $s_y;
6000                         $tm[4] = $x * (1 - $s_x);
6001                         $tm[5] = $y * (1 - $s_y);
6002                         //scale the coordinate system
6003                         $this->Transform($tm);
6004                 }
6005
6006                 /**
6007                 * Horizontal Mirroring.
6008                 * @param int $x abscissa of the point. Default is current x position
6009                 * @since 2.1.000 (2008-01-07)
6010                 * @see StartTransform(), StopTransform()
6011                 */
6012                 function MirrorH($x=''){
6013                         $this->Scale(-100, 100, $x);
6014                 }
6015
6016                 /**
6017                 * Verical Mirroring.
6018                 * @param int $y ordinate of the point. Default is current y position
6019                 * @since 2.1.000 (2008-01-07)
6020                 * @see StartTransform(), StopTransform()
6021                 */
6022                 function MirrorV($y=''){
6023                         $this->Scale(100, -100, '', $y);
6024                 }
6025
6026                 /**
6027                 * Point reflection mirroring.
6028                 * @param int $x abscissa of the point. Default is current x position
6029                 * @param int $y ordinate of the point. Default is current y position
6030                 * @since 2.1.000 (2008-01-07)
6031                 * @see StartTransform(), StopTransform()
6032                 */
6033                 function MirrorP($x='',$y=''){
6034                         $this->Scale(-100, -100, $x, $y);
6035                 }
6036
6037                 /**
6038                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
6039                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
6040                 * @param int $x abscissa of the point. Default is current x position
6041                 * @param int $y ordinate of the point. Default is current y position
6042                 * @since 2.1.000 (2008-01-07)
6043                 * @see StartTransform(), StopTransform()
6044                 */
6045                 function MirrorL($angle=0, $x='',$y=''){
6046                         $this->Scale(-100, 100, $x, $y);
6047                         $this->Rotate(-2*($angle-90), $x, $y);
6048                 }
6049
6050                 /**
6051                 * Translate graphic object horizontally.
6052                 * @param int $t_x movement to the right (or left for RTL)
6053                 * @since 2.1.000 (2008-01-07)
6054                 * @see StartTransform(), StopTransform()
6055                 */
6056                 function TranslateX($t_x){
6057                         $this->Translate($t_x, 0);
6058                 }
6059
6060                 /**
6061                 * Translate graphic object vertically.
6062                 * @param int $t_y movement to the bottom
6063                 * @since 2.1.000 (2008-01-07)
6064                 * @see StartTransform(), StopTransform()
6065                 */
6066                 function TranslateY($t_y){
6067                         $this->Translate(0, $t_y);
6068                 }
6069
6070                 /**
6071                 * Translate graphic object horizontally and vertically.
6072                 * @param int $t_x movement to the right
6073                 * @param int $t_y movement to the bottom
6074                 * @since 2.1.000 (2008-01-07)
6075                 * @see StartTransform(), StopTransform()
6076                 */
6077                 function Translate($t_x, $t_y){
6078                         if ($this->rtl) {
6079                                 $t_x = -$t_x;
6080                         }
6081                         //calculate elements of transformation matrix
6082                         $tm[0] = 1;
6083                         $tm[1] = 0;
6084                         $tm[2] = 0;
6085                         $tm[3] = 1;
6086                         $tm[4] = $t_x * $this->k;
6087                         $tm[5] = -$t_y * $this->k;
6088                         //translate the coordinate system
6089                         $this->Transform($tm);
6090                 }
6091
6092                 /**
6093                 * Rotate object.
6094                 * @param float $angle angle in degrees for counter-clockwise rotation
6095                 * @param int $x abscissa of the rotation center. Default is current x position
6096                 * @param int $y ordinate of the rotation center. Default is current y position
6097                 * @since 2.1.000 (2008-01-07)
6098                 * @see StartTransform(), StopTransform()
6099                 */
6100                 function Rotate($angle, $x='', $y=''){
6101                         if ($x === '') {
6102                                 $x=$this->x;
6103                         }
6104                         if ($y === '') {
6105                                 $y=$this->y;
6106                         }
6107                         if ($this->rtl) {
6108                                 $x = $this->w - $x;
6109                                 $angle = -$angle;
6110                         }
6111                         $y = ($this->h - $y) * $this->k;
6112                         $x *= $this->k;
6113                         //calculate elements of transformation matrix
6114                         $tm[0] = cos(deg2rad($angle));
6115                         $tm[1] = sin(deg2rad($angle));
6116                         $tm[2] = -$tm[1];
6117                         $tm[3] = $tm[0];
6118                         $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
6119                         $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
6120                         //rotate the coordinate system around ($x,$y)
6121                         $this->Transform($tm);
6122                 }
6123
6124                 /**
6125                 * Skew horizontally.
6126                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6127                 * @param int $x abscissa of the skewing center. default is current x position
6128                 * @param int $y ordinate of the skewing center. default is current y position
6129                 * @since 2.1.000 (2008-01-07)
6130                 * @see StartTransform(), StopTransform()
6131                 */
6132                 function SkewX($angle_x, $x='', $y=''){
6133                         $this->Skew($angle_x, 0, $x, $y);
6134                 }
6135
6136                 /**
6137                 * Skew vertically.
6138                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6139                 * @param int $x abscissa of the skewing center. default is current x position
6140                 * @param int $y ordinate of the skewing center. default is current y position
6141                 * @since 2.1.000 (2008-01-07)
6142                 * @see StartTransform(), StopTransform()
6143                 */
6144                 function SkewY($angle_y, $x='', $y=''){
6145                         $this->Skew(0, $angle_y, $x, $y);
6146                 }
6147
6148                 /**
6149                 * Skew.
6150                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6151                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6152                 * @param int $x abscissa of the skewing center. default is current x position
6153                 * @param int $y ordinate of the skewing center. default is current y position
6154                 * @since 2.1.000 (2008-01-07)
6155                 * @see StartTransform(), StopTransform()
6156                 */
6157                 function Skew($angle_x, $angle_y, $x='', $y=''){
6158                         if ($x === '') {
6159                                 $x = $this->x;
6160                         }
6161                         if ($y === '') {
6162                                 $y = $this->y;
6163                         }
6164                         if ($this->rtl) {
6165                                 $x = $this->w - $x;
6166                                 $angle_x = -$angle_x;
6167                         }
6168                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
6169                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
6170                         }
6171                         $x *= $this->k;
6172                         $y = ($this->h - $y) * $this->k;
6173                         //calculate elements of transformation matrix
6174                         $tm[0] = 1;
6175                         $tm[1] = tan(deg2rad($angle_y));
6176                         $tm[2] = tan(deg2rad($angle_x));
6177                         $tm[3] = 1;
6178                         $tm[4] = -$tm[2] * $y;
6179                         $tm[5] = -$tm[1] * $x;
6180                         //skew the coordinate system
6181                         $this->Transform($tm);
6182                 }
6183
6184                 /**
6185                 * Apply graphic transformations.
6186                 * @since 2.1.000 (2008-01-07)
6187                 * @see StartTransform(), StopTransform()
6188                 */
6189                 function Transform($tm){
6190                         $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
6191                 }
6192
6193                 // END TRANSFORMATIONS SECTION -------------------------
6194
6195
6196                 // START GRAPHIC FUNCTIONS SECTION ---------------------
6197                 // The following section is based on the code provided by David Hernandez Sanz
6198
6199                 /**
6200                 * 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.
6201                 * @param float $width The width.
6202                 * @since 1.0
6203                 * @see Line(), Rect(), Cell(), MultiCell()
6204                 */
6205                 function SetLineWidth($width) {
6206                         //Set line width
6207                         $this->LineWidth = $width;
6208                         //$this->linestyleWidth = sprintf('%.2f w', ($width * $this->k));
6209                         // FrontAccounting fix
6210                         // My PHP 5.2.6 environment gave an "Unsupported operand types"
6211                         // error for the multiplication on the next line some of the
6212                         // time when this method is called - I debugged and sometimes
6213                         // the $width parameter is some sort of weird array.  I don't
6214                         // understand what's going on, but casting it to a (float) seems
6215                         // to "fix" the problem.  -Jason Maas, 2009/09/25
6216                         $this->linestyleWidth = sprintf('%.2f w', ((float) $width * (float) $this->k));
6217                         $this->_out($this->linestyleWidth);
6218                 }
6219
6220                 /**
6221                 * Returns the current the line width.
6222                 * @return int Line width
6223                 * @since 2.1.000 (2008-01-07)
6224                 * @see Line(), SetLineWidth()
6225                 */
6226                 function GetLineWidth() {
6227                         return $this->LineWidth;
6228                 }
6229
6230                 /**
6231                 * Set line style.
6232                 * @param array $style Line style. Array with keys among the following:
6233                 * <ul>
6234                 *        <li>width (float): Width of the line in user units.</li>
6235                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
6236                 * butt, round, square. The difference between "square" and "butt" is that
6237                 * "square" projects a flat end past the end of the line.</li>
6238                 *        <li>join (string): Type of join. Possible values are: miter, round,
6239                 * bevel.</li>
6240                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
6241                 * series of length values, which are the lengths of the on and off dashes.
6242                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
6243                 * 1 off, 2 on, 1 off, ...</li>
6244                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
6245                 * the point at which the pattern starts.</li>
6246                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
6247                 * </ul>
6248                 * @access public
6249                 * @since 2.1.000 (2008-01-08)
6250                 */
6251                 function SetLineStyle($style) {
6252                         extract($style);
6253                         if (isset($width)) {
6254                                 $width_prev = $this->LineWidth;
6255                                 $this->SetLineWidth($width);
6256                                 $this->LineWidth = $width_prev;
6257                         }
6258                         if (isset($cap)) {
6259                                 $ca = array("butt" => 0, "round"=> 1, "square" => 2);
6260                                 if (isset($ca[$cap])) {
6261                                         $this->linestyleCap = $ca[$cap]." J";
6262                                         $this->_out($this->linestyleCap);
6263                                 }
6264                         }
6265                         if (isset($join)) {
6266                                 $ja = array("miter" => 0, "round" => 1, "bevel" => 2);
6267                                 if (isset($ja[$join])) {
6268                                         $this->linestyleJoin = $ja[$join]." j";
6269                                         $this->_out($this->linestyleJoin);
6270                                 }
6271                         }
6272                         if (isset($dash)) {
6273                                 $dash_string = "";
6274                                 if ($dash) {
6275                                         if (preg_match("/^.+,/", $dash)) {
6276                                                 $tab = explode(",", $dash);
6277                                         } else {
6278                                                 $tab = array($dash);
6279                                         }
6280                                         $dash_string = "";
6281                                         foreach ($tab as $i => $v) {
6282                                                 if ($i) {
6283                                                         $dash_string .= " ";
6284                                                 }
6285                                                 $dash_string .= sprintf("%.2f", $v);
6286                                         }
6287                                 }
6288                                 if (!isset($phase) OR !$dash) {
6289                                         $phase = 0;
6290                                 }
6291                                 $this->linestyleDash = sprintf("[%s] %.2f d", $dash_string, $phase);
6292                                 $this->_out($this->linestyleDash);
6293                         }
6294                         if (isset($color)) {
6295                                 $this->SetDrawColorArray($color);
6296                         }
6297                 }
6298
6299                 /*
6300                 * Set a draw point.
6301                 * @param float $x Abscissa of point.
6302                 * @param float $y Ordinate of point.
6303                 * @access protected
6304                 * @since 2.1.000 (2008-01-08)
6305                 */
6306                 function _outPoint($x, $y) {
6307                         if ($this->rtl) {
6308                                 $x = $this->w - $x;
6309                         }
6310                         $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k));
6311                 }
6312
6313                 /*
6314                 * Draws a line from last draw point.
6315                 * @param float $x Abscissa of end point.
6316                 * @param float $y Ordinate of end point.
6317                 * @access protected
6318                 * @since 2.1.000 (2008-01-08)
6319                 */
6320                 function _outLine($x, $y) {
6321                         if ($this->rtl) {
6322                                 $x = $this->w - $x;
6323                         }
6324                         $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k));
6325                 }
6326
6327                 /**
6328                 * Draws a rectangle.
6329                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6330                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6331                 * @param float $w Width.
6332                 * @param float $h Height.
6333                 * @param string $op options
6334                 * @access protected
6335                 * @since 2.1.000 (2008-01-08)
6336                 */
6337                 function _outRect($x, $y, $w, $h, $op) {
6338                         if ($this->rtl) {
6339                                 $x = $this->w - $x - $w;
6340                         }
6341                         $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
6342                 }
6343
6344                 /*
6345                 * Draws a Bezier curve from last draw point.
6346                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
6347                 * @param float $x1 Abscissa of control point 1.
6348                 * @param float $y1 Ordinate of control point 1.
6349                 * @param float $x2 Abscissa of control point 2.
6350                 * @param float $y2 Ordinate of control point 2.
6351                 * @param float $x3 Abscissa of end point.
6352                 * @param float $y3 Ordinate of end point.
6353                 * @access protected
6354                 * @since 2.1.000 (2008-01-08)
6355                 */
6356                 function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
6357                         if ($this->rtl) {
6358                                 $x1 = $this->w - $x1;
6359                                 $x2 = $this->w - $x2;
6360                                 $x3 = $this->w - $x3;
6361                         }
6362                         $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));
6363                 }
6364
6365                 /**
6366                 * Draws a line between two points.
6367                 * @param float $x1 Abscissa of first point.
6368                 * @param float $y1 Ordinate of first point.
6369                 * @param float $x2 Abscissa of second point.
6370                 * @param float $y2 Ordinate of second point.
6371                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6372                 * @access public
6373                 * @since 1.0
6374                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
6375                 */
6376                 function Line($x1, $y1, $x2, $y2, $style=array()) {
6377                         if ($style) {
6378                                 $this->SetLineStyle($style);
6379                         }
6380                         $this->_outPoint($x1, $y1);
6381                         $this->_outLine($x2, $y2);
6382                         $this->_out(" S");
6383                 }
6384
6385                 /**
6386                 * Draws a rectangle.
6387                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6388                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6389                 * @param float $w Width.
6390                 * @param float $h Height.
6391                 * @param string $style Style of rendering. Possible values are:
6392                 * <ul>
6393                 *        <li>D or empty string: Draw (default).</li>
6394                 *        <li>F: Fill.</li>
6395                 *        <li>DF or FD: Draw and fill.</li>
6396                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6397                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6398                 * </ul>
6399                 * @param array $border_style Border style of rectangle. Array with keys among the following:
6400                 * <ul>
6401                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
6402                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
6403                 * </ul>
6404                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
6405                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6406                 * @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).
6407                 * @access public
6408                 * @since 1.0
6409                 * @see SetLineStyle()
6410                 */
6411                 function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
6412                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6413                                 $this->SetFillColorArray($fill_color);
6414                         }
6415                         switch ($style) {
6416                                 case "F": {
6417                                         $op = 'f';
6418                                         $border_style = array();
6419                                         $this->_outRect($x, $y, $w, $h, $op);
6420                                         break;
6421                                 }
6422                                 case "DF":
6423                                 case "FD": {
6424                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6425                                                 $op = 'B';
6426                                                 if (isset($border_style["all"])) {
6427                                                         $this->SetLineStyle($border_style["all"]);
6428                                                         $border_style = array();
6429                                                 }
6430                                         } else {
6431                                                 $op = 'f';
6432                                         }
6433                                         $this->_outRect($x, $y, $w, $h, $op);
6434                                         break;
6435                                 }
6436                                 case "CNZ": {
6437                                         $op = "W n";
6438                                         break;
6439                                 }
6440                                 case "CEO": {
6441                                         $op = "W* n";
6442                                         break;
6443                                 }
6444                                 default: {
6445                                         $op = 'S';
6446                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6447                                                 if (isset($border_style["all"]) AND $border_style["all"]) {
6448                                                         $this->SetLineStyle($border_style["all"]);
6449                                                         $border_style = array();
6450                                                 }
6451                                                 $this->_outRect($x, $y, $w, $h, $op);
6452                                         }
6453                                         break;
6454                                 }
6455                         }
6456                         if ($border_style) {
6457                                 $border_style2 = array();
6458                                 foreach ($border_style as $line => $value) {
6459                                         $lenght = strlen($line);
6460                                         for ($i = 0; $i < $lenght; $i++) {
6461                                                 $border_style2[$line[$i]] = $value;
6462                                         }
6463                                 }
6464                                 $border_style = $border_style2;
6465                                 if (isset($border_style["L"]) AND $border_style["L"]) {
6466                                         $this->Line($x, $y, $x, $y + $h, $border_style["L"]);
6467                                 }
6468                                 if (isset($border_style["T"]) AND $border_style["T"]) {
6469                                         $this->Line($x, $y, $x + $w, $y, $border_style["T"]);
6470                                 }
6471                                 if (isset($border_style["R"]) AND $border_style["R"]) {
6472                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]);
6473                                 }
6474                                 if (isset($border_style["B"]) AND $border_style["B"]) {
6475                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]);
6476                                 }
6477                         }
6478                 }
6479
6480
6481                 /**
6482                 * Draws a Bezier curve.
6483                 * The Bezier curve is a tangent to the line between the control points at
6484                 * either end of the curve.
6485                 * @param float $x0 Abscissa of start point.
6486                 * @param float $y0 Ordinate of start point.
6487                 * @param float $x1 Abscissa of control point 1.
6488                 * @param float $y1 Ordinate of control point 1.
6489                 * @param float $x2 Abscissa of control point 2.
6490                 * @param float $y2 Ordinate of control point 2.
6491                 * @param float $x3 Abscissa of end point.
6492                 * @param float $y3 Ordinate of end point.
6493                 * @param string $style Style of rendering. Possible values are:
6494                 * <ul>
6495                 *        <li>D or empty string: Draw (default).</li>
6496                 *        <li>F: Fill.</li>
6497                 *        <li>DF or FD: Draw and fill.</li>
6498                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6499                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6500                 * </ul>
6501                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6502                 * @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).
6503                 * @access public
6504                 * @see SetLineStyle()
6505                 * @since 2.1.000 (2008-01-08)
6506                 */
6507                 function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style="", $line_style=array(), $fill_color=array()) {
6508                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6509                                 $this->SetFillColorArray($fill_color);
6510                         }
6511                         switch ($style) {
6512                                 case "F": {
6513                                         $op = "f";
6514                                         $line_style = array();
6515                                         break;
6516                                 }
6517                                 case "FD":
6518                                 case "DF": {
6519                                         $op = "B";
6520                                         break;
6521                                 }
6522                                 case "CNZ": {
6523                                         $op = "W n";
6524                                         break;
6525                                 }
6526                                 case "CEO": {
6527                                         $op = "W* n";
6528                                         break;
6529                                 }
6530                                 default: {
6531                                         $op = "S";
6532                                         break;
6533                                 }
6534                         }
6535                         if ($line_style) {
6536                                 $this->SetLineStyle($line_style);
6537                         }
6538                         $this->_outPoint($x0, $y0);
6539                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6540                         $this->_out($op);
6541                 }
6542
6543                 /**
6544                 * Draws a poly-Bezier curve.
6545                 * Each Bezier curve segment is a tangent to the line between the control points at
6546                 * either end of the curve.
6547                 * @param float $x0 Abscissa of start point.
6548                 * @param float $y0 Ordinate of start point.
6549                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
6550                 * @param string $style Style of rendering. Possible values are:
6551                 * <ul>
6552                 *        <li>D or empty string: Draw (default).</li>
6553                 *        <li>F: Fill.</li>
6554                 *        <li>DF or FD: Draw and fill.</li>
6555                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6556                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6557                 * </ul>
6558                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6559                 * @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).
6560                 * @access public
6561                 * @see SetLineStyle()
6562                 * @since 3.0008 (2008-05-12)
6563                 */
6564                 function Polycurve($x0, $y0, $segments, $style="", $line_style=array(), $fill_color=array()) {
6565                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6566                                 $this->SetFillColorArray($fill_color);
6567                         }
6568                         switch ($style) {
6569                                 case "F": {
6570                                         $op = "f";
6571                                         $line_style = array();
6572                                         break;
6573                                 }
6574                                 case "FD":
6575                                 case "DF": {
6576                                         $op = "B";
6577                                         break;
6578                                 }
6579                                 case "CNZ": {
6580                                         $op = "W n";
6581                                         break;
6582                                 }
6583                                 case "CEO": {
6584                                         $op = "W* n";
6585                                         break;
6586                                 }
6587                                 default: {
6588                                         $op = "S";
6589                                         break;
6590                                 }
6591                         }
6592                         if ($line_style) {
6593                                 $this->SetLineStyle($line_style);
6594                         }
6595                         $this->_outPoint($x0, $y0);
6596                         foreach ($segments as $segment) {
6597                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
6598                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6599                         }
6600                         $this->_out($op);
6601                 }
6602
6603                 /**
6604                 * Draws an ellipse.
6605                 * An ellipse is formed from n Bezier curves.
6606                 * @param float $x0 Abscissa of center point.
6607                 * @param float $y0 Ordinate of center point.
6608                 * @param float $rx Horizontal radius.
6609                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
6610                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6611                 * @param float $astart: Angle start of draw line. Default value: 0.
6612                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6613                 * @param string $style Style of rendering. Possible values are:
6614                 * <ul>
6615                 *        <li>D or empty string: Draw (default).</li>
6616                 *        <li>F: Fill.</li>
6617                 *        <li>DF or FD: Draw and fill.</li>
6618                 *        <li>C: Draw close.</li>
6619                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6620                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6621                 * </ul>
6622                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6623                 * @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).
6624                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
6625                 * @access public
6626                 * @since 2.1.000 (2008-01-08)
6627                 */
6628                 function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6629                         if ($angle) {
6630                                 $this->StartTransform();
6631                                 $this->Rotate($angle, $x0, $y0);
6632                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6633                                 $this->StopTransform();
6634                                 return;
6635                         }
6636                         if ($rx) {
6637                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6638                                         $this->SetFillColorArray($fill_color);
6639                                 }
6640                                 switch ($style) {
6641                                         case "F": {
6642                                                 $op = "f";
6643                                                 $line_style = array();
6644                                                 break;
6645                                         }
6646                                         case "FD":
6647                                         case "DF": {
6648                                                 $op = "B";
6649                                                 break;
6650                                         }
6651                                         case "C": {
6652                                                 $op = "s"; // Small "s" signifies closing the path as well
6653                                                 break;
6654                                         }
6655                                         case "CNZ": {
6656                                                 $op = "W n";
6657                                                 break;
6658                                         }
6659                                         case "CEO": {
6660                                                 $op = "W* n";
6661                                                 break;
6662                                         }
6663                                         default: {
6664                                                 $op = "S";
6665                                                 break;
6666                                         }
6667                                 }
6668                                 if ($line_style) {
6669                                         $this->SetLineStyle($line_style);
6670                                 }
6671                                 if (!$ry) {
6672                                         $ry = $rx;
6673                                 }
6674                                 $rx *= $this->k;
6675                                 $ry *= $this->k;
6676                                 if ($nc < 2){
6677                                         $nc = 2;
6678                                 }
6679                                 $astart = deg2rad((float) $astart);
6680                                 $afinish = deg2rad((float) $afinish);
6681                                 $total_angle = $afinish - $astart;
6682                                 $dt = $total_angle / $nc;
6683                                 $dtm = $dt / 3;
6684                                 $x0 *= $this->k;
6685                                 $y0 = ($this->h - $y0) * $this->k;
6686                                 $t1 = $astart;
6687                                 $a0 = $x0 + ($rx * cos($t1));
6688                                 $b0 = $y0 + ($ry * sin($t1));
6689                                 $c0 = -$rx * sin($t1);
6690                                 $d0 = $ry * cos($t1);
6691                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
6692                                 for ($i = 1; $i <= $nc; $i++) {
6693                                         // Draw this bit of the total curve
6694                                         $t1 = ($i * $dt) + $astart;
6695                                         $a1 = $x0 + ($rx * cos($t1));
6696                                         $b1 = $y0 + ($ry * sin($t1));
6697                                         $c1 = -$rx * sin($t1);
6698                                         $d1 = $ry * cos($t1);
6699                                         $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));
6700                                         $a0 = $a1;
6701                                         $b0 = $b1;
6702                                         $c0 = $c1;
6703                                         $d0 = $d1;
6704                                 }
6705                                 $this->_out($op);
6706                         }
6707                 }
6708
6709                 /**
6710                 * Draws a circle.
6711                 * A circle is formed from n Bezier curves.
6712                 * @param float $x0 Abscissa of center point.
6713                 * @param float $y0 Ordinate of center point.
6714                 * @param float $r Radius.
6715                 * @param float $astart: Angle start of draw line. Default value: 0.
6716                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6717                 * @param string $style Style of rendering. Possible values are:
6718                 * <ul>
6719                 *        <li>D or empty string: Draw (default).</li>
6720                 *        <li>F: Fill.</li>
6721                 *        <li>DF or FD: Draw and fill.</li>
6722                 *        <li>C: Draw close.</li>
6723                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6724                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6725                 * </ul>
6726                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6727                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6728                 * @param integer $nc Number of curves used in circle. Default value: 8.
6729                 * @access public
6730                 * @since 2.1.000 (2008-01-08)
6731                 */
6732                 function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6733                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6734                 }
6735
6736                 /**
6737                 * Draws a polygon.
6738                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
6739                 * @param string $style Style of rendering. Possible values are:
6740                 * <ul>
6741                 *        <li>D or empty string: Draw (default).</li>
6742                 *        <li>F: Fill.</li>
6743                 *        <li>DF or FD: Draw and fill.</li>
6744                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6745                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6746                 * </ul>
6747                 * @param array $line_style Line style of polygon. Array with keys among the following:
6748                 * <ul>
6749                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
6750                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
6751                 * </ul>
6752                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
6753                 * @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).
6754                 * @access public
6755                 * @since 2.1.000 (2008-01-08)
6756                 */
6757                 function Polygon($p, $style="", $line_style=array(), $fill_color=array()) {
6758                         $np = count($p) / 2;
6759                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6760                                 $this->SetFillColorArray($fill_color);
6761                         }
6762                         switch ($style) {
6763                                 case "F": {
6764                                         $line_style = array();
6765                                         $op = "f";
6766                                         break;
6767                                 }
6768                                 case "FD":
6769                                 case "DF": {
6770                                         $op = "B";
6771                                         break;
6772                                 }
6773                                 case "CNZ": {
6774                                         $op = "W n";
6775                                         break;
6776                                 }
6777                                 case "CEO": {
6778                                         $op = "W* n";
6779                                         break;
6780                                 }
6781                                 default: {
6782                                         $op = "S";
6783                                         break;
6784                                 }
6785                         }
6786                         $draw = true;
6787                         if ($line_style) {
6788                                 if (isset($line_style["all"])) {
6789                                         $this->SetLineStyle($line_style["all"]);
6790                                 } else { // 0 .. (np - 1), op = {B, S}
6791                                         $draw = false;
6792                                         if ("B" == $op) {
6793                                                 $op = "f";
6794                                                 $this->_outPoint($p[0], $p[1]);
6795                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6796                                                         $this->_outLine($p[$i], $p[$i + 1]);
6797                                                 }
6798                                                 $this->_outLine($p[0], $p[1]);
6799                                                 $this->_out($op);
6800                                         }
6801                                         $p[($np * 2)] = $p[0];
6802                                         $p[(($np * 2) + 1)] = $p[1];
6803                                         for ($i = 0; $i < $np; $i++) {
6804                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
6805                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
6806                                                 }
6807                                         }
6808                                 }
6809                         }
6810                         if ($draw) {
6811                                 $this->_outPoint($p[0], $p[1]);
6812                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6813                                         $this->_outLine($p[$i], $p[$i + 1]);
6814                                 }
6815                                 $this->_outLine($p[0], $p[1]);
6816                                 $this->_out($op);
6817                         }
6818                 }
6819
6820                 /**
6821                 * Draws a regular polygon.
6822                 * @param float $x0 Abscissa of center point.
6823                 * @param float $y0 Ordinate of center point.
6824                 * @param float $r: Radius of inscribed circle.
6825                 * @param integer $ns Number of sides.
6826                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
6827                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
6828                 * @param string $style Style of rendering. Possible values are:
6829                 * <ul>
6830                 *        <li>D or empty string: Draw (default).</li>
6831                 *        <li>F: Fill.</li>
6832                 *        <li>DF or FD: Draw and fill.</li>
6833                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6834                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6835                 * </ul>
6836                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6837                 * <ul>
6838                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
6839                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6840                 * </ul>
6841                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6842                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6843                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6844                 * <ul>
6845                 *        <li>D or empty string: Draw (default).</li>
6846                 *        <li>F: Fill.</li>
6847                 *        <li>DF or FD: Draw and fill.</li>
6848                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6849                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6850                 * </ul>
6851                 * @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).
6852                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6853                 * @access public
6854                 * @since 2.1.000 (2008-01-08)
6855                 */
6856                 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()) {
6857                         if (3 > $ns) {
6858                                 $ns = 3;
6859                         }
6860                         if ($draw_circle) {
6861                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6862                         }
6863                         $p = array();
6864                         for ($i = 0; $i < $ns; $i++) {
6865                                 $a = $angle + ($i * 360 / $ns);
6866                                 $a_rad = deg2rad((float) $a);
6867                                 $p[] = $x0 + ($r * sin($a_rad));
6868                                 $p[] = $y0 + ($r * cos($a_rad));
6869                         }
6870                         $this->Polygon($p, $style, $line_style, $fill_color);
6871                 }
6872
6873                 /**
6874                 * Draws a star polygon
6875                 * @param float $x0 Abscissa of center point.
6876                 * @param float $y0 Ordinate of center point.
6877                 * @param float $r Radius of inscribed circle.
6878                 * @param integer $nv Number of vertices.
6879                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
6880                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6881                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
6882                 * @param string $style Style of rendering. Possible values are:
6883                 * <ul>
6884                 *        <li>D or empty string: Draw (default).</li>
6885                 *        <li>F: Fill.</li>
6886                 *        <li>DF or FD: Draw and fill.</li>
6887                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6888                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6889                 * </ul>
6890                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6891                 * <ul>
6892                 *        <li>all: Line style of all sides. Array like for
6893                 * {@link SetLineStyle SetLineStyle}.</li>
6894                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6895                 * </ul>
6896                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6897                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6898                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6899                 * <ul>
6900                 *        <li>D or empty string: Draw (default).</li>
6901                 *        <li>F: Fill.</li>
6902                 *        <li>DF or FD: Draw and fill.</li>
6903                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6904                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6905                 * </ul>
6906                 * @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).
6907                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6908                 * @access public
6909                 * @since 2.1.000 (2008-01-08)
6910                 */
6911                 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()) {
6912                         if (2 > $nv) {
6913                                 $nv = 2;
6914                         }
6915                         if ($draw_circle) {
6916                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6917                         }
6918                         $p2 = array();
6919                         $visited = array();
6920                         for ($i = 0; $i < $nv; $i++) {
6921                                 $a = $angle + ($i * 360 / $nv);
6922                                 $a_rad = deg2rad((float) $a);
6923                                 $p2[] = $x0 + ($r * sin($a_rad));
6924                                 $p2[] = $y0 + ($r * cos($a_rad));
6925                                 $visited[] = false;
6926                         }
6927                         $p = array();
6928                         $i = 0;
6929                         do {
6930                                 $p[] = $p2[$i * 2];
6931                                 $p[] = $p2[($i * 2) + 1];
6932                                 $visited[$i] = true;
6933                                 $i += $ng;
6934                                 $i %= $nv;
6935                         } while (!$visited[$i]);
6936                         $this->Polygon($p, $style, $line_style, $fill_color);
6937                 }
6938
6939                 /**
6940                 * Draws a rounded rectangle.
6941                 * @param float $x Abscissa of upper-left corner.
6942                 * @param float $y Ordinate of upper-left corner.
6943                 * @param float $w Width.
6944                 * @param float $h Height.
6945                 * @param float $r Radius of the rounded corners.
6946                 * @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").
6947                 * @param string $style Style of rendering. Possible values are:
6948                 * <ul>
6949                 *        <li>D or empty string: Draw (default).</li>
6950                 *        <li>F: Fill.</li>
6951                 *        <li>DF or FD: Draw and fill.</li>
6952                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6953                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6954                 * </ul>
6955                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6956                 * @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).
6957                 * @access public
6958                 * @since 2.1.000 (2008-01-08)
6959                 */
6960                 function RoundedRect($x, $y, $w, $h, $r, $round_corner="1111", $style="", $border_style=array(), $fill_color=array()) {
6961                         if ("0000" == $round_corner) { // Not rounded
6962                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
6963                         } else { // Rounded
6964                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6965                                         $this->SetFillColorArray($fill_color);
6966                                 }
6967                                 switch ($style) {
6968                                         case "F": {
6969                                                 $border_style = array();
6970                                                 $op = "f";
6971                                                 break;
6972                                         }
6973                                         case "FD":
6974                                         case "DF": {
6975                                                 $op = "B";
6976                                                 break;
6977                                         }
6978                                         case "CNZ": {
6979                                                 $op = "W n";
6980                                                 break;
6981                                         }
6982                                         case "CEO": {
6983                                                 $op = "W* n";
6984                                                 break;
6985                                         }
6986                                         default: {
6987                                                 $op = "S";
6988                                                 break;
6989                                         }
6990                                 }
6991                                 if ($border_style) {
6992                                         $this->SetLineStyle($border_style);
6993                                 }
6994                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
6995                                 $this->_outPoint($x + $r, $y);
6996                                 $xc = $x + $w - $r;
6997                                 $yc = $y + $r;
6998                                 $this->_outLine($xc, $y);
6999                                 if ($round_corner[0]) {
7000                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
7001                                 } else {
7002                                         $this->_outLine($x + $w, $y);
7003                                 }
7004                                 $xc = $x + $w - $r;
7005                                 $yc = $y + $h - $r;
7006                                 $this->_outLine($x + $w, $yc);
7007                                 if ($round_corner[1]) {
7008                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
7009                                 } else {
7010                                         $this->_outLine($x + $w, $y + $h);
7011                                 }
7012                                 $xc = $x + $r;
7013                                 $yc = $y + $h - $r;
7014                                 $this->_outLine($xc, $y + $h);
7015                                 if ($round_corner[2]) {
7016                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
7017                                 } else {
7018                                         $this->_outLine($x, $y + $h);
7019                                 }
7020                                 $xc = $x + $r;
7021                                 $yc = $y + $r;
7022                                 $this->_outLine($x, $yc);
7023                                 if ($round_corner[3]) {
7024                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
7025                                 } else {
7026                                         $this->_outLine($x, $y);
7027                                         $this->_outLine($x + $r, $y);
7028                                 }
7029                                 $this->_out($op);
7030                         }
7031                 }
7032
7033                 // END GRAPHIC FUNCTIONS SECTION -----------------------
7034
7035                 // BIDIRECTIONAL TEXT SECTION --------------------------
7036                 /**
7037                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7038                  * @param string $str string to manipulate.
7039                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7040                  * @return string
7041                  * @author Nicola Asuni
7042                  * @since 2.1.000 (2008-01-08)
7043                 */
7044                 function utf8StrRev($str, $setbom=false, $forcertl=false) {
7045                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $forcertl), $setbom);
7046                 }
7047
7048                 /**
7049                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7050                  * @param array $ta array of characters composing the string.
7051                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7052                  * @return string
7053                  * @author Nicola Asuni
7054                  * @since 2.4.000 (2008-03-06)
7055                 */
7056                 function utf8Bidi($ta, $forcertl=false) {
7057                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
7058                         // paragraph embedding level
7059                         $pel = 0;
7060                         // max level
7061                         $maxlevel = 0;
7062                         // create string from array
7063                         $str = $this->UTF8ArrSubString($ta);
7064                         // check if string contains arabic text
7065                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
7066                                 $arabic = true;
7067                         } else {
7068                                 $arabic = false;
7069                         }
7070                         // check if string contains RTL text
7071                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
7072                                 return $ta;
7073                         }
7074                         
7075                         // get number of chars
7076                         $numchars = count($ta);
7077                         
7078                         if ($forcertl == 'R') {
7079                                         $pel = 1;
7080                         } elseif ($forcertl == 'L') {
7081                                         $pel = 0;
7082                         } else {
7083                                 // P2. In each paragraph, find the first character of type L, AL, or R.
7084                                 // 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.
7085                                 for ($i=0; $i < $numchars; $i++) {
7086                                         $type = $unicode[$ta[$i]];
7087                                         if ($type == 'L') {
7088                                                 $pel = 0;
7089                                                 break;
7090                                         } elseif (($type == 'AL') OR ($type == 'R')) {
7091                                                 $pel = 1;
7092                                                 break;
7093                                         }
7094                                 }
7095                         }
7096                         
7097                         // Current Embedding Level
7098                         $cel = $pel;
7099                         // directional override status
7100                         $dos = 'N';
7101                         $remember = array();
7102                         // start-of-level-run
7103                         $sor = $pel % 2 ? 'R' : 'L';
7104                         $eor = $sor;
7105
7106                         //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array()));
7107                         //$current_level = &$levels[count( $levels )-1];
7108
7109                         // Array of characters data
7110                         $chardata = Array();
7111
7112                         // 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.
7113                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
7114                         for ($i=0; $i < $numchars; $i++) {
7115                                 if ($ta[$i] == K_RLE) {
7116                                         // X2. With each RLE, compute the least greater odd embedding level.
7117                                         //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
7118                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7119                                         $next_level = $cel + ($cel % 2) + 1;
7120                                         if ($next_level < 62) {
7121                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
7122                                                 $cel = $next_level;
7123                                                 $dos = 'N';
7124                                                 $sor = $eor;
7125                                                 $eor = $cel % 2 ? 'R' : 'L';
7126                                         }
7127                                 } elseif ($ta[$i] == K_LRE) {
7128                                         // X3. With each LRE, compute the least greater even embedding level.
7129                                         //      a. If this new level would be valid, then this embedding code is valid. Remember (push) the current embedding level and override status. Reset the current level to this new level, and reset the override status to neutral.
7130                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7131                                         $next_level = $cel + 2 - ($cel % 2);
7132                                         if ( $next_level < 62 ) {
7133                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
7134                                                 $cel = $next_level;
7135                                                 $dos = 'N';
7136                                                 $sor = $eor;
7137                                                 $eor = $cel % 2 ? 'R' : 'L';
7138                                         }
7139                                 } elseif ($ta[$i] == K_RLO) {
7140                                         // X4. With each RLO, compute the least greater odd embedding level.
7141                                         //      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.
7142                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7143                                         $next_level = $cel + ($cel % 2) + 1;
7144                                         if ($next_level < 62) {
7145                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
7146                                                 $cel = $next_level;
7147                                                 $dos = 'R';
7148                                                 $sor = $eor;
7149                                                 $eor = $cel % 2 ? 'R' : 'L';
7150                                         }
7151                                 } elseif ($ta[$i] == K_LRO) {
7152                                         // X5. With each LRO, compute the least greater even embedding level.
7153                                         //      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.
7154                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7155                                         $next_level = $cel + 2 - ($cel % 2);
7156                                         if ( $next_level < 62 ) {
7157                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
7158                                                 $cel = $next_level;
7159                                                 $dos = 'L';
7160                                                 $sor = $eor;
7161                                                 $eor = $cel % 2 ? 'R' : 'L';
7162                                         }
7163                                 } elseif ($ta[$i] == K_PDF) {
7164                                         // 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.
7165                                         if (count($remember)) {
7166                                                 $last = count($remember ) - 1;
7167                                                 if (($remember[$last]['num'] == K_RLE) OR
7168                                                           ($remember[$last]['num'] == K_LRE) OR
7169                                                           ($remember[$last]['num'] == K_RLO) OR
7170                                                           ($remember[$last]['num'] == K_LRO)) {
7171                                                         $match = array_pop($remember);
7172                                                         $cel = $match['cel'];
7173                                                         $dos = $match['dos'];
7174                                                         $sor = $eor;
7175                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
7176                                                 }
7177                                         }
7178                                 } elseif (($ta[$i] != K_RLE) AND
7179                                                                  ($ta[$i] != K_LRE) AND
7180                                                                  ($ta[$i] != K_RLO) AND
7181                                                                  ($ta[$i] != K_LRO) AND
7182                                                                  ($ta[$i] != K_PDF)) {
7183                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
7184                                         //      a. Set the level of the current character to the current embedding level.
7185                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
7186                                         if ($dos != 'N') {
7187                                                 $chardir = $dos;
7188                                         } else {
7189                                                 $chardir = $unicode[$ta[$i]];
7190                                         }
7191                                         // stores string characters and other information
7192                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
7193                                 }
7194                         } // end for each char
7195
7196                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
7197                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
7198                         // 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.
7199
7200                         // 3.3.3 Resolving Weak Types
7201                         // 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.
7202                         // Nonspacing marks are now resolved based on the previous characters.
7203                         $numchars = count($chardata);
7204
7205                         // 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.
7206                         $prevlevel = -1; // track level changes
7207                         $levcount = 0; // counts consecutive chars at the same level
7208                         for ($i=0; $i < $numchars; $i++) {
7209                                 if ($chardata[$i]['type'] == 'NSM') {
7210                                         if ($levcount) {
7211                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7212                                         } elseif ($i > 0) {
7213                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
7214                                         }
7215                                 }
7216                                 if ($chardata[$i]['level'] != $prevlevel) {
7217                                         $levcount = 0;
7218                                 } else {
7219                                         $levcount++;
7220                                 }
7221                                 $prevlevel = $chardata[$i]['level'];
7222                         }
7223
7224                         // 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.
7225                         $prevlevel = -1;
7226                         $levcount = 0;
7227                         for ($i=0; $i < $numchars; $i++) {
7228                                 if ($chardata[$i]['char'] == 'EN') {
7229                                         for ($j=$levcount; $j >= 0; $j--) {
7230                                                 if ($chardata[$j]['type'] == 'AL') {
7231                                                         $chardata[$i]['type'] = 'AN';
7232                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
7233                                                         break;
7234                                                 }
7235                                         }
7236                                 }
7237                                 if ($chardata[$i]['level'] != $prevlevel) {
7238                                         $levcount = 0;
7239                                 } else {
7240                                         $levcount++;
7241                                 }
7242                                 $prevlevel = $chardata[$i]['level'];
7243                         }
7244
7245                         // W3. Change all ALs to R.
7246                         for ($i=0; $i < $numchars; $i++) {
7247                                 if ($chardata[$i]['type'] == 'AL') {
7248                                         $chardata[$i]['type'] = 'R';
7249                                 }
7250                         }
7251
7252                         // 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.
7253                         $prevlevel = -1;
7254                         $levcount = 0;
7255                         for ($i=0; $i < $numchars; $i++) {
7256                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7257                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7258                                                 $chardata[$i]['type'] = 'EN';
7259                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7260                                                 $chardata[$i]['type'] = 'EN';
7261                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
7262                                                 $chardata[$i]['type'] = 'AN';
7263                                         }
7264                                 }
7265                                 if ($chardata[$i]['level'] != $prevlevel) {
7266                                         $levcount = 0;
7267                                 } else {
7268                                         $levcount++;
7269                                 }
7270                                 $prevlevel = $chardata[$i]['level'];
7271                         }
7272
7273                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
7274                         $prevlevel = -1;
7275                         $levcount = 0;
7276                         for ($i=0; $i < $numchars; $i++) {
7277                                 if ($chardata[$i]['type'] == 'ET') {
7278                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
7279                                                 $chardata[$i]['type'] = 'EN';
7280                                         } else {
7281                                                 $j = $i+1;
7282                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
7283                                                         if ($chardata[$j]['type'] == 'EN') {
7284                                                                 $chardata[$i]['type'] = 'EN';
7285                                                                 break;
7286                                                         } elseif ($chardata[$j]['type'] != 'ET') {
7287                                                                 break;
7288                                                         }
7289                                                         $j++;
7290                                                 }
7291                                         }
7292                                 }
7293                                 if ($chardata[$i]['level'] != $prevlevel) {
7294                                         $levcount = 0;
7295                                 } else {
7296                                         $levcount++;
7297                                 }
7298                                 $prevlevel = $chardata[$i]['level'];
7299                         }
7300
7301                         // W6. Otherwise, separators and terminators change to Other Neutral.
7302                         $prevlevel = -1;
7303                         $levcount = 0;
7304                         for ($i=0; $i < $numchars; $i++) {
7305                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
7306                                         $chardata[$i]['type'] = 'ON';
7307                                 }
7308                                 if ($chardata[$i]['level'] != $prevlevel) {
7309                                         $levcount = 0;
7310                                 } else {
7311                                         $levcount++;
7312                                 }
7313                                 $prevlevel = $chardata[$i]['level'];
7314                         }
7315
7316                         //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.
7317                         $prevlevel = -1;
7318                         $levcount = 0;
7319                         for ($i=0; $i < $numchars; $i++) {
7320                                 if ($chardata[$i]['char'] == 'EN') {
7321                                         for ($j=$levcount; $j >= 0; $j--) {
7322                                                 if ($chardata[$j]['type'] == 'L') {
7323                                                         $chardata[$i]['type'] = 'L';
7324                                                 } elseif ($chardata[$j]['type'] == 'R') {
7325                                                         break;
7326                                                 }
7327                                         }
7328                                 }
7329                                 if ($chardata[$i]['level'] != $prevlevel) {
7330                                         $levcount = 0;
7331                                 } else {
7332                                         $levcount++;
7333                                 }
7334                                 $prevlevel = $chardata[$i]['level'];
7335                         }
7336
7337                         // 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.
7338                         $prevlevel = -1;
7339                         $levcount = 0;
7340                         for ($i=0; $i < $numchars; $i++) {
7341                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7342                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7343                                                 $chardata[$i]['type'] = 'L';
7344                                         } elseif (($chardata[$i]['type'] == 'N') AND
7345                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7346                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7347                                                 $chardata[$i]['type'] = 'R';
7348                                         } elseif ($chardata[$i]['type'] == 'N') {
7349                                                 // N2. Any remaining neutrals take the embedding direction
7350                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7351                                         }
7352                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7353                                         // first char
7354                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7355                                                 $chardata[$i]['type'] = 'L';
7356                                         } elseif (($chardata[$i]['type'] == 'N') AND
7357                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
7358                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7359                                                 $chardata[$i]['type'] = 'R';
7360                                         } elseif ($chardata[$i]['type'] == 'N') {
7361                                                 // N2. Any remaining neutrals take the embedding direction
7362                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7363                                         }
7364                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
7365                                         //last char
7366                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
7367                                                 $chardata[$i]['type'] = 'L';
7368                                         } elseif (($chardata[$i]['type'] == 'N') AND
7369                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7370                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
7371                                                 $chardata[$i]['type'] = 'R';
7372                                         } elseif ($chardata[$i]['type'] == 'N') {
7373                                                 // N2. Any remaining neutrals take the embedding direction
7374                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7375                                         }
7376                                 } elseif ($chardata[$i]['type'] == 'N') {
7377                                         // N2. Any remaining neutrals take the embedding direction
7378                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
7379                                 }
7380                                 if ($chardata[$i]['level'] != $prevlevel) {
7381                                         $levcount = 0;
7382                                 } else {
7383                                         $levcount++;
7384                                 }
7385                                 $prevlevel = $chardata[$i]['level'];
7386                         }
7387
7388                         // 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.
7389                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
7390                         for ($i=0; $i < $numchars; $i++) {
7391                                 $odd = $chardata[$i]['level'] % 2;
7392                                 if ($odd) {
7393                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7394                                                 $chardata[$i]['level'] += 1;
7395                                         }
7396                                 } else {
7397                                         if ($chardata[$i]['type'] == 'R') {
7398                                                 $chardata[$i]['level'] += 1;
7399                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7400                                                 $chardata[$i]['level'] += 2;
7401                                         }
7402                                 }
7403                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
7404                         }
7405
7406                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
7407                         //      1. Segment separators,
7408                         //      2. Paragraph separators,
7409                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
7410                         //      4. Any sequence of white space characters at the end of the line.
7411                         for ($i=0; $i < $numchars; $i++) {
7412                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
7413                                         $chardata[$i]['level'] = $pel;
7414                                 } elseif ($chardata[$i]['type'] == 'WS') {
7415                                         $j = $i+1;
7416                                         while ($j < $numchars) {
7417                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
7418                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
7419                                                         $chardata[$i]['level'] = $pel;
7420                                                         break;
7421                                                 } elseif ($chardata[$j]['type'] != 'WS') {
7422                                                         break;
7423                                                 }
7424                                                 $j++;
7425                                         }
7426                                 }
7427                         }
7428
7429                         // Arabic Shaping
7430                         // 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.
7431                         if ($arabic) {
7432                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
7433                                 $alfletter = array(1570,1571,1573,1575);
7434                                 $chardata2 = $chardata;
7435                                 $laaletter = false;
7436                                 $charAL = array();
7437                                 $x = 0;
7438                                 for ($i=0; $i < $numchars; $i++) {
7439                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
7440                                                 $charAL[$x] = $chardata[$i];
7441                                                 $charAL[$x]['i'] = $i;
7442                                                 $chardata[$i]['x'] = $x;
7443                                                 $x++;
7444                                         }
7445                                 }
7446                                 $numAL = $x;
7447                                 for ($i=0; $i < $numchars; $i++) {
7448                                         $thischar = $chardata[$i];
7449                                         if ($i > 0) {
7450                                                 $prevchar = $chardata[($i-1)];
7451                                         } else {
7452                                                 $prevchar = false;
7453                                         }
7454                                         if (($i+1) < $numchars) {
7455                                                 $nextchar = $chardata[($i+1)];
7456                                         } else {
7457                                                 $nextchar = false;
7458                                         }
7459                                         if ($unicode[$thischar['char']] == 'AL') {
7460                                                 $x = $thischar['x'];
7461                                                 if ($x > 0) {
7462                                                         $prevchar = $charAL[($x-1)];
7463                                                 } else {
7464                                                         $prevchar = false;
7465                                                 }
7466                                                 if (($x+1) < $numAL) {
7467                                                         $nextchar = $charAL[($x+1)];
7468                                                 } else {
7469                                                         $nextchar = false;
7470                                                 }
7471                                                 // if laa letter
7472                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
7473                                                         $arabicarr = $laa_array;
7474                                                         $laaletter = true;
7475                                                         if ($x > 1) {
7476                                                                 $prevchar = $charAL[($x-2)];
7477                                                         } else {
7478                                                                 $prevchar = false;
7479                                                         }
7480                                                 } else {
7481                                                         $arabicarr = $unicode_arlet;
7482                                                         $laaletter = false;
7483                                                 }
7484                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
7485                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7486                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7487                                                         ($prevchar['type'] == $thischar['type']) AND
7488                                                         ($nextchar['type'] == $thischar['type']) AND
7489                                                         ($nextchar['char'] != 1567)) {
7490                                                         if (in_array($prevchar['char'], $endedletter)) {
7491                                                                 if (isset($arabicarr[$thischar['char']][2])) {
7492                                                                         // initial
7493                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7494                                                                 }
7495                                                         } else {
7496                                                                 if (isset($arabicarr[$thischar['char']][3])) {
7497                                                                         // medial
7498                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
7499                                                                 }
7500                                                         }
7501                                                 } elseif (($nextchar !== false) AND
7502                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7503                                                         ($nextchar['type'] == $thischar['type']) AND
7504                                                         ($nextchar['char'] != 1567)) {
7505                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
7506                                                                 // initial
7507                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7508                                                         }
7509                                                 } elseif ((($prevchar !== false) AND
7510                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7511                                                         ($prevchar['type'] == $thischar['type'])) OR
7512                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
7513                                                         // final
7514                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
7515                                                                 ($chardata[$i-1]['char'] == 1604) AND
7516                                                                 ($chardata[$i-2]['char'] == 1604)) {
7517                                                                 //Allah Word
7518                                                                 // mark characters to delete with false
7519                                                                 $chardata2[$i-2]['char'] = false;
7520                                                                 $chardata2[$i-1]['char'] = false;
7521                                                                 $chardata2[$i]['char'] = 65010;
7522                                                         } else {
7523                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
7524                                                                         if (isset($arabicarr[$thischar['char']][0])) {
7525                                                                                 // isolated
7526                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7527                                                                         }
7528                                                                 } else {
7529                                                                         if (isset($arabicarr[$thischar['char']][1])) {
7530                                                                                 // final
7531                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
7532                                                                         }
7533                                                                 }
7534                                                         }
7535                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
7536                                                         // isolated
7537                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7538                                                 }
7539                                                 // if laa letter
7540                                                 if ($laaletter) {
7541                                                         // mark characters to delete with false
7542                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
7543                                                 }
7544                                         } // end if AL (Arabic Letter)
7545                                 } // end for each char
7546                                 /*
7547                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594.
7548                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
7549                                  */
7550                                 $cw = &$this->CurrentFont['cw'];
7551                                 for ($i=0; $i < ($numchars-1); $i++) {
7552                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
7553                                                 // check if the subtitution font is defined on current font
7554                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
7555                                                         $chardata2[$i]['char'] = false;
7556                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
7557                                                 }
7558                                         }
7559                                 }
7560                                 // remove marked characters
7561                                 foreach($chardata2 as $key => $value) {
7562                                         if ($value['char'] === false) {
7563                                                 unset($chardata2[$key]);
7564                                         }
7565                                 }
7566                                 $chardata = array_values($chardata2);
7567                                 $numchars = count($chardata);
7568                                 unset($chardata2);
7569                                 unset($arabicarr);
7570                                 unset($laaletter);
7571                                 unset($charAL);
7572                         }
7573
7574                         // 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.
7575                         for ($j=$maxlevel; $j > 0; $j--) {
7576                                 $ordarray = Array();
7577                                 $revarr = Array();
7578                                 $onlevel = false;
7579                                 for ($i=0; $i < $numchars; $i++) {
7580                                         if ($chardata[$i]['level'] >= $j) {
7581                                                 $onlevel = true;
7582                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
7583                                                         // 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.
7584                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
7585                                                 }
7586                                                 $revarr[] = $chardata[$i];
7587                                         } else {
7588                                                 if ($onlevel) {
7589                                                         $revarr = array_reverse($revarr);
7590                                                         $ordarray = array_merge($ordarray, $revarr);
7591                                                         $revarr = Array();
7592                                                         $onlevel = false;
7593                                                 }
7594                                                 $ordarray[] = $chardata[$i];
7595                                         }
7596                                 }
7597                                 if ($onlevel) {
7598                                         $revarr = array_reverse($revarr);
7599                                         $ordarray = array_merge($ordarray, $revarr);
7600                                 }
7601                                 $chardata = $ordarray;
7602                         }
7603
7604                         $ordarray = array();
7605                         for ($i=0; $i < $numchars; $i++) {
7606                                 $ordarray[] = $chardata[$i]['char'];
7607                         }
7608
7609                         return $ordarray;
7610                 }
7611
7612                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
7613
7614                 /*
7615                 * Adds a bookmark.
7616                 * @param string $txt bookmark description.
7617                 * @param int $level bookmark level (minimum value is 0).
7618                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
7619                 * @access public
7620                 * @author Olivier Plathey, Nicola Asuni
7621                 * @since 2.1.002 (2008-02-12)
7622                 */
7623                 function Bookmark($txt, $level=0, $y=-1) {
7624                         if ($level < 0) {
7625                                 $level = 0;
7626                         }
7627                         if (isset($this->outlines[0])) {
7628                                 $lastoutline = end($this->outlines);
7629                                 $maxlevel = $lastoutline['l'] + 1;
7630                         } else {
7631                                 $maxlevel = 0;
7632                         }
7633                         if ($level > $maxlevel) {
7634                                 $level = $maxlevel;
7635                         }
7636                         if ($y == -1) {
7637                                 $y = $this->GetY();
7638                         }
7639                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->PageNo());
7640                 }
7641
7642                 /*
7643                 * Create a bookmark PDF string.
7644                 * @access protected
7645                 * @author Olivier Plathey, Nicola Asuni
7646                 * @since 2.1.002 (2008-02-12)
7647                 */
7648                 function _putbookmarks() {
7649                         $nb = count($this->outlines);
7650                         if ($nb == 0) {
7651                                 return;
7652                         }
7653                         $lru = array();
7654                         $level = 0;
7655                         foreach($this->outlines as $i => $o) {
7656                                 if ($o['l'] > 0) {
7657                                         $parent = $lru[($o['l'] - 1)];
7658                                         //Set parent and last pointers
7659                                         $this->outlines[$i]['parent'] = $parent;
7660                                         $this->outlines[$parent]['last'] = $i;
7661                                         if ($o['l'] > $level) {
7662                                                 //Level increasing: set first pointer
7663                                                 $this->outlines[$parent]['first'] = $i;
7664                                         }
7665                                 } else {
7666                                         $this->outlines[$i]['parent'] = $nb;
7667                                 }
7668                                 if (($o['l'] <= $level) AND ($i > 0)) {
7669                                         //Set prev and next pointers
7670                                         $prev = $lru[$o['l']];
7671                                         $this->outlines[$prev]['next'] = $i;
7672                                         $this->outlines[$i]['prev'] = $prev;
7673                                 }
7674                                 $lru[$o['l']] = $i;
7675                                 $level = $o['l'];
7676                         }
7677                         //Outline items
7678                         $n = $this->n + 1;
7679                         foreach($this->outlines as $i => $o) {
7680                                 $this->_newobj();
7681                                 $this->_out('<</Title '.$this->_textstring($o['t']));
7682                                 $this->_out('/Parent '.($n+$o['parent']).' 0 R');
7683                                 if (isset($o['prev']))
7684                                 $this->_out('/Prev '.($n+$o['prev']).' 0 R');
7685                                 if (isset($o['next']))
7686                                 $this->_out('/Next '.($n+$o['next']).' 0 R');
7687                                 if (isset($o['first']))
7688                                 $this->_out('/First '.($n+$o['first']).' 0 R');
7689                                 if (isset($o['last']))
7690                                 $this->_out('/Last '.($n+$o['last']).' 0 R');
7691                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]', 1+2*$o['p'], ($this->h-$o['y'])*$this->k));
7692                                 $this->_out('/Count 0>>');
7693                                 $this->_out('endobj');
7694                         }
7695                         //Outline root
7696                         $this->_newobj();
7697                         $this->OutlineRoot=$this->n;
7698                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
7699                         $this->_out('/Last '.($n+$lru[0]).' 0 R>>');
7700                         $this->_out('endobj');
7701                 }
7702
7703
7704                 // --- JAVASCRIPT - FORMS ------------------------------
7705
7706                 /*
7707                 * Adds a javascript
7708                 * @access public
7709                 * @author Johannes Güntert, Nicola Asuni
7710                 * @since 2.1.002 (2008-02-12)
7711                 */
7712                 function IncludeJS($script) {
7713                         $this->javascript .= $script;
7714                 }
7715
7716                 /*
7717                 * Create a javascript PDF string.
7718                 * @access protected
7719                 * @author Johannes Güntert, Nicola Asuni
7720                 * @since 2.1.002 (2008-02-12)
7721                 */
7722                 function _putjavascript() {
7723                         if (empty($this->javascript)) {
7724                                 return;
7725                         }
7726                         // the following two lines are uded to avoid form fields duplication after saving
7727                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
7728                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
7729                         $this->_newobj();
7730                         $this->n_js = $this->n;
7731                         $this->_out('<<');
7732                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
7733                         $this->_out('>>');
7734                         $this->_out('endobj');
7735                         $this->_newobj();
7736                         $this->_out('<<');
7737                         $this->_out('/S /JavaScript');
7738                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
7739                         $this->_out('>>');
7740                         $this->_out('endobj');
7741                 }
7742
7743                 /*
7744                 * Convert color to javascript color.
7745                 * @param string $color color name or #RRGGBB
7746                 * @access protected
7747                 * @author Denis Van Nuffelen, Nicola Asuni
7748                 * @since 2.1.002 (2008-02-12)
7749                 */
7750                 function _JScolor($color) {
7751                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
7752                         if (substr($color,0,1) == '#') {
7753                                 return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
7754                         }
7755                         if (!in_array($color,$aColors)) {
7756                                 $this->Error('Invalid color: '.$color);
7757                         }
7758                         return 'color.'.$color;
7759                 }
7760
7761                 /*
7762                 * Adds a javascript form field.
7763                 * @param string $type field type
7764                 * @param string $name field name
7765                 * @param int $x horizontal position
7766                 * @param int $y vertical position
7767                 * @param int $w width
7768                 * @param int $h height
7769                 * @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>
7770                 * @access protected
7771                 * @author Denis Van Nuffelen, Nicola Asuni
7772                 * @since 2.1.002 (2008-02-12)
7773                 */
7774                 function _addfield($type, $name, $x, $y, $w, $h, $prop) {
7775                         // the followind avoid fields duplication after saving the document
7776                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
7777                         $k = $this->k;
7778                         $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";
7779                         $this->javascript .= "f".$name.".textSize=".$this->FontSizePt.";\n";
7780                         foreach ($prop as $key => $val) {
7781                                 if (strcmp(substr($key,-5),"Color") == 0) {
7782                                         $val = $this->_JScolor($val);
7783                                 } else {
7784                                         $val = "'".$val."'";
7785                                 }
7786                                 $this->javascript .= "f".$name.".".$key."=".$val.";\n";
7787                         }
7788                         $this->x += $w;
7789                         $this->javascript .= "}";
7790                 }
7791
7792                 /*
7793                 * Creates a text field
7794                 * @param string $name field name
7795                 * @param int $w width
7796                 * @param int $h height
7797                 * @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>
7798                 * @access public
7799                 * @author Denis Van Nuffelen, Nicola Asuni
7800                 * @since 2.1.002 (2008-02-12)
7801                 */
7802                 function TextField($name, $w, $h, $prop=array()) {
7803                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
7804                 }
7805
7806                 /*
7807                 * Creates a RadioButton field
7808                 * @param string $name field name
7809                 * @param int $w width
7810                 * @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>
7811                 * @access public
7812                 * @author Nicola Asuni
7813                 * @since 2.2.003 (2008-03-03)
7814                 */
7815                 function RadioButton($name, $w, $prop=array()) {
7816                         if (!isset($prop['strokeColor'])) {
7817                                 $prop['strokeColor']='black';
7818                         }
7819                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
7820                 }
7821
7822                 /*
7823                 * Creates a List-box field
7824                 * @param string $name field name
7825                 * @param int $w width
7826                 * @param int $h height
7827                 * @param array $values array containing the list of values.
7828                 * @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>
7829                 * @access public
7830                 * @author Nicola Asuni
7831                 * @since 2.2.003 (2008-03-03)
7832                 */
7833                 function ListBox($name, $w, $h, $values, $prop=array()) {
7834                         if (!isset($prop['strokeColor'])) {
7835                                 $prop['strokeColor'] = 'ltGray';
7836                         }
7837                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
7838                         $s = '';
7839                         foreach($values as $value) {
7840                                 $s .= "'".addslashes($value)."',";
7841                         }
7842                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7843                 }
7844
7845                 /*
7846                 * Creates a Combo-box field
7847                 * @param string $name field name
7848                 * @param int $w width
7849                 * @param int $h height
7850                 * @param array $values array containing the list of values.
7851                 * @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>
7852                 * @access public
7853                 * @author Denis Van Nuffelen, Nicola Asuni
7854                 * @since 2.1.002 (2008-02-12)
7855                 */
7856                 function ComboBox($name, $w, $h, $values, $prop=array()) {
7857                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
7858                         $s = '';
7859                         foreach($values as $value) {
7860                                 $s .= "'".addslashes($value)."',";
7861                         }
7862                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7863                 }
7864
7865                 /*
7866                 * Creates a CheckBox field
7867                 * @param string $name field name
7868                 * @param int $w width
7869                 * @param boolean $checked define the initial state (default = false).
7870                 * @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>
7871                 * @access public
7872                 * @author Denis Van Nuffelen, Nicola Asuni
7873                 * @since 2.1.002 (2008-02-12)
7874                 */
7875                 function CheckBox($name, $w, $checked=false, $prop=array()) {
7876                         $prop['value'] = ($checked ? 'Yes' : 'Off');
7877                         if (!isset($prop['strokeColor'])) {
7878                                 $prop['strokeColor'] = 'black';
7879                         }
7880                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
7881                 }
7882
7883                 /*
7884                 * Creates a button field
7885                 * @param string $name field name
7886                 * @param int $w width
7887                 * @param int $h height
7888                 * @param string $caption caption.
7889                 * @param string $action action triggered by the button (JavaScript code).
7890                 * @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>
7891                 * @access public
7892                 * @author Denis Van Nuffelen, Nicola Asuni
7893                 * @since 2.1.002 (2008-02-12)
7894                 */
7895                 function Button($name, $w, $h, $caption, $action, $prop=array()) {
7896                         if (!isset($prop['strokeColor'])) {
7897                                 $prop['strokeColor'] = 'black';
7898                         }
7899                         if (!isset($prop['borderStyle'])) {
7900                                 $prop['borderStyle'] = 'beveled';
7901                         }
7902                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
7903                         $this->javascript .= "f".$name.".buttonSetCaption('".addslashes($caption)."');\n";
7904                         $this->javascript .= "f".$name.".setAction('MouseUp','".addslashes($action)."');\n";
7905                         $this->javascript .= "f".$name.".highlight='push';\n";
7906                         $this->javascript .= "f".$name.".print=false;\n";
7907                 }
7908
7909                 // END JAVASCRIPT - FORMS ------------------------------
7910
7911                 /*
7912                 * Enable Write permissions for PDF Reader.
7913                 * @access protected
7914                 * @author Nicola Asuni
7915                 * @since 2.9.000 (2008-03-26)
7916                 */
7917                 function _putuserrights() {
7918                         if (!$this->ur) {
7919                                 return;
7920                         }
7921                         $this->_out('/Perms');
7922                         $this->_out('<<');
7923                         $this->_out('/UR3');
7924                         $this->_out('<<');
7925                         //$this->_out('/SubFilter/adbe.pkcs7.detached/Filter/Adobe.PPKLite/Contents');
7926                         //$this->_out('<0>');
7927                         //$this->_out('/ByteRange[0 3]');
7928                         $this->_out('/M '.$this->_datestring('D:'.date('YmdHis')));
7929                         $this->_out('/Name(TCPDF)');
7930                         $this->_out('/Reference[');
7931                         $this->_out('<<');
7932                         $this->_out('/TransformParams');
7933                         $this->_out('<<');
7934                         $this->_out('/Type/TransformParams');
7935                         $this->_out('/V/2.2');
7936                         if (!empty($this->ur_document)) {
7937                                 $this->_out('/Document['.$this->ur_document.']');
7938                         }
7939                         if (!empty($this->ur_annots)) {
7940                                 $this->_out('/Annots['.$this->ur_annots.']');
7941                         }
7942                         if (!empty($this->ur_form)) {
7943                                 $this->_out('/Form['.$this->ur_form.']');
7944                         }
7945                         if (!empty($this->ur_signature)) {
7946                                 $this->_out('/Signature['.$this->ur_signature.']');
7947                         }
7948                         $this->_out('>>');
7949                         $this->_out('/TransformMethod/UR3');
7950                         $this->_out('/Type/SigRef');
7951                         $this->_out('>>');
7952                         $this->_out(']');
7953                         $this->_out('/Type/Sig');
7954                         $this->_out('>>');
7955                         $this->_out('>>');
7956                 }
7957
7958                 /*
7959                 * Set User's Rights for PDF Reader
7960                 * Check the PDF Reference 8.7.1 Transform Methods,
7961                 * Table 8.105 Entries in the UR transform parameters dictionary
7962                 * @param boolean $enable if true enable user's rights on PDF reader
7963                 * @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.
7964                 * @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.
7965                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
7966                 * @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.
7967                 * @access public
7968                 * @author Nicola Asuni
7969                 * @since 2.9.000 (2008-03-26)
7970                 */
7971                 function setUserRights(
7972                                 $enable=true,
7973                                 $document="/FullSave",
7974                                 $annots="/Create/Delete/Modify/Copy/Import/Export",
7975                                 $form="/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate",
7976                                 $signature="/Modify") {
7977                         $this->ur = $enable;
7978                         $this->ur_document = $document;
7979                         $this->ur_annots = $annots;
7980                         $this->ur_form = $form;
7981                         $this->ur_signature = $signature;
7982                 }
7983
7984                 /*
7985                 * Create a new page group.
7986                 * NOTE: call this function before calling AddPage()
7987                 * @access public
7988                 * @since 3.0.000 (2008-03-27)
7989                 */
7990                 function startPageGroup() {
7991                         $this->newpagegroup = true;
7992                 }
7993
7994                 /*
7995                 * Return the current page in the group.
7996                 * @return current page in the group
7997                 * @access public
7998                 * @since 3.0.000 (2008-03-27)
7999                 */
8000                 function getGroupPageNo() {
8001                         return $this->pagegroups[$this->currpagegroup];
8002                 }
8003
8004                 /*
8005                 * Return the alias of the current page group
8006         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
8007                 * (will be replaced by the total number of pages in this group).
8008                 * @return alias of the current page group
8009                 * @access public
8010                 * @since 3.0.000 (2008-03-27)
8011                 */
8012                 function getPageGroupAlias() {
8013                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
8014                                 return "{".$this->currpagegroup."}";
8015             }
8016                         return $this->currpagegroup;
8017                 }
8018
8019                 /*
8020                 * Put visibility settings.
8021                 * @access protected
8022                 * @since 3.0.000 (2008-03-27)
8023                 */
8024                 function _putocg() {
8025                         $this->_newobj();
8026                         $this->n_ocg_print = $this->n;
8027                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
8028                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
8029                         $this->_out('endobj');
8030                         $this->_newobj();
8031                         $this->n_ocg_view=$this->n;
8032                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
8033                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
8034                         $this->_out('endobj');
8035                 }
8036
8037                 /*
8038                 * Set the visibility of the successive elements.
8039                 * This can be useful, for instance, to put a background
8040                 * image or color that will show on screen but won't print.
8041                 * @param string $v visibility mode. Legal values are: all, print, screen.
8042                 * @access public
8043                 * @since 3.0.000 (2008-03-27)
8044                 */
8045                 function setVisibility($v) {
8046                         if ($this->openMarkedContent) {
8047                                 // close existing open marked-content
8048                                 $this->_out('EMC');
8049                                 $this->openMarkedContent = false;
8050                         }
8051                         switch($v) {
8052                                 case "print": {
8053                                         $this->_out('/OC /OC1 BDC');
8054                                         $this->openMarkedContent = true;
8055                                         break;
8056                                 }
8057                                 case "screen": {
8058                                         $this->_out('/OC /OC2 BDC');
8059                                         $this->openMarkedContent = true;
8060                                         break;
8061                                 }
8062                                 case "all": {
8063                                         $this->_out('');
8064                                         break;
8065                                 }
8066                                 default: {
8067                                         $this->Error('Incorrect visibility: '.$v);
8068                                         break;
8069                                 }
8070                         }
8071                         $this->visibility = $v;
8072                 }
8073
8074                 /*
8075                 * Add transparency parameters to the current extgstate
8076                 * @param array $params parameters
8077                 * @return the number of extgstates
8078                 * @access protected
8079                 * @since 3.0.000 (2008-03-27)
8080                 */
8081                 function addExtGState($parms) {
8082                         $n = count($this->extgstates) + 1;
8083                         $this->extgstates[$n]['parms'] = $parms;
8084                         return $n;
8085                 }
8086
8087                 /*
8088                 * Add an extgstate
8089                 * @param array $gs extgstate
8090                 * @access protected
8091                 * @since 3.0.000 (2008-03-27)
8092                 */
8093                 function setExtGState($gs) {
8094                         $this->_out(sprintf('/GS%d gs', $gs));
8095                 }
8096
8097                 /*
8098                 * Put extgstates for object transparency
8099                 * @param array $gs extgstate
8100                 * @access protected
8101                 * @since 3.0.000 (2008-03-27)
8102                 */
8103                 function _putextgstates() {
8104                         $ne = count($this->extgstates);
8105                         for ($i = 1; $i <= $ne; $i++) {
8106                                 $this->_newobj();
8107                                 $this->extgstates[$i]['n'] = $this->n;
8108                                 $this->_out('<</Type /ExtGState');
8109                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
8110                                         $this->_out('/'.$k.' '.$v);
8111                                 }
8112                                 $this->_out('>>');
8113                                 $this->_out('endobj');
8114                         }
8115                 }
8116
8117                 /*
8118                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
8119                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
8120                 * @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
8121                 * @access public
8122                 * @since 3.0.000 (2008-03-27)
8123                 */
8124                 function setAlpha($alpha, $bm='Normal') {
8125                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
8126                         $this->setExtGState($gs);
8127                 }
8128
8129                 /*
8130                 * Set the default JPEG compression quality (1-100)
8131                 * @param int $quality JPEG quality, integer between 1 and 100
8132                 * @access public
8133                 * @since 3.0.000 (2008-03-27)
8134                 */
8135                 function setJPEGQuality($quality) {
8136                         if (($quality < 1) OR ($quality > 100)) {
8137                                 $quality = 75;
8138                         }
8139                         $this->jpeg_quality = intval($quality);
8140                 }
8141
8142                 /*
8143                 * Set the default number of columns in a row for HTML tables.
8144                 * @param int $cols number of columns
8145                 * @access public
8146                 * @since 3.0.014 (2008-06-04)
8147                 */
8148                 function setDefaultTableColumns($cols=4) {
8149                         $this->default_table_columns = intval($cols);
8150                 }
8151
8152                 /*
8153                 * Set the height of cell repect font height.
8154                 * @param int $h cell proportion respect font height (typical value = 1.25).
8155                 * @access public
8156                 * @since 3.0.014 (2008-06-04)
8157                 */
8158                 function setCellHeightRatio($h) {
8159                         $this->cell_height_ratio = $h;
8160                 }
8161
8162                 /*
8163                 * return the height of cell repect font height.
8164                 * @access public
8165                 * @since 4.0.012 (2008-07-24)
8166                 */
8167                 function getCellHeightRatio() {
8168                         return $this->cell_height_ratio;
8169                 }
8170
8171                 /*
8172                 * Set the PDF version (check PDF reference for valid values).
8173                 * Default value is 1.t
8174                 * @access public
8175                 * @since 3.1.000 (2008-06-09)
8176                 */
8177                 function setPDFVersion($version="1.7") {
8178                         $this->PDFVersion = $version;
8179                 }
8180
8181                 /*
8182                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
8183                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
8184                 * <ul>
8185                 * <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>
8186                 * <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>
8187                 * <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>
8188                 * <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>
8189                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
8190                 * <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>
8191                 * <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>
8192                 * <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>
8193                 * <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>
8194                 * <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>
8195                 * <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>
8196                 * <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>
8197                 * <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>
8198                 * <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>
8199                 * <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>
8200                 * <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>
8201                 * </ul>
8202                 * @param array $preferences array of options.
8203                 * @author Nicola Asuni
8204                 * @access public
8205                 * @since 3.1.000 (2008-06-09)
8206                 */
8207                 function setViewerPreferences($preferences) {
8208                         $this->viewer_preferences = $preferences;
8209                 }
8210
8211                 /**
8212                 * Paints a linear colour gradient.
8213                 * @param float $x abscissa of the top left corner of the rectangle.
8214                 * @param float $y ordinate of the top left corner of the rectangle.
8215                 * @param float $w width of the rectangle.
8216                 * @param float $h height of the rectangle.
8217                 * @param array $col1 first color (RGB components).
8218                 * @param array $col2 second color (RGB components).
8219                 * @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).
8220                 * @author Andreas Würmser, Nicola Asuni
8221                 * @since 3.1.000 (2008-06-09)
8222                 * @access public
8223                 */
8224                 function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
8225                         $this->Clip($x, $y, $w, $h);
8226                         $this->Gradient(2, $col1, $col2, $coords);
8227                 }
8228
8229                 /**
8230                 * Paints a radial colour gradient.
8231                 * @param float $x abscissa of the top left corner of the rectangle.
8232                 * @param float $y ordinate of the top left corner of the rectangle.
8233                 * @param float $w width of the rectangle.
8234                 * @param float $h height of the rectangle.
8235                 * @param array $col1 first color (RGB components).
8236                 * @param array $col2 second color (RGB components).
8237                 * @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.
8238                 * @author Andreas Würmser, Nicola Asuni
8239                 * @since 3.1.000 (2008-06-09)
8240                 * @access public
8241                 */
8242                 function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
8243                         $this->Clip($x, $y, $w, $h);
8244                         $this->Gradient(3, $col1, $col2, $coords);
8245                 }
8246
8247                 /**
8248                 * Paints a coons patch mesh.
8249                 * @param float $x abscissa of the top left corner of the rectangle.
8250                 * @param float $y ordinate of the top left corner of the rectangle.
8251                 * @param float $w width of the rectangle.
8252                 * @param float $h height of the rectangle.
8253                 * @param array $col1 first color (lower left corner) (RGB components).
8254                 * @param array $col2 second color (lower right corner) (RGB components).
8255                 * @param array $col3 third color (upper right corner) (RGB components).
8256                 * @param array $col4 fourth color (upper left corner) (RGB components).
8257                 * @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>
8258                 * @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
8259                 * @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
8260                 * @author Andreas Würmser, Nicola Asuni
8261                 * @since 3.1.000 (2008-06-09)
8262                 * @access public
8263                 */
8264                 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) {
8265                         $this->Clip($x, $y, $w, $h);
8266                         $n = count($this->gradients) + 1;
8267                         $this->gradients[$n]['type'] = 6; //coons patch mesh
8268                         //check the coords array if it is the simple array or the multi patch array
8269                         if (!isset($coords[0]['f'])){
8270                                 //simple array -> convert to multi patch array
8271                                 if (!isset($col1[1])) {
8272                                         $col1[1] = $col1[2] = $col1[0];
8273                                 }
8274                                 if (!isset($col2[1])) {
8275                                         $col2[1] = $col2[2] = $col2[0];
8276                                 }
8277                                 if (!isset($col3[1])) {
8278                                         $col3[1] = $col3[2] = $col3[0];
8279                                 }
8280                                 if (!isset($col4[1])) {
8281                                         $col4[1] = $col4[2] = $col4[0];
8282                                 }
8283                                 $patch_array[0]['f'] = 0;
8284                                 $patch_array[0]['points'] = $coords;
8285                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
8286                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
8287                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
8288                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
8289                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
8290                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
8291                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
8292                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
8293                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
8294                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
8295                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
8296                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
8297                         } else {
8298                                 //multi patch array
8299                                 $patch_array = $coords;
8300                         }
8301                         $bpcd = 65535; //16 BitsPerCoordinate
8302                         //build the data stream
8303                         $this->gradients[$n]['stream'] = "";
8304                         for($i=0; $i < count($patch_array); $i++) {
8305                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
8306                                 for($j=0; $j < count($patch_array[$i]['points']); $j++) {
8307                                         //each point as 16 bit
8308                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j]-$coords_min)/($coords_max-$coords_min))*$bpcd;
8309                                         if ($patch_array[$i]['points'][$j] < 0) {
8310                                                 $patch_array[$i]['points'][$j] = 0;
8311                                         }
8312                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
8313                                                 $patch_array[$i]['points'][$j] = $bpcd;
8314                                         }
8315                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]/256));
8316                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]%256));
8317                                 }
8318                                 for($j=0; $j < count($patch_array[$i]['colors']); $j++) {
8319                                         //each color component as 8 bit
8320                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
8321                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
8322                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
8323                                 }
8324                         }
8325                         //paint the gradient
8326                         $this->_out('/Sh'.$n.' sh');
8327                         //restore previous Graphic State
8328                         $this->_out('Q');
8329                 }
8330
8331                 /**
8332                 * Set a rectangular clipping area.
8333                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
8334                 * @param float $y ordinate of the top left corner of the rectangle.
8335                 * @param float $w width of the rectangle.
8336                 * @param float $h height of the rectangle.
8337                 * @author Andreas Würmser, Nicola Asuni
8338                 * @since 3.1.000 (2008-06-09)
8339                 * @access protected
8340                 */
8341                 function Clip($x, $y, $w, $h){
8342                         if ($this->rtl) {
8343                                 $x = $this->w - $x - $w;
8344                         }
8345                         //save current Graphic State
8346                         $s = 'q';
8347                         //set clipping area
8348                         $s .= sprintf(' %.2f %.2f %.2f %.2f re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
8349                         //set up transformation matrix for gradient
8350                         $s .= sprintf(' %.3f 0 0 %.3f %.3f %.3f cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
8351                         $this->_out($s);
8352                 }
8353
8354                 /**
8355                 * Output gradient.
8356                 * @param int $type type of gradient.
8357                 * @param array $col1 first color (RGB components).
8358                 * @param array $col2 second color (RGB components).
8359                 * @param array $coords array of coordinates.
8360                 * @author Andreas Würmser, Nicola Asuni
8361                 * @since 3.1.000 (2008-06-09)
8362                 * @access protected
8363                 */
8364                 function Gradient($type, $col1, $col2, $coords){
8365                         $n = count($this->gradients) + 1;
8366                         $this->gradients[$n]['type'] = $type;
8367                         if (!isset($col1[1])) {
8368                                 $col1[1]=$col1[2]=$col1[0];
8369                         }
8370                         $this->gradients[$n]['col1'] = sprintf('%.3f %.3f %.3f', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
8371                         if (!isset($col2[1])) {
8372                                 $col2[1] = $col2[2] = $col2[0];
8373                         }
8374                         $this->gradients[$n]['col2'] = sprintf('%.3f %.3f %.3f', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
8375                         $this->gradients[$n]['coords'] = $coords;
8376                         //paint the gradient
8377                         $this->_out('/Sh'.$n.' sh');
8378                         //restore previous Graphic State
8379                         $this->_out('Q');
8380                 }
8381
8382                 /**
8383                 * Output shaders.
8384                 * @author Andreas Würmser, Nicola Asuni
8385                 * @since 3.1.000 (2008-06-09)
8386                 * @access protected
8387                 */
8388                 function _putshaders() {
8389                         foreach($this->gradients as $id => $grad) {
8390                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
8391                                         $this->_newobj();
8392                                         $this->_out('<<');
8393                                         $this->_out('/FunctionType 2');
8394                                         $this->_out('/Domain [0.0 1.0]');
8395                                         $this->_out('/C0 ['.$grad['col1'].']');
8396                                         $this->_out('/C1 ['.$grad['col2'].']');
8397                                         $this->_out('/N 1');
8398                                         $this->_out('>>');
8399                                         $this->_out('endobj');
8400                                         $f1 = $this->n;
8401                                 }
8402                                 $this->_newobj();
8403                                 $this->_out('<<');
8404                                 $this->_out('/ShadingType '.$grad['type']);
8405                                 $this->_out('/ColorSpace /DeviceRGB');
8406                                 if ($grad['type'] == 2) {
8407                                         $this->_out(sprintf('/Coords [%.3f %.3f %.3f %.3f]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
8408                                         $this->_out('/Function '.$f1.' 0 R');
8409                                         $this->_out('/Extend [true true] ');
8410                                         $this->_out('>>');
8411                                 } elseif ($grad['type'] == 3) {
8412                                         //x0, y0, r0, x1, y1, r1
8413                                         //at this this time radius of inner circle is 0
8414                                         $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]));
8415                                         $this->_out('/Function '.$f1.' 0 R');
8416                                         $this->_out('/Extend [true true] ');
8417                                         $this->_out('>>');
8418                                 } elseif ($grad['type'] == 6) {
8419                                         $this->_out('/BitsPerCoordinate 16');
8420                                         $this->_out('/BitsPerComponent 8');
8421                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
8422                                         $this->_out('/BitsPerFlag 8');
8423                                         $this->_out('/Length '.strlen($grad['stream']));
8424                                         $this->_out('>>');
8425                                         $this->_putstream($grad['stream']);
8426                                 }
8427                                 $this->_out('endobj');
8428                                 $this->gradients[$id]['id'] = $this->n;
8429                         }
8430                 }
8431
8432                 /**
8433                 * Output an arc
8434                 * @author Maxime Delorme, Nicola Asuni
8435                 * @since 3.1.000 (2008-06-09)
8436                 * @access protected
8437                 */
8438                 function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
8439                         $h = $this->h;
8440                         $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));
8441                 }
8442
8443                 /**
8444                 * Draw the sector of a circle.
8445                 * It can be used for instance to render pie charts.
8446                 * @param float $xc abscissa of the center.
8447                 * @param float $yc ordinate of the center.
8448                 * @param float $r radius.
8449                 * @param float $a start angle (in degrees).
8450                 * @param float $b end angle (in degrees).
8451                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
8452                 * @param float $cw: indicates whether to go clockwise (default: true).
8453                 * @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.
8454                 * @author Maxime Delorme, Nicola Asuni
8455                 * @since 3.1.000 (2008-06-09)
8456                 * @access public
8457                 */
8458                 function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
8459                         if ($this->rtl) {
8460                                 $xc = $this->w - $xc - $w;
8461                         }
8462                         if ($cw) {
8463                                 $d = $b;
8464                                 $b = $o - $a;
8465                                 $a = $o - $d;
8466                         } else {
8467                                 $b += $o;
8468                                 $a += $o;
8469                         }
8470                         $a = ($a % 360) + 360;
8471                         $b = ($b % 360) + 360;
8472                         if ($a > $b) {
8473                                 $b +=360;
8474                         }
8475                         $b = $b / 360 * 2 * M_PI;
8476                         $a = $a / 360 * 2 * M_PI;
8477                         $d = $b - $a;
8478                         if ($d == 0 ) {
8479                                 $d = 2 * M_PI;
8480                         }
8481                         $k = $this->k;
8482                         $hp = $this->h;
8483                         if ($style=='F') {
8484                                 $op = 'f';
8485                         } elseif ($style=='FD' or $style=='DF') {
8486                                 $op = 'b';
8487                         } else {
8488                                 $op = 's';
8489                         }
8490                         if (sin($d/2)) {
8491                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
8492                         }
8493                         //first put the center
8494                         $this->_out(sprintf('%.2f %.2f m', ($xc)*$k, ($hp-$yc)*$k));
8495                         //put the first point
8496                         $this->_out(sprintf('%.2f %.2f l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
8497                         //draw the arc
8498                         if ($d < (M_PI/2)) {
8499                                 $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));
8500                         } else {
8501                                 $b = $a + $d/4;
8502                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
8503                                 $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));
8504                                 $a = $b;
8505                                 $b = $a + $d/4;
8506                                 $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));
8507                                 $a = $b;
8508                                 $b = $a + $d/4;
8509                                 $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) );
8510                                 $a = $b;
8511                                 $b = $a + $d/4;
8512                                 $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));
8513                         }
8514                         //terminate drawing
8515                         $this->_out($op);
8516                 }
8517
8518                 /**
8519                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
8520                 * Only vector drawing is supported, not text or bitmap.
8521                 * 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).
8522                 * @param string $file Name of the file containing the image.
8523                 * @param float $x Abscissa of the upper-left corner.
8524                 * @param float $y Ordinate of the upper-left corner.
8525                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
8526                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
8527                 * @param mixed $link URL or identifier returned by AddLink().
8528                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
8529                 * @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>
8530                 * @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>
8531                 * @author Valentin Schmidt, Nicola Asuni
8532                 * @since 3.1.000 (2008-06-09)
8533                 * @access public
8534                 */
8535                 function ImageEps($file, $x, $y, $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='') {
8536                         $data = file_get_contents($file);
8537                         if ($data === false) {
8538                                 $this->Error('EPS file not found: '.$file);
8539                         }
8540                         $regs = array();
8541                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
8542                         preg_match ('/%%Creator:([^\r\n]+)/', $data, $regs); # find Creator
8543                         if (count($regs) > 1) {
8544                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
8545                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
8546                                         $versexp = explode(' ', $version_str);
8547                                         $version = (float)array_pop($versexp);
8548                                         if ($version >= 9) {
8549                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
8550                                         }
8551                                 }
8552                         }
8553                         // strip binary bytes in front of PS-header
8554                         $start = strpos($data, '%!PS-Adobe');
8555                         if ($start > 0) {
8556                                 $data = substr($data, $start);
8557                         }
8558                         // find BoundingBox params
8559                         preg_match ("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
8560                         if (count($regs) > 1) {
8561                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
8562                         } else {
8563                                 $this->Error('No BoundingBox found in EPS file: '.$file);
8564                         }
8565                         $start = strpos($data, '%%EndSetup');
8566                         if ($start === false) {
8567                                 $start = strpos($data, '%%EndProlog');
8568                         }
8569                         if ($start === false) {
8570                                 $start = strpos($data, '%%BoundingBox');
8571                         }
8572                         $data = substr($data, $start);
8573                         $end = strpos($data, '%%PageTrailer');
8574                         if ($end===false) {
8575                                 $end = strpos($data, 'showpage');
8576                         }
8577                         if ($end) {
8578                                 $data = substr($data, 0, $end);
8579                         }
8580                         $k = $this->k;
8581                         if ($w > 0) {
8582                                 $scale_x = $w/(($x2-$x1)/$k);
8583                                 if ($h > 0) {
8584                                         $scale_y = $h/(($y2-$y1)/$k);
8585                                 } else {
8586                                         $scale_y = $scale_x;
8587                                         $h = ($y2-$y1)/$k * $scale_y;
8588                                 }
8589                         } else {
8590                                 if ($h > 0) {
8591                                         $scale_y = $h/(($y2-$y1)/$k);
8592                                         $scale_x = $scale_y;
8593                                         $w = ($x2-$x1)/$k * $scale_x;
8594                                 } else {
8595                                         $w = ($x2 - $x1) / $k;
8596                                         $h = ($y2 - $y1) / $k;
8597                                 }
8598                         }
8599                         // Check whether we need a new page first as this does not fit
8600                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
8601                                 // Automatic page break
8602                                 $this->AddPage($this->CurOrientation);
8603                                 // Reset coordinates to top fo next page
8604                                 //$x = $this->GetX();
8605                                 $y = $this->GetY() + $this->cMargin;
8606                         }
8607                         // set bottomcoordinates
8608                         $this->img_rb_y = $y + $h;
8609                         // set alignment
8610                         if ($this->rtl) {
8611                                 if ($palign == 'L') {
8612                                         $ximg = $this->lMargin;
8613                                         // set right side coordinate
8614                                         $this->img_rb_x = $ximg + $w;
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 = $this->w - $x - $w;
8621                                         // set left side coordinate
8622                                         $this->img_rb_x = $ximg;
8623                                 }
8624                         } else {
8625                                 if ($palign == 'R') {
8626                                         $ximg = $this->w - $this->rMargin - $w;
8627                                         // set left side coordinate
8628                                         $this->img_rb_x = $ximg;
8629                                 } elseif ($palign == 'C') {
8630                                         $ximg = ($this->w - $x - $w) / 2;
8631                                         // set right side coordinate
8632                                         $this->img_rb_x = $ximg + $w;
8633                                 } else {
8634                                         $ximg = $x;
8635                                         // set right side coordinate
8636                                         $this->img_rb_x = $ximg + $w;
8637                                 }
8638                         }
8639                         if ($useBoundingBox){
8640                                 $dx = $ximg * $k - $x1;
8641                                 $dy = $y * $k - $y1;
8642                         } else {
8643                                 $dx = $ximg * $k;
8644                                 $dy = $y * $k;
8645                         }
8646                         // save the current graphic state
8647                         $this->_out('q');
8648                         // translate
8649                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy+($this->hPt - 2*$y*$k - ($y2-$y1))));
8650                         // scale
8651                         if (isset($scale_x)) {
8652                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1*(1-$scale_x), $y2*(1-$scale_y)));
8653                         }
8654                         // handle pc/unix/mac line endings
8655                         $lines = preg_split("/\r\n|[\r\n]/", $data);
8656                         $u=0;
8657                         $cnt = count($lines);
8658                         for ($i=0; $i < $cnt; $i++) {
8659                                 $line = $lines[$i];
8660                                 if (($line == '') OR ($line[0] == '%')) {
8661                                         continue;
8662                                 }
8663                                 $len = strlen($line);
8664                                 $chunks = explode(' ', $line);
8665                                 $cmd = array_pop($chunks);
8666                                 // RGB
8667                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
8668                                         $b = array_pop($chunks);
8669                                         $g = array_pop($chunks);
8670                                         $r = array_pop($chunks);
8671                                         $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!
8672                                         continue;
8673                                 }
8674                                 switch ($cmd) {
8675                                         case 'm':
8676                                         case 'l':
8677                                         case 'v':
8678                                         case 'y':
8679                                         case 'c':
8680                                         case 'k':
8681                                         case 'K':
8682                                         case 'g':
8683                                         case 'G':
8684                                         case 's':
8685                                         case 'S':
8686                                         case 'J':
8687                                         case 'j':
8688                                         case 'w':
8689                                         case 'M':
8690                                         case 'd':
8691                                         case 'n':
8692                                         case 'v': {
8693                                                 $this->_out($line);
8694                                                 break;
8695                                         }
8696                                         case 'x': {// custom fill color
8697                                                 list($c,$m,$y,$k) = $chunks;
8698                                                 $this->_out("$c $m $y $k k");
8699                                                 break;
8700                                         }
8701                                         case 'X': { // custom stroke color
8702                                                 list($c,$m,$y,$k) = $chunks;
8703                                                 $this->_out("$c $m $y $k K");
8704                                                 break;
8705                                         }
8706                                         case 'Y':
8707                                         case 'N':
8708                                         case 'V':
8709                                         case 'L':
8710                                         case 'C': {
8711                                                 $line[$len-1] = strtolower($cmd);
8712                                                 $this->_out($line);
8713                                                 break;
8714                                         }
8715                                         case 'b':
8716                                         case 'B': {
8717                                                 $this->_out($cmd . '*');
8718                                                 break;
8719                                         }
8720                                         case 'f':
8721                                         case 'F': {
8722                                                 if ($u > 0) {
8723                                                         $isU = false;
8724                                                         $max = min($i+5, $cnt);
8725                                                         for ($j=$i+1; $j < $max; $j++)
8726                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
8727                                                         if ($isU) {
8728                                                                 $this->_out("f*");
8729                                                         }
8730                                                 } else {
8731                                                         $this->_out("f*");
8732                                                 }
8733                                                 break;
8734                                         }
8735                                         case '*u': {
8736                                                 $u++;
8737                                                 break;
8738                                         }
8739                                         case '*U': {
8740                                                 $u--;
8741                                                 break;
8742                                         }
8743                                 }
8744                         }
8745                         // restore previous graphic state
8746                         $this->_out('Q');
8747                         if ($link) {
8748                                 $this->Link($ximg, $y, $w, $h, $link);
8749                         }
8750                         // set pointer to align the successive text/objects
8751                         switch($align) {
8752                                 case 'T':{
8753                                         $this->y = $y;
8754                                         $this->x = $this->img_rb_x;
8755                                         break;
8756                                 }
8757                                 case 'M':{
8758                                         $this->y = $y + round($h/2);
8759                                         $this->x = $this->img_rb_x;
8760                                         break;
8761                                 }
8762                                 case 'B':{
8763                                         $this->y = $this->img_rb_y;
8764                                         $this->x = $this->img_rb_x;
8765                                         break;
8766                                 }
8767                                 case 'N':{
8768                                         $this->SetY($this->img_rb_y);
8769                                         break;
8770                                 }
8771                                 default:{
8772                                         break;
8773                                 }
8774                         }
8775                         $this->endlinex = $this->img_rb_x;
8776                 }
8777
8778                 /**
8779                  * Set document barcode.
8780                  * @param string $bc barcode
8781                  */
8782                 function setBarcode($bc="") {
8783                         $this->barcode = $bc;
8784                 }
8785
8786                 /**
8787                  * Get current barcode.
8788                  * @return string
8789                  * @since 4.0.012 (2008-07-24)
8790                  */
8791                 function getBarcode() {
8792                         return $this->barcode;
8793                 }
8794
8795                 /**
8796                  * Print Barcode.
8797                  * @param string $code code to print
8798                  * @param string $type type of barcode.
8799                  * @param int $x x position in user units
8800                  * @param int $y y position in user units
8801                  * @param int $w width in user units
8802                  * @param int $h height position in user units
8803                  * @param float $xres width of the smallest bar in user units
8804                  * @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>
8805                  * @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>
8806                  * @author Nicola Asuni
8807                  * @since 3.1.000 (2008-06-09)
8808                  * @access public
8809                  */
8810                 function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
8811                         if (empty($code)) {
8812                                 return;
8813                         }
8814                         $barcodeobj = new TCPDFbarcode($code, $type);
8815                         $arrcode = $barcodeobj->getBarcodeArray();
8816                         if ($arrcode === false) {
8817                                 $this->Error('Error in barcode string');
8818                         }
8819                         // set default values
8820                         if (!isset($style["position"])) {
8821                                 if ($this->rtl) {
8822                                         $style["position"] = "R";
8823                                 } else {
8824                                         $style["position"] = "L";
8825                                 }
8826                         }
8827                         if (!isset($style["padding"])) {
8828                                 $style["padding"] = 0;
8829                         }
8830                         if (!isset($style["fgcolor"])) {
8831                                 $style["fgcolor"] = array(0,0,0); // default black
8832                         }
8833                         if (!isset($style["bgcolor"])) {
8834                                 $style["bgcolor"] = false; // default transparent
8835                         }
8836                         if (!isset($style["border"])) {
8837                                 $style["border"] = false;
8838                         }
8839                         if (!isset($style["text"])) {
8840                                 $style["text"] = false;
8841                                 $fontsize = 0;
8842                         }
8843                         if ($style["text"] AND isset($style["font"])) {
8844                                 $prevFontFamily = $this->FontFamily;
8845                                 $prevFontStyle = $this->FontStyle;
8846                                 $prevFontSizePt = $this->FontSizePt;
8847                                 if (isset($style["fontsize"])) {
8848                                         $fontsize = $style["fontsize"];
8849                                 } else {
8850                                         $fontsize = 0;
8851                                 }
8852                                 $this->SetFont($style["font"], '', $fontsize);
8853                         }
8854                         if (!isset($style["stretchtext"])) {
8855                                 $style["stretchtext"] = 4;
8856                         }
8857                         // set foreground color
8858                         $prevDrawColor = $this->DrawColor;
8859                         $prevTextColor = $this->TextColor;
8860                         $this->SetDrawColorArray($style["fgcolor"]);
8861                         $this->SetTextColorArray($style["fgcolor"]);
8862                         if (empty($w) OR ($w <= 0)) {
8863                                 if ($this->rtl) {
8864                                         $w = $this->x - $this->lMargin;
8865                                 } else {
8866                                         $w = $this->w - $this->rMargin - $this->x;
8867                                 }
8868                         }
8869                         if (empty($x)) {
8870                                 $x = $this->GetX();
8871                         }
8872                         if ($this->rtl) {
8873                                 $x = $this->w - $x;
8874                         }
8875                         if (empty($y)) {
8876                                 $y = $this->GetY();
8877                         }
8878                         if (empty($xres)) {
8879                                 $xres = 0.4;
8880                         }
8881                         $fbw = ($arrcode["maxw"] * $xres) + (2 * $style["padding"]);
8882                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style["padding"]);
8883                         if (empty($h)) {
8884                                 $h = 10 + $extraspace;
8885                         }
8886                         if ((($y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
8887                                 //Automatic page break
8888                                 $x = $this->x;
8889                                 $ws = $this->ws;
8890                                 if ($ws > 0) {
8891                                         $this->ws = 0;
8892                                         $this->_out('0 Tw');
8893                                 }
8894                                 $this->AddPage($this->CurOrientation);
8895                                 if ($ws > 0) {
8896                                         $this->ws = $ws;
8897                                         $this->_out(sprintf('%.3f Tw',$ws * $k));
8898                                 }
8899                                 $this->x = $x;
8900                                 $y = $this->y;
8901                         }
8902                         // maximum bar heigth
8903                         $barh = $h - $extraspace;
8904                         switch ($style["position"]) {
8905                                 case "L": { // left
8906                                         if ($this->rtl) {
8907                                                 $xpos = $x - $w;
8908                                         } else {
8909                                                 $xpos = $x;
8910                                         }
8911                                         break;
8912                                 }
8913                                 case "C": { // center
8914                                         $xdiff = (($w - $fbw) / 2);
8915                                         if ($this->rtl) {
8916                                                 $xpos = $x - $w + $xdiff;
8917                                         } else {
8918                                                 $xpos = $x + $xdiff;
8919                                         }
8920                                         break;
8921                                 }
8922                                 case "R": { // right
8923                                         if ($this->rtl) {
8924                                                 $xpos = $x - $fbw;
8925                                         } else {
8926                                                 $xpos = $x + $w - $fbw;
8927                                         }
8928                                         break;
8929                                 }
8930                                 case "S": { // stretch
8931                                         $fbw = $w;
8932                                         $xres = ($w - (2 * $style["padding"])) / $arrcode["maxw"];
8933                                         if ($this->rtl) {
8934                                                 $xpos = $x - $w;
8935                                         } else {
8936                                                 $xpos = $x;
8937                                         }
8938                                         break;
8939                                 }
8940                         }
8941                         $xpos_rect = $xpos;
8942                         $xpos = $xpos_rect + $style["padding"];
8943                         $xpos_text = $xpos;
8944                         // barcode is always printed in LTR direction
8945                         $tempRTL = $this->rtl;
8946                         $this->rtl = false;
8947                         // print background color
8948                         if ($style["bgcolor"]) {
8949                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style["bgcolor"]);
8950                         } elseif ($style["border"]) {
8951                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
8952                         }
8953                         // print bars
8954                         if ($arrcode !== false) {
8955                                 foreach ($arrcode["bcode"] as $k => $v) {
8956                                         $bw = ($v["w"] * $xres);
8957                                         if ($v["t"]) {
8958                                                 // braw a vertical bar
8959                                                 $ypos = $y + $style["padding"] + ($v["p"] * $barh / $arrcode["maxh"]);
8960                                                 $this->Rect($xpos, $ypos, $bw, ($v["h"] * $barh  / $arrcode["maxh"]), 'DF', array("L"=>0,"T"=>0,"R"=>0,"B"=>0), $style["fgcolor"]);
8961                                         }
8962                                         $xpos += $bw;
8963                                 }
8964                         }
8965                         // print text
8966                         if ($style["text"]) {
8967                                 // print text
8968                                 $this->x = $xpos_text;
8969                                 $this->y = $y + $style["padding"] + $barh;
8970                                 $this->Cell(($arrcode["maxw"] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style["stretchtext"]);
8971                         }
8972                         // restore original direction
8973                         $this->rtl = $tempRTL;
8974                         // restore previous font
8975                         if ($style["text"] AND isset($style["font"])) {
8976                                 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
8977                         }
8978                         // restore colors
8979                         $this->DrawColor = $prevDrawColor;
8980                         $this->TextColor = $prevTextColor;
8981                         // set bottomcoordinates
8982                         $this->img_rb_y = $y + $h;
8983                         if ($this->rtl) {
8984                                 // set left side coordinate
8985                                 $this->img_rb_x = ($this->w - $x - $w);
8986                         } else {
8987                                 // set right side coordinate
8988                                 $this->img_rb_x = $x + $w;
8989                         }
8990                         // set pointer to align the successive text/objects
8991                         switch($align) {
8992                                 case 'T':{
8993                                         $this->y = $y;
8994                                         $this->x = $this->img_rb_x;
8995                                         break;
8996                                 }
8997                                 case 'M':{
8998                                         $this->y = $y + round($h/2);
8999                                         $this->x = $this->img_rb_x;
9000                                         break;
9001                                 }
9002                                 case 'B':{
9003                                         $this->y = $this->img_rb_y;
9004                                         $this->x = $this->img_rb_x;
9005                                         break;
9006                                 }
9007                                 case 'N':{
9008                                         $this->SetY($this->img_rb_y);
9009                                         break;
9010                                 }
9011                                 default:{
9012                                         break;
9013                                 }
9014                         }
9015                 }
9016
9017                 /**
9018                  * This function is DEPRECATED, please use the new write1DBarcode() function.
9019                  * @param int $x x position in user units
9020                  * @param int $y y position in user units
9021                  * @param int $w width in user units
9022                  * @param int $h height position in user units
9023                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
9024                  * @param string $style barcode style
9025                  * @param string $font font for text
9026                  * @param int $xres x resolution
9027                  * @param string $code code to print
9028                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
9029                  * @see write1DBarcode()
9030                  */
9031                 function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
9032                         // convert old settings for the new write1DBarcode() function.
9033                         $xres = 1 / $xres;
9034                         $newstyle = array(
9035                                 "position" => "L",
9036                                 "border" => false,
9037                                 "padding" => 0,
9038                                 "fgcolor" => array(0,0,0),
9039                                 "bgcolor" => false,
9040                                 "text" => true,
9041                                 "font" => $font,
9042                                 "fontsize" => 8,
9043                                 "stretchtext" => 4
9044                         );
9045                         if ($style & 1) {
9046                                 $newstyle["border"] = true;
9047                         }
9048                         if ($style & 2) {
9049                                 $newstyle["bgcolor"] = false;
9050                         }
9051                         if ($style & 4) {
9052                                 $newstyle["position"] = "C";
9053                         } elseif ($style & 8) {
9054                                 $newstyle["position"] = "L";
9055                         } elseif ($style & 16) {
9056                                 $newstyle["position"] = "R";
9057                         }
9058                         if ($style & 128) {
9059                                 $newstyle["text"] = true;
9060                         }
9061                         if ($style & 256) {
9062                                 $newstyle["stretchtext"] = 4;
9063                         }
9064                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
9065                 }
9066
9067                 /**
9068                  * Returns an array containing current margins:
9069                  * <ul>
9070                                 <li>$ret['left'] = left  margin</li>
9071                                 <li>$ret['right'] = right margin</li>
9072                                 <li>$ret['top'] = top margin</li>
9073                                 <li>$ret['bottom'] = bottom margin</li>
9074                                 <li>$ret['header'] = header margin</li>
9075                                 <li>$ret['footer'] = footer margin</li>
9076                                 <li>$ret['cell'] = cell margin</li>
9077                  * </ul>
9078                  * @return array containing all margins measures
9079                  * @since 3.2.000 (2008-06-23)
9080                  */
9081                 function getMargins() {
9082                         $ret = array(
9083                                 'left' => $this->lMargin,
9084                                 'right' => $this->rMargin,
9085                                 'top' => $this->tMargin,
9086                                 'bottom' => $this->bMargin,
9087                                 'header' => $this->header_margin,
9088                                 'footer' => $this->footer_margin,
9089                                 'cell' => $this->cMargin,
9090                         );
9091                         return $ret;
9092                 }
9093
9094                 /**
9095                  * Returns an array containing original margins:
9096                  * <ul>
9097                                 <li>$ret['left'] = left  margin</li>
9098                                 <li>$ret['right'] = right margin</li>
9099                  * </ul>
9100                  * @return array containing all margins measures
9101                  * @since 4.0.012 (2008-07-24)
9102                  */
9103                 function getOriginalMargins() {
9104                         $ret = array(
9105                                 'left' => $this->original_lMargin,
9106                                 'right' => $this->original_rMargin
9107                         );
9108                         return $ret;
9109                 }
9110
9111                 /**
9112                  * Returns the current font size.
9113                  * @return current font size
9114                  * @since 3.2.000 (2008-06-23)
9115                  */
9116                 function getFontSize() {
9117                         return $this->FontSize;
9118                 }
9119
9120                 /**
9121                  * Returns the current font size in points unit.
9122                  * @return current font size in points unit
9123                  * @since 3.2.000 (2008-06-23)
9124                  */
9125                 function getFontSizePt() {
9126                         return $this->FontSizePt;
9127                 }
9128
9129                 /**
9130                  * Prints a cell (rectangular area) with optional borders, background color and html text string.
9131                  * 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 />
9132                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
9133                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
9134                  * @param float $h Cell minimum height. The cell extends automatically if needed.
9135                  * @param float $x upper-left corner X coordinate
9136                  * @param float $y upper-left corner Y coordinate
9137                  * @param string $html html text to print. Default value: empty string.
9138                  * @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>
9139                  * @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>
9140         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
9141                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
9142                  * @param boolean $reseth if true reset the last cell height (default true).
9143                  * @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>
9144                  * @uses MultiCell()
9145                  * @see Multicell(), writeHTML()
9146                  */
9147                 function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='') {
9148                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true);
9149                 }
9150
9151                 /**
9152                  * Returns the HTML DOM array.
9153                  * <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>
9154                  * @param string $html html code
9155                  * @return array
9156                  * @since 3.2.000 (2008-06-20)
9157                  */
9158                 function getHtmlDomArray($html) {
9159                         // remove all unsupported tags (the line below lists all supported tags)
9160                         $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>");
9161                         //replace carriage returns, newlines and tabs
9162                         $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ", "\\" => "\\\\");
9163                         $html = strtr($html, $repTable);
9164                         // remove extra spaces from tables
9165                         $html = preg_replace('/[\s]*<\/table>[\s]*/', '</table>', $html);
9166                         $html = preg_replace('/[\s]*<\/tr>[\s]*/', '</tr>', $html);
9167                         $html = preg_replace('/[\s]*<tr/', '<tr', $html);
9168                         $html = preg_replace('/[\s]*<\/th>[\s]*/', '</th>', $html);
9169                         $html = preg_replace('/[\s]*<th/', '<th', $html);
9170                         $html = preg_replace('/[\s]*<\/td>[\s]*/', '</td>', $html);
9171                         $html = preg_replace('/[\s]*<td/', '<td', $html);
9172                         $html = preg_replace('/<\/th>/', '<span></span></th>', $html);
9173                         $html = preg_replace('/<\/td>/', '<span></span></td>', $html);
9174                         // pattern for generic tag
9175                         $tagpattern = '/(<[^>]+>)/Uu';
9176                         // explodes the string
9177                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9178                         // count elements
9179                         $maxel = count($a);
9180                         $key = 0;
9181                         // create an array of elements
9182                         $dom = array();
9183                         $dom[$key] = array();
9184                         // set first void element
9185                         $dom[$key]['tag'] = false;
9186                         $dom[$key]['value'] = "";
9187                         $dom[$key]['parent'] = 0;
9188                         $dom[$key]['fontname'] = $this->FontFamily;
9189                         $dom[$key]['fontstyle'] = $this->FontStyle;
9190                         $dom[$key]['fontsize'] = $this->FontSizePt;
9191                         $dom[$key]['bgcolor'] = false;
9192                         $dom[$key]['fgcolor'] = $this->fgcolor;
9193                         $dom[$key]['align'] = '';
9194                         $key++;
9195                         $level = array();
9196                         array_push($level, 0); // root
9197                         while ($key <= $maxel) {
9198                                 if ($key > 0) {
9199                                         $dom[$key] = array();
9200                                 }
9201                                 $element = $a[($key-1)];
9202                                 if (preg_match($tagpattern, $element)) {
9203                                         // html tag
9204                                         $dom[$key]['tag'] = true;
9205                                         $element = substr($element, 1, -1);
9206                                         // get tag name
9207                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
9208                                         $dom[$key]['value'] = strtolower($tag[1]);
9209                                         if ($element[0] == '/') {
9210                                                 // closing html tag
9211                                                 $dom[$key]['opening'] = false;
9212                                                 $dom[$key]['parent'] = end($level);
9213                                                 array_pop($level);
9214                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
9215                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
9216                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
9217                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
9218                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
9219                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
9220                                                 // set the number of columns in table tag
9221                                                 if (($dom[$key]['value'] == "tr") AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
9222                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
9223                                                 }
9224                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9225                                                         $dom[($dom[$key]['parent'])]['content'] = "";
9226                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; $i++) {
9227                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[($i-1)];
9228                                                         }
9229                                                         $key = $i;
9230                                                 }
9231                                         } else {
9232                                                 // opening html tag
9233                                                 $dom[$key]['opening'] = true;
9234                                                 $dom[$key]['parent'] = end($level);
9235                                                 if (substr($element, -1, 1) != '/') {
9236                                                         // not self-closing tag
9237                                                         array_push($level, $key);
9238                                                         $dom[$key]['self'] = false;
9239                                                 } else {
9240                                                         $dom[$key]['self'] = true;
9241                                                 }
9242                                                 // copy some values from parent
9243                                                 if ($key > 0) {
9244                                                         $dom[$key]['fontname'] = $dom[($dom[$key]['parent'])]['fontname'];
9245                                                         $dom[$key]['fontstyle'] = $dom[($dom[$key]['parent'])]['fontstyle'];
9246                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'];
9247                                                         $dom[$key]['bgcolor'] = $dom[($dom[$key]['parent'])]['bgcolor'];
9248                                                         $dom[$key]['fgcolor'] = $dom[($dom[$key]['parent'])]['fgcolor'];
9249                                                         $dom[$key]['align'] = $dom[($dom[$key]['parent'])]['align'];
9250                                                 }
9251                                                 // get attributes
9252                                                 preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER);
9253                                                 $dom[$key]['attribute'] = array(); // reset attribute array
9254                                                 foreach ($attr_array[1] as $id => $name) {
9255                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
9256                                                 }
9257                                                 // split style attributes
9258                                                 if (isset($dom[$key]['attribute']['style'])) {
9259                                                         // get style attributes
9260                                                         preg_match_all('/([^:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
9261                                                         $dom[$key]['style'] = array(); // reset style attribute array
9262                                                         foreach ($style_array[1] as $id => $name) {
9263                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
9264                                                         }
9265                                                         // --- get some style attributes ---
9266                                                         if (isset($dom[$key]['style']['font-family'])) {
9267                                                                 // font family
9268                                                                 if (isset($dom[$key]['style']['font-family'])) {
9269                                                                         $fontslist = preg_split("/,/", strtolower($dom[$key]['style']['font-family']));
9270                                                                         foreach($fontslist as $font) {
9271                                                                                 $font = trim(strtolower($font));
9272                                                                                 if (in_array($font, $this->fontlist)){
9273                                                                                         $dom[$key]['fontname'] = $font;
9274                                                                                         break;
9275                                                                                 }
9276                                                                         }
9277                                                                 }
9278                                                         }
9279                                                         // font size
9280                                                         if (isset($dom[$key]['style']['font-size'])) {
9281                                                                 $fsize = trim($dom[$key]['style']['font-size']);
9282                                                                 switch ($fsize) {
9283                                                                         case 'xx-small': {
9284                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
9285                                                                                 break;
9286                                                                         }
9287                                                                         case 'x-small': {
9288                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
9289                                                                                 break;
9290                                                                         }
9291                                                                         case 'small': {
9292                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
9293                                                                                 break;
9294                                                                         }
9295                                                                         case 'medium': {
9296                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
9297                                                                                 break;
9298                                                                         }
9299                                                                         case 'large': {
9300                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
9301                                                                                 break;
9302                                                                         }
9303                                                                         case 'x-large': {
9304                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
9305                                                                                 break;
9306                                                                         }
9307                                                                         case 'xx-large': {
9308                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
9309                                                                                 break;
9310                                                                         }
9311                                                                         default: {
9312                                                                                 $dom[$key]['fontsize'] = intval($fsize);
9313                                                                         }
9314                                                                 }
9315                                                         }
9316                                                         // font style
9317                                                         $dom[$key]['fontstyle'] = "";
9318                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight'][0]) == "b")) {
9319                                                                 $dom[$key]['fontstyle'] .= "B";
9320                                                         }
9321                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == "i")) {
9322                                                                 $dom[$key]['fontstyle'] .= "I";
9323                                                         }
9324                                                         // font color
9325                                                         if (isset($dom[$key]['style']['color']) AND (!empty($dom[$key]['style']['color']))) {
9326                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
9327                                                         }
9328                                                         // background color
9329                                                         if (isset($dom[$key]['style']['background-color']) AND (!empty($dom[$key]['style']['background-color']))) {
9330                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
9331                                                         }
9332                                                         // text-decoration
9333                                                         if (isset($dom[$key]['style']['text-decoration'])) {
9334                                                                 $decors = explode(" ", strtolower($dom[$key]['style']['text-decoration']));
9335                                                                 foreach ($decors as $dec) {
9336                                                                         $dec = trim($dec);
9337                                                                         if ($dec[0] == "u") {
9338                                                                                 $dom[$key]['fontstyle'] .= "U";
9339                                                                         } elseif ($dec[0] == "l") {
9340                                                                                 $dom[$key]['fontstyle'] .= "D";
9341                                                                         }
9342                                                                 }
9343                                                         }
9344                                                         // check for width attribute
9345                                                         if (isset($dom[$key]['style']['width'])) {
9346                                                                 $dom[$key]['width'] = intval($dom[$key]['style']['width']);
9347                                                         }
9348                                                         // check for height attribute
9349                                                         if (isset($dom[$key]['style']['height'])) {
9350                                                                 $dom[$key]['height'] = intval($dom[$key]['style']['height']);
9351                                                         }
9352                                                         // check for text alignment
9353                                                         if (isset($dom[$key]['style']['text-align'])) {
9354                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
9355                                                         }
9356                                                 }
9357                                                 // check for font tag
9358                                                 if ($dom[$key]['value'] == "font") {
9359                                                         // font family
9360                                                         if (isset($dom[$key]['attribute']['face'])) {
9361                                                                 $fontslist = preg_split("/,/", strtolower($dom[$key]['attribute']['face']));
9362                                                                 foreach($fontslist as $font) {
9363                                                                         $font = trim(strtolower($font));
9364                                                                         if (in_array($font, $this->fontlist)){
9365                                                                                 $dom[$key]['fontname'] = $font;
9366                                                                                 break;
9367                                                                         }
9368                                                                 }
9369                                                         }
9370                                                         // font size
9371                                                         if (isset($dom[$key]['attribute']['size'])) {
9372                                                                 if ($key > 0) {
9373                                                                         if ($dom[$key]['attribute']['size'][0] == "+") {
9374                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
9375                                                                         } elseif ($dom[$key]['attribute']['size'][0] == "-") {
9376                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
9377                                                                         } else {
9378                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9379                                                                         }
9380                                                                 } else {
9381                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9382                                                                 }
9383                                                         }
9384                                                 }
9385                                                 if (($dom[$key]['value'] == "ul") OR ($dom[$key]['value'] == "ol") OR ($dom[$key]['value'] == "dl")) {
9386                                                         // force natural alignment for lists
9387                                                         if ($this->rtl) {
9388                                                                 $dom[$key]['align'] = "R";
9389                                                         } else {
9390                                                                 $dom[$key]['align'] = "L";
9391                                                         }
9392                                                 }
9393                                                 if (($dom[$key]['value'] == "small") OR ($dom[$key]['value'] == "sup") OR ($dom[$key]['value'] == "sub")) {
9394                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
9395                                                 }
9396                                                 if (($dom[$key]['value'] == "strong") OR ($dom[$key]['value'] == "b")) {
9397                                                         $dom[$key]['fontstyle'] .= "B";
9398                                                 }
9399                                                 if (($dom[$key]['value'] == "em") OR ($dom[$key]['value'] == "i")) {
9400                                                         $dom[$key]['fontstyle'] .= "I";
9401                                                 }
9402                                                 if (($dom[$key]['value'][0] == "h") AND (intval($dom[$key]['value'][1]) > 0) AND (intval($dom[$key]['value'][1]) < 7)) {
9403                                                         $headsize = (4 - intval($dom[$key]['value'][1])) * 2;
9404                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
9405                                                         $dom[$key]['fontstyle'] .= "B";
9406                                                 }
9407                                                 if (($dom[$key]['value'] == "table")) {
9408                                                         $dom[$key]['rows'] = 0; // number of rows
9409                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
9410                                                 }
9411                                                 if (($dom[$key]['value'] == "tr")) {
9412                                                         $dom[$key]['cols'] = 0;
9413                                                         // store the number of rows on table element
9414                                                         $dom[($dom[$key]['parent'])]['rows']++;
9415                                                         // store the TR elements IDs on table element
9416                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
9417                                                 }
9418                                                 if (($dom[$key]['value'] == "th") OR ($dom[$key]['value'] == "td")) {
9419                                                         if (isset($dom[$key]['attribute']['colspan'])) {
9420                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
9421                                                         } else {
9422                                                                 $colspan = 1;
9423                                                         }
9424                                                         $dom[$key]['attribute']['colspan'] = $colspan;
9425                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
9426                                                 }
9427                                                 // set foreground color attribute
9428                                                 if (isset($dom[$key]['attribute']['color']) AND (!empty($dom[$key]['attribute']['color']))) {
9429                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
9430                                                 }
9431                                                 // set background color attribute
9432                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!empty($dom[$key]['attribute']['bgcolor']))) {
9433                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
9434                                                 }
9435                                                 // check for width attribute
9436                                                 if (isset($dom[$key]['attribute']['width'])) {
9437                                                         $dom[$key]['width'] = intval($dom[$key]['attribute']['width']);
9438                                                 }
9439                                                 // check for height attribute
9440                                                 if (isset($dom[$key]['attribute']['height'])) {
9441                                                         $dom[$key]['height'] = intval($dom[$key]['attribute']['height']);
9442                                                 }
9443                                                 // check for text alignment
9444                                                 if (isset($dom[$key]['attribute']['align']) AND (!empty($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
9445                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
9446                                                 }
9447                                         } // end opening tag
9448                                 } else {
9449                                         // text
9450                                         $dom[$key]['tag'] = false;
9451                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
9452                                         $dom[$key]['parent'] = end($level);
9453                                         // calculate text width
9454                                         //$dom[$key]['width'] = $this->GetStringWidth($dom[$key]['value'], $dom[($dom[$key]['parent'])]['fontname'], $dom[($dom[$key]['parent'])]['fontstyle'], $dom[($dom[$key]['parent'])]['fontsize']);
9455                                 }
9456                                 $key++;
9457                         }
9458                         return $dom;
9459                 }
9460
9461                 /**
9462                  * Allows to preserve some HTML formatting (limited support).<br />
9463                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
9464                  * 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,
9465                  * @param string $html text to display
9466                  * @param boolean $ln if true add a new line after text (default = true)
9467                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
9468                  * @param boolean $reseth if true reset the last cell height (default false).
9469                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
9470                  * @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>
9471                  */
9472                 function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
9473                         // store current values
9474                         $prevlMargin = $this->lMargin;
9475                         $prevrMargin = $this->rMargin;
9476                         $prevcMargin = $this->cMargin;
9477                         $prevFontFamily = $this->FontFamily;
9478                         $prevFontStyle = $this->FontStyle;
9479                         $prevFontSizePt = $this->FontSizePt;
9480                         $curfontname = $prevFontFamily;
9481                         $curfontstyle = $prevFontStyle;
9482                         $curfontsize = $prevFontSizePt;
9483                         $prevbgcolor = $this->bgcolor;
9484                         $prevfgcolor = $this->fgcolor;
9485                         $this->newline = true;
9486                         $minstartliney = $this->y;
9487                         $yshift = 0;
9488                         $startlinepage = $this->page;
9489                         $newline = true;
9490                         if (isset($this->footerlen[$this->page])) {
9491                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9492                         } else {
9493                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9494                         }
9495                         $startlinepos = $this->footerpos[$this->page];
9496                         $lalign = $align;
9497                         $plalign = $align;
9498                         if ($this->rtl) {
9499                                 $w = $this->x - $this->lMargin;
9500                         } else {
9501                                 $w = $this->w - $this->rMargin - $this->x;
9502                         }
9503                         $w -= (2 * $this->cMargin);
9504                         if ($cell) {
9505                                 if ($this->rtl) {
9506                                         $this->x -= $this->cMargin;
9507                                 } else {
9508                                         $this->x += $this->cMargin;
9509                                 }
9510                         }
9511                         $this->listindent = $this->GetStringWidth("0000");
9512                         $this->listnum = 0;
9513                         if ((empty($this->lasth))OR ($reseth)) {
9514                                 //set row height
9515                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9516                         }
9517                         $dom = $this->getHtmlDomArray($html);
9518                         $maxel = count($dom);
9519                         $key = 0;
9520                         while ($key < $maxel) {
9521                                 if ($dom[$key]['tag'] OR ($key == 0)) {
9522                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
9523                                                 $dom[$key]['align'] = ($this->rtl)?'R':'L';
9524                                         }
9525                                         // vertically align image in line
9526                                         if ((!$this->newline) AND ($dom[$key]['value'] == 'img') 
9527                                                 AND (isset($dom[$key]['attribute']['height']))
9528                                                 AND ($dom[$key]['attribute']['height'] > 0)
9529                                                 ) {
9530                                                 $this->y += (($curfontsize / $this->k) - $this->pixelsToUnits($dom[$key]['attribute']['height']));
9531                                                 $minstartliney = min($this->y, $minstartliney);
9532                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
9533                                                 // account for different font size
9534                                                 $pfontname = $curfontname;
9535                                                 $pfontstyle = $curfontstyle;
9536                                                 $pfontsize = $curfontsize;
9537                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
9538                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
9539                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
9540                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
9541                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9542                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
9543                                                         if (is_numeric($fontsize) AND ($fontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($fontsize != $curfontsize) AND (!$this->newline)) {
9544                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
9545                                                                 $minstartliney = min($this->y, $minstartliney);
9546                                                         }
9547                                                         $curfontname = $fontname;
9548                                                         $curfontstyle = $fontstyle;
9549                                                         $curfontsize = $fontsize;
9550                                                 }
9551                                         }
9552                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
9553                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
9554                                                 $wfill = true;
9555                                         } else {
9556                                                 $wfill = $fill | false;
9557                                         }
9558                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
9559                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
9560                                         }
9561                                         if (isset($dom[$key]['align'])) {
9562                                                 $lalign = $dom[$key]['align'];
9563                                         }
9564                                         if (empty($lalign)) {
9565                                                 $lalign = $align;
9566                                         }
9567                                 }
9568                                 // align lines
9569                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
9570                                         $newline = true;
9571                                         // we are at the beginning of a new line
9572                                         if (isset($startlinex)) {
9573                                                 $yshift = $minstartliney - $startliney;
9574                                                 if ($yshift > 0) {
9575                                                         $yshift = 0;
9576                                                 }
9577                                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9578                                                         // the last line must be shifted to be aligned as requested
9579                                                         $linew = abs($this->endlinex - $startlinex);
9580                                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9581                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9582                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9583                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9584                                                         } elseif (isset($opentagpos)) {
9585                                                                 $midpos = $opentagpos;
9586                                                         } elseif (isset($this->footerlen[$startlinepage])) {
9587                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9588                                                                 $midpos = $this->footerpos[$startlinepage];
9589                                                         } else {
9590                                                                 $midpos = 0;
9591                                                         }
9592                                                         if ($midpos > 0) {
9593                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9594                                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9595                                                         } else {
9596                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9597                                                                 $pend = "";
9598                                                         }
9599                                                         // calculate shifting amount
9600                                                         $mdiff = abs($w - $linew);
9601                                                         if ($plalign == "C") {
9602                                                                 if ($this->rtl) {
9603                                                                         $t_x = -($mdiff / 2);
9604                                                                 } else {
9605                                                                         $t_x = ($mdiff / 2);
9606                                                                 }
9607                                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9608                                                                 // right alignment on LTR document
9609                                                                 $t_x = $mdiff;
9610                                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9611                                                                 // left alignment on RTL document
9612                                                                 $t_x = -$mdiff;
9613                                                         } else {
9614                                                                 $t_x = 0;
9615                                                         }
9616                                                         if (($t_x != 0) OR ($yshift < 0)) {
9617                                                                 // shift the line
9618                                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9619                                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9620                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9621                                                                 // shift the annotations and links
9622                                                                 if (isset($this->PageAnnots[$this->page])) {
9623                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9624                                                                                 if ($pac['y'] >= $minstartliney) {
9625                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9626                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9627                                                                                 }
9628                                                                         }
9629                                                                 }
9630                                                                 $this->y -= $yshift;
9631                                                         }
9632                                                 }
9633                                         }
9634                                         $this->checkPageBreak($this->lasth);
9635                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9636                                         if ($wfill) {
9637                                                 $this->SetFillColorArray($this->bgcolor);
9638                                         }
9639                                         $startlinex = $this->x;
9640                                         $startliney = $this->y;
9641                                         $minstartliney = $this->y;
9642                                         $startlinepage = $this->page;
9643                                         if (isset($endlinepos)) {
9644                                                 $startlinepos = $endlinepos;
9645                                                 unset($endlinepos);
9646                                         } else {
9647                                                 if (isset($this->footerlen[$this->page])) {
9648                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9649                                                 } else {
9650                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9651                                                 }
9652                                                 $startlinepos = $this->footerpos[$this->page];
9653                                         }
9654                                         $plalign = $lalign;
9655                                         $this->newline = false;
9656                                 }
9657                                 if (isset($opentagpos)) {
9658                                         unset($opentagpos);
9659                                 }
9660                                 if ($dom[$key]['tag']) {
9661                                         if ($dom[$key]['opening']) {
9662                                                 // table content is handled in a special way
9663                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9664                                                         $trid = $dom[$key]['parent'];
9665                                                         $table_el = $dom[$trid]['parent'];
9666                                                         if (!isset($dom[$table_el]['cols'])) {
9667                                                                 $dom[$table_el]['cols'] = $trid['cols'];
9668                                                         }
9669                                                         // calculate cell width
9670                                                         if (isset($dom[($dom[$key]['parent'])]['width'])) {
9671                                                                 $table_width = $this->pixelsToUnits($dom[($dom[$key]['parent'])]['width']);
9672                                                         } else {
9673                                                                 $table_width = $w;
9674                                                         }
9675                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
9676                                                                 $currentcmargin = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding']);
9677                                                         } else {
9678                                                                 $currentcmargin = 0;
9679                                                         }
9680                                                         $this->cMargin = $currentcmargin;
9681                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
9682                                                                 $cellspacing = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing']);
9683                                                         } else {
9684                                                                 $cellspacing = 0;
9685                                                         }
9686                                                         if ($this->rtl) {
9687                                                                 $cellspacingx = -$cellspacing;
9688                                                         } else {
9689                                                                 $cellspacingx = $cellspacing;
9690                                                         }
9691                                                         $colspan = $dom[$key]['attribute']['colspan'];
9692                                                         if (isset($dom[$key]['width'])) {
9693                                                                 $cellw = $this->pixelsToUnits($dom[$key]['width']);
9694                                                         } else {
9695                                                                 $cellw = ($colspan * ($table_width / $dom[$table_el]['cols']));
9696                                                         }
9697                                                         $cellw -= $cellspacing;
9698                                                         if (isset($dom[$key]['content'])) {
9699                                                                 $cell_content = $dom[$key]['content'];
9700                                                         } else {
9701                                                                 $cell_content = "&nbsp;";
9702                                                         }
9703                                                         $tagtype = $dom[$key]['value'];
9704                                                         $parentid = $key;
9705                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
9706                                                                 // move $key index forward
9707                                                                 $key++;
9708                                                         }
9709                                                         if (!isset($dom[$trid]['startpage'])) {
9710                                                                 $dom[$trid]['startpage'] = $this->page;
9711                                                         } else {
9712                                                                 $this->setPage($dom[$trid]['startpage']);
9713                                                         }
9714                                                         if (!isset($dom[$trid]['starty'])) {
9715                                                                 $dom[$trid]['starty'] = $this->y;
9716                                                         } else {
9717                                                                 $this->y = $dom[$trid]['starty'];
9718                                                         }
9719                                                         if (!isset($dom[$trid]['startx'])) {
9720                                                                 $dom[$trid]['startx'] = $this->x;
9721                                                         }
9722                                                         $this->x += ($cellspacingx / 2);
9723                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
9724                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
9725                                                         }       else {
9726                                                                 $rowspan = 1;
9727                                                         }
9728                                                         // skip row-spanned cells started on the previous rows
9729                                                         if (isset($dom[$table_el]['rowspans'])) {
9730                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9731                                                                         if  (($trwsp['startx'] == $this->x) AND (($trwsp['starty'] < $this->y) OR ($trwsp['startpage'] < $this->page)) AND ($trwsp['rowspan'] > 0)) {
9732                                                                                 $this->x = $trwsp['endx'] + $cellspacingx;
9733                                                                         }
9734                                                                 }
9735                                                         }
9736                                                         // add rowspan information to table element
9737                                                         if ($rowspan > 1) {
9738                                                                 if (isset($this->footerlen[$this->page])) {
9739                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9740                                                                 } else {
9741                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9742                                                                 }
9743                                                                 $trintmrkpos = $this->footerpos[$this->page];
9744                                                                 $trsid = array_push($dom[$table_el]['rowspans'], array('rowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
9745                                                         }
9746                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
9747                                                         if ($rowspan > 1) {
9748                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
9749                                                         }
9750                                                         // push background colors
9751                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
9752                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
9753                                                         }
9754
9755                                                         // write the cell content
9756                                                         $this->MultiCell($cellw, 0, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
9757
9758                                                         $this->cMargin = $currentcmargin;
9759                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
9760                                                         
9761                                                         // update the end of row position
9762                                                         if ($rowspan <= 1) {
9763                                                                 if (isset($dom[$trid]['endy'])) {
9764                                                                         if ($this->page == $dom[$trid]['endpage']) {
9765                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
9766                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
9767                                                                                 $dom[$trid]['endy'] = $this->y;
9768                                                                         }
9769                                                                 } else {
9770                                                                         $dom[$trid]['endy'] = $this->y;
9771                                                                 }
9772                                                                 if (isset($dom[$trid]['endpage'])) {
9773                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
9774                                                                 } else {
9775                                                                         $dom[$trid]['endpage'] = $this->page;
9776                                                                 }
9777                                                         } else {
9778                                                         // account for row-spanned cells
9779                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
9780                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
9781                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
9782                                                         }
9783                                                         if (isset($dom[$table_el]['rowspans'])) {
9784                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9785                                                                         if ($trwsp['rowspan'] > 0) {
9786                                                                                 if (isset($dom[$trid]['endpage'])) {
9787                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
9788                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
9789                                                                                         } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[$trid]['endpage']) {
9790                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $trwsp['endy'];
9791                                                                                         } else {
9792                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
9793                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
9794                                                                                         }
9795                                                                                 }
9796                                                                         }
9797                                                                 }
9798                                                         }
9799                                                         $this->x += ($cellspacingx / 2);
9800                                                 } else {
9801                                                         // opening tag (or self-closing tag)
9802                                                         if (!isset($opentagpos)) {
9803                                                                 if (isset($this->footerlen[$this->page])) {
9804                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9805                                                                 } else {
9806                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9807                                                                 }
9808                                                                 $opentagpos = $this->footerpos[$this->page];
9809                                                         }
9810                                                         $this->openHTMLTagHandler($dom, $key, $cell);
9811                                                 }
9812                                         } else {
9813                                                 // closing tag
9814                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
9815                                         }
9816                                 } elseif (strlen($dom[$key]['value']) > 0) {
9817                                         // print list-item
9818                                         if (!empty($this->lispacer)) {
9819                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
9820                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9821                                                 $minstartliney = $this->y;
9822                                                 $tmpx = $this->x;
9823                                                 $lspace = $this->GetStringWidth($this->lispacer."  ");
9824                                                 if ($this->rtl) {
9825                                                         $this->x += $lspace;
9826                                                 } else {
9827                                                         $this->x -= $lspace;
9828                                                 }
9829                                                 $this->Write($this->lasth, $this->lispacer, '', false, '', false, 0, false);
9830                                                 $this->x = $tmpx;
9831                                                 $this->lispacer = "";
9832                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
9833                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9834                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
9835                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
9836                                                         $minstartliney = min($this->y, $minstartliney);
9837                                                 }
9838                                         }
9839                                         // text
9840                                         $this->htmlvspace = 0;
9841                                         if ($newline) {
9842                                                 if ($this->rtl OR $this->tmprtl) {
9843                                                         $dom[$key]['value'] = rtrim($dom[$key]['value']);
9844                                                 } else {
9845                                                         $dom[$key]['value'] = ltrim($dom[$key]['value']);
9846                                                 }
9847                                                 $newline = false;
9848                                         }
9849                                         if ($this->HREF) {
9850                                                 // HTML <a> Link
9851                                                 $strrest = $this->addHtmlLink($this->HREF, $dom[$key]['value'], $wfill, true);
9852                                         } else {
9853                                                 $ctmpmargin = $this->cMargin;
9854                                                 $this->cMargin = 0;
9855                                                 // write only until the end of the line and get the rest
9856                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, "", false, 0, true);
9857                                                 $this->cMargin = $ctmpmargin;
9858                                         }
9859                                         if (strlen($strrest) > 0) {
9860                                                 // store the remaining string on the previous $key position
9861                                                 $this->newline = true;
9862                                                 if ($cell) {
9863                                                         if ($this->rtl) {
9864                                                                 $this->x -= $this->cMargin;
9865                                                         } else {
9866                                                                 $this->x += $this->cMargin;
9867                                                         }
9868                                                 }
9869                                                 $dom[$key]['value'] = ltrim($strrest);
9870                                                 $key--;
9871                                         }
9872                                 }
9873                                 $key++;
9874                         } // end for each $key
9875                         // align the last line
9876                         if (isset($startlinex)) {
9877                                 $yshift = $minstartliney - $startliney;
9878                                 if ($yshift > 0) {
9879                                         $yshift = 0;
9880                                 }
9881                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9882                                         // the last line must be shifted to be aligned as requested
9883                                         $linew = abs($this->endlinex - $startlinex);
9884                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9885                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9886                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9887                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9888                                         } elseif (isset($opentagpos)) {
9889                                                 $midpos = $opentagpos;
9890                                         } elseif (isset($this->footerlen[$startlinepage])) {
9891                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9892                                                 $midpos = $this->footerpos[$startlinepage];
9893                                         } else {
9894                                                 $midpos = 0;
9895                                         }
9896                                         if ($midpos > 0) {
9897                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9898                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9899                                         } else {
9900                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9901                                                 $pend = "";
9902                                         }
9903                                         // calculate shifting amount
9904                                         $mdiff = abs($w - $linew);
9905                                         if ($plalign == "C") {
9906                                                 if ($this->rtl) {
9907                                                         $t_x = -($mdiff / 2);
9908                                                 } else {
9909                                                         $t_x = ($mdiff / 2);
9910                                                 }
9911                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9912                                                 // right alignment on LTR document
9913                                                 $t_x = $mdiff;
9914                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9915                                                 // left alignment on RTL document
9916                                                 $t_x = -$mdiff;
9917                                         } else {
9918                                                 $t_x = 0;
9919                                         }
9920                                         if (($t_x != 0) OR ($yshift < 0)) {
9921                                                 // shift the line
9922                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9923                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9924                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9925                                                 // shift the annotations and links
9926                                                 if (isset($this->PageAnnots[$this->page])) {
9927                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9928                                                                 if ($pac['y'] >= $minstartliney) {
9929                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9930                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9931                                                                 }
9932                                                         }
9933                                                 }
9934                                                 $this->y -= $yshift;
9935                                         }
9936                                 }
9937                         }
9938                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == "table")))) {
9939                                 $this->Ln($this->lasth);
9940                         }
9941                         // restore previous values
9942                         $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
9943                         $this->SetFillColorArray($prevbgcolor);
9944                         $this->SetTextColorArray($prevfgcolor);
9945                         $this->lMargin = $prevlMargin;
9946                         $this->rMargin = $prevrMargin;
9947                         $this->cMargin = $prevcMargin;
9948                         unset($dom);
9949                 }
9950
9951                 /**
9952                  * Process opening tags.
9953                  * @param array $dom html dom array
9954                  * @param int $key current element id
9955                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
9956                  * @access protected
9957                  */
9958                 function openHTMLTagHandler(&$dom, $key, $cell=false) {
9959                         $tag = $dom[$key];
9960                         $parent = $dom[($dom[$key]['parent'])];
9961                         // check for text direction attribute
9962                         if (isset($tag['attribute']['dir'])) {
9963                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
9964                         } else {
9965                                 $this->tmprtl = false;
9966                         }
9967                         //Opening tag
9968                         switch($tag['value']) {
9969                                 case 'table': {
9970                                         $dom[$key]['rowspans'] = array();
9971                                         if (isset($tag['attribute']['cellpadding'])) {
9972                                                 $this->oldcMargin = $this->cMargin;
9973                                                 $this->cMargin = $this->pixelsToUnits($tag['attribute']['cellpadding']);
9974                                         }
9975                                         break;
9976                                 }
9977                                 case 'tr': {
9978                                         // array of columns positions
9979                                         $dom[$key]['cellpos'] = array();
9980                                         break;
9981                                 }
9982                                 case 'td':
9983                                 case 'th': {
9984                                         break;
9985                                 }
9986                                 case 'hr': {
9987                                         $this->addHTMLVertSpace(1, $cell);
9988                                         $this->htmlvspace = 0;
9989                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
9990                                                 $hrWidth = $this->pixelsToUnits($tag['attribute']['width']);
9991                                         } else {
9992                                                 $hrWidth = $this->w - $this->lMargin - $this->rMargin;
9993                                         }
9994                                         $x = $this->GetX();
9995                                         $y = $this->GetY();
9996                                         $prevlinewidth = $this->GetLineWidth();
9997                                         $this->Line($x, $y, $x + $hrWidth, $y);
9998                                         $this->SetLineWidth($prevlinewidth);
9999                                         $this->addHTMLVertSpace(1, $cell);
10000                                         break;
10001                                 }
10002                                 case 'b': {
10003                                         $this->setStyle('b', true);
10004                                         break;
10005                                 }
10006                                 case 'i': {
10007                                         $this->setStyle('i', true);
10008                                         break;
10009                                 }
10010                                 case 'u': {
10011                                         $this->setStyle('u', true);
10012                                         break;
10013                                 }
10014                                 case 'del': {
10015                                         $this->setStyle('d', true);
10016                                         break;
10017                                 }
10018                                 case 'a': {
10019                                         if (array_key_exists('href', $tag['attribute'])) {
10020                                                 $this->HREF = $tag['attribute']['href'];
10021                                         }
10022                                         break;
10023                                 }
10024                                 case 'img': {
10025                                         if (isset($tag['attribute']['src'])) {
10026                                                 // replace relative path with real server path
10027                                                 if ($tag['attribute']['src'][0] == '/') {
10028                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
10029                                                 }
10030                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
10031                                                 if (!isset($tag['attribute']['width'])) {
10032                                                         $tag['attribute']['width'] = 0;
10033                                                 }
10034                                                 if (!isset($tag['attribute']['height'])) {
10035                                                         $tag['attribute']['height'] = 0;
10036                                                 }
10037                                                 //if (!isset($tag['attribute']['align'])) {
10038                                                         // the only alignment supported is "bottom"
10039                                                         // further development is required for other modes.
10040                                                         $tag['attribute']['align'] = 'bottom';
10041                                                 //} 
10042                                                 switch($tag['attribute']['align']) {
10043                                                         case 'top': {
10044                                                                 $align = 'T';
10045                                                                 break;
10046                                                         }
10047                                                         case 'middle': {
10048                                                                 $align = 'M';
10049                                                                 break;
10050                                                         }
10051                                                         case 'bottom': {
10052                                                                 $align = 'B';
10053                                                                 break;
10054                                                         }
10055                                                         default: {
10056                                                                 $align = 'B';
10057                                                                 break;
10058                                                         }
10059                                                 }
10060                                                 $fileinfo = pathinfo($tag['attribute']['src']);
10061                                                 if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
10062                                                         $type = strtolower($fileinfo['extension']);
10063                                                 }
10064                                                 $prevy = $this->y;
10065                                                 if (($type == "eps") OR ($type == "ai")) {
10066                                                         $this->ImageEps($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', true, $align);
10067                                                 } else {
10068                                                         $this->Image($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', '', $align);
10069                                                 }
10070                                                 switch($align) {
10071                                                         case 'T': {
10072                                                                 $this->y = $prevy;
10073                                                                 break;
10074                                                         }
10075                                                         case 'M': {
10076                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
10077                                                                 break;
10078                                                         }
10079                                                         case 'B': {
10080                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
10081                                                                 break;
10082                                                         }
10083                                                 }
10084                                         }
10085                                         break;
10086                                 }
10087                                 case 'dl': {
10088                                         $this->listnum++;
10089                                         break;
10090                                 }
10091                                 case 'dt': {
10092                                         $this->addHTMLVertSpace(1, $cell);
10093                                         break;
10094                                 }
10095                                 case 'dd': {
10096                                         if ($this->rtl) {
10097                                                 $this->rMargin += $this->listindent;
10098                                         } else {
10099                                                 $this->lMargin += $this->listindent;
10100                                         }
10101                                         $this->addHTMLVertSpace(1, $cell);
10102                                         break;
10103                                 }
10104                                 case 'ul':
10105                                 case 'ol': {
10106                                         $this->listnum++;
10107                                         if ($tag['value'] == "ol") {
10108                                                 $this->listordered[$this->listnum] = true;
10109                                         } else {
10110                                                 $this->listordered[$this->listnum] = false;
10111                                         }
10112                                         $this->listcount[$this->listnum] = 0;
10113                                         if ($this->rtl) {
10114                                                 $this->rMargin += $this->listindent;
10115                                         } else {
10116                                                 $this->lMargin += $this->listindent;
10117                                         }
10118                                         break;
10119                                 }
10120                                 case 'li': {
10121                                         $this->Ln('', $cell);
10122                                         if ($tag['value'] == 'li') {
10123                                                 if ($this->listordered[$this->listnum]) {
10124                                                         if (isset($tag['attribute']['value'])) {
10125                                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
10126                                                         }
10127                                                         $this->listcount[$this->listnum]++;
10128                                                         if ($this->rtl) {
10129                                                                 $this->lispacer = ".".($this->listcount[$this->listnum]);
10130                                                         } else {
10131                                                                 $this->lispacer = ($this->listcount[$this->listnum]).".";
10132                                                         }
10133                                                 } else {
10134                                                         //unordered list symbol
10135                                                         $this->lispacer = "-";
10136                                                 }
10137                                         } else {
10138                                                 $this->lispacer = "";
10139                                         }
10140                                         break;
10141                                 }
10142                                 case 'blockquote': {
10143                                         if ($this->rtl) {
10144                                                 $this->rMargin += $this->listindent;
10145                                         } else {
10146                                                 $this->lMargin += $this->listindent;
10147                                         }
10148                                         $this->addHTMLVertSpace(2, $cell);
10149                                         break;
10150                                 }
10151                                 case 'br': {
10152                                         $this->Ln('', $cell);
10153                                         break;
10154                                 }
10155                                 case 'div': {
10156                                         $this->addHTMLVertSpace(2, $cell);
10157                                         break;
10158                                 }
10159                                 case 'p': {
10160                                         $this->addHTMLVertSpace(2, $cell);
10161                                         break;
10162                                 }
10163                                 case 'sup': {
10164                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
10165                                         break;
10166                                 }
10167                                 case 'sub': {
10168                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
10169                                         break;
10170                                 }
10171                                 case 'h1':
10172                                 case 'h2':
10173                                 case 'h3':
10174                                 case 'h4':
10175                                 case 'h5':
10176                                 case 'h6': {
10177                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k);
10178                                         break;
10179                                 }
10180                                 default: {
10181                                         break;
10182                                 }
10183                         }
10184                 }
10185
10186                 /**
10187                  * Process closing tags.
10188                  * @param array $dom html dom array
10189                  * @param int $key current element id
10190                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10191                  * @access protected
10192                  */
10193                 function closeHTMLTagHandler(&$dom, $key, $cell=false) {
10194                         $tag = $dom[$key];
10195                         $parent = $dom[($dom[$key]['parent'])];
10196                         //Closing tag
10197                         switch($tag['value']) {
10198                                 case 'td':
10199                                 case 'th': {
10200                                         break;
10201                                 }
10202                                 case 'tr': {
10203                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
10204                                         // update row-spanned cells
10205                                         if (isset($dom[$table_el]['rowspans'])) {
10206                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
10207                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
10208                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
10209                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $dom[($dom[$key]['parent'])]['endpage']) {
10210                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
10211                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[($dom[$key]['parent'])]['endpage']) {
10212                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
10213                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
10214                                                                 }
10215                                                         }
10216                                                 }
10217                                         }
10218                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
10219                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];
10220                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
10221                                                 $cellspacing = $this->pixelsToUnits($dom[$table_el]['attribute']['cellspacing']);
10222                                                 $this->y += $cellspacing;
10223                                         }                               
10224                                         $this->Ln(0, $cell);
10225                                         $this->x = $dom[($dom[$key]['parent'])]['startx'];
10226                                         break;
10227                                 }
10228                                 case 'table': {
10229                                         // draw borders
10230                                         $table_el = $parent;
10231                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
10232                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
10233                                                         $border = 1;
10234                                         } else {
10235                                                 $border = 0;
10236                                         }
10237                                         // for each row
10238                                         foreach ($table_el['trids'] as $j => $trkey) {
10239                                                 $parent = $dom[$trkey];
10240                                                 $restspace = $this->getPageHeight() - $this->y - $this->getBreakMargin();
10241                                                 // for each cell on the row
10242                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
10243                                                         if (isset($cellpos['rowspanid'])) {
10244                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
10245                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
10246                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
10247                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
10248                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
10249                                                         } else {
10250                                                                 $endy = $parent['endy'];
10251                                                                 $startpage = $parent['startpage'];
10252                                                                 $endpage = $parent['endpage'];
10253                                                         }
10254                                                         $this->setPage($startpage);
10255                                                         $this->y = $parent['starty'];
10256                                                         if ($endpage > $startpage) {
10257                                                                 // design borders around HTML cells.
10258                                                                 for ($page=$startpage; $page <= $endpage; $page++) {
10259                                                                         $this->setPage($page);
10260                                                                         if ($page == $startpage) {
10261                                                                                 $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin();
10262                                                                                 $ch = $restspace;
10263                                                                         } elseif ($page == $endpage) {
10264                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10265                                                                                 $ch = $endy - $this->tMargin;
10266                                                                         } else {
10267                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10268                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
10269                                                                         }
10270
10271                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10272                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
10273                                                                                 $fill = true;
10274                                                                         } else {
10275                                                                                 $fill = false;
10276                                                                         }
10277                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
10278                                                                         $this->x = $cellpos['startx'];
10279                                                                         // design a cell around the text
10280                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10281                                                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10282                                                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10283                                                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10284                                                                         $this->intmrk[$this->page] += strlen($ccode."\n");
10285                                                                 }
10286                                                         } else {
10287                                                                 $ch = $endy - $parent['starty'];
10288                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10289                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
10290                                                                         $fill = true;
10291                                                                 } else {
10292                                                                         $fill = false;
10293                                                                 }
10294                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
10295                                                                 $this->x = $cellpos['startx'];
10296                                                                 $this->y = $parent['starty'];
10297                                                                 // design a cell around the text
10298                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10299                                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10300                                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10301                                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10302                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
10303                                                         }
10304                                                 }
10305                                                 if (isset($table_el['attribute']['cellspacing'])) {
10306                                                         $cellspacing = $this->pixelsToUnits($table_el['attribute']['cellspacing']);
10307                                                         $this->y += $cellspacing;
10308                                                 }
10309                                                 $this->Ln(0, $cell);
10310                                                 $this->x = $parent['startx'];
10311                                         }
10312                                         if (isset($parent['cellpadding'])) {
10313                                                 $this->cMargin = $this->oldcMargin;
10314                                         }
10315                                         //set row height
10316                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10317                                         break;
10318                                 }
10319                                 case 'b': {
10320                                         $this->setStyle('b', false);
10321                                         break;
10322                                 }
10323                                 case 'i': {
10324                                         $this->setStyle('i', false);
10325                                         break;
10326                                 }
10327                                 case 'u': {
10328                                         $this->setStyle('u', false);
10329                                         break;
10330                                 }
10331                                 case 'del': {
10332                                         $this->setStyle('d', false);
10333                                         break;
10334                                 }
10335                                 case 'a': {
10336                                         $this->HREF = '';
10337                                         break;
10338                                 }
10339                                 case 'sup': {
10340                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
10341                                         break;
10342                                 }
10343                                 case 'sub': {
10344                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
10345                                         break;
10346                                 }
10347                                 case 'div': {
10348                                         $this->addHTMLVertSpace(1, $cell);
10349                                         break;
10350                                 }
10351                                 case 'blockquote': {
10352                                         if ($this->rtl) {
10353                                                 $this->rMargin -= $this->listindent;
10354                                         } else {
10355                                                 $this->lMargin -= $this->listindent;
10356                                         }
10357                                         $this->addHTMLVertSpace(2, $cell);
10358                                         break;
10359                                 }
10360                                 case 'p': {
10361                                         $this->addHTMLVertSpace(2, $cell);
10362                                         break;
10363                                 }
10364                                 case 'dl': {
10365                                         $this->listnum--;
10366                                         if ($this->listnum <= 0) {
10367                                                 $this->listnum = 0;
10368                                                 $this->addHTMLVertSpace(2, $cell);
10369                                         }
10370                                         break;
10371                                 }
10372                                 case 'dt': {
10373                                         $this->lispacer = "";
10374                                         break;
10375                                 }
10376                                 case 'dd': {
10377                                         $this->lispacer = "";
10378                                         if ($this->rtl) {
10379                                                 $this->rMargin -= $this->listindent;
10380                                         } else {
10381                                                 $this->lMargin -= $this->listindent;
10382                                         }
10383                                         break;
10384                                 }
10385                                 case 'ul':
10386                                 case 'ol': {
10387                                         $this->listnum--;
10388                                         $this->lispacer = "";
10389                                         if ($this->rtl) {
10390                                                 $this->rMargin -= $this->listindent;
10391                                         } else {
10392                                                 $this->lMargin -= $this->listindent;
10393                                         }
10394                                         if ($this->listnum <= 0) {
10395                                                 $this->listnum = 0;
10396                                                 $this->addHTMLVertSpace(2, $cell);
10397                                         }
10398                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10399                                         break;
10400                                 }
10401                                 case 'li': {
10402                                         $this->lispacer = "";
10403                                         break;
10404                                 }
10405                                 case 'h1':
10406                                 case 'h2':
10407                                 case 'h3':
10408                                 case 'h4':
10409                                 case 'h5':
10410                                 case 'h6': {
10411                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k);
10412                                         break;
10413                                 }
10414                                 default : {
10415                                         break;
10416                                 }
10417                         }
10418                         $this->tmprtl = false;
10419                 }
10420                 
10421                 /**
10422                  * Add vertical spaces if needed.
10423                  * @param int $n number of spaces to add
10424                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10425                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
10426                  * @access protected
10427                  */
10428                 function addHTMLVertSpace($n, $cell=false, $h='') {
10429                         if (is_string($h)) {
10430                                 $vsize = $n * $this->lasth;
10431                         } else {
10432                                 $vsize = $n * $h;
10433                         }
10434                         if ($vsize > $this->htmlvspace) {
10435                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
10436                                 $this->htmlvspace = $vsize;
10437             }
10438         }
10439                 
10440         } // END OF TCPDF CLASS
10441 }
10442 //============================================================+
10443 // END OF FILE
10444 //============================================================+