a7cbd2d960ba60ce526a42f227793b9e45174810
[fa-stable.git] / reporting / includes / tcpdf.php
1 <?php
2 //============================================================+
3 // File name   : tcpdf.php
4 // Begin       : 2002-08-03
5 // Last Update : 2008-08-05
6 // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
7 // Version     : 4.0.017_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 and CMYK colors and transparency;
56 //      * supports links;
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 // Anyone that has reported a bug or sent a suggestion.
83 //============================================================+
84
85 /**
86  * This is a PHP class for generating PDF documents without requiring external extensions.<br>
87  * 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>
88  * <h3>TCPDF main features are:</h3>
89  * <ul>
90  * <li>no external libraries are required for the basic functions;</li>
91  * <li>supports all ISO page formats;</li>
92  * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
93  * <li>supports document encryption;</li>
94  * <li>includes methods to publish some XHTML code;</li>
95  * <li>includes graphic (geometric) and transformation methods;</li>
96  * <li>includes bookmarks;</li>
97  * <li>includes Javascript and forms support;</li>
98  * <li>includes a method to print various barcode formats;</li>
99  * <li>supports TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
100  * <li>supports custom page formats, margins and units of measure;</li>
101  * <li>includes methods for page header and footer management;</li>
102  * <li>supports automatic page break;</li>
103  * <li>supports automatic page numbering and page groups;</li>
104  * <li>supports automatic line break and text justification;
105  * <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>
106  * <li>supports stroke and clipping mode for text;</li>
107  * <li>supports clipping masks;</li>
108  * <li>supports Grayscale, RGB and CMYK colors and transparency;</li>
109  * <li>supports links;</li>
110  * <li>supports page compression (requires zlib extension);</li>
111  * <li>supports PDF user's rights.</li>
112  * </ul>
113  * Tools to encode your unicode fonts are on fonts/utils directory.</p>
114  * @package com.tecnick.tcpdf
115  * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
116  * @author Nicola Asuni
117  * @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
118  * @link http://www.tcpdf.org
119  * @license http://www.gnu.org/copyleft/lesser.html LGPL
120  * @version 4.0.017_PHP4
121  */
122
123 /**
124  * main configuration file
125  */
126 /** -------------------------------FrontAccounting 2.0  ---------------------------
127   * following changes are done for FrontAccounting 2.0 - Joe Hunt 06.08.2008
128   * 1. /config/tcpdf_config.php is not included, commented out
129   * 2. Following 3 defines instead:
130   *    if (!defined("K_PATH_FONTS"))
131   *        define ("K_PATH_FONTS", '../reporting/fonts/');
132   *    define ("K_PATH_CACHE", '../reporting/fonts/');
133   *    define("K_CELL_HEIGHT_RATIO", 1.25);
134   * 3. ./unicode_data2.php only included if unicode is set. (in class constructor)
135   *    We only use a reduced variant of unicode_data.php (unicode_data.php).af wrap the
136   *    following defines
137   *    if (!defined("K_RE_PATTERN_RTL"))
138   *    and
139   *    if (!defined("K_RE_PATTERN_ARABIC"))
140   * 4. Parameter $unicode in constructor renamed to $uni.
141   * 4. Header function renamed to Header1 (due to conflict with FrontReport Header)
142   * -------------------------------------------------------------------------------
143   */
144 if (!defined("K_PATH_FONTS"))
145         define ("K_PATH_FONTS", '../reporting/fonts/');
146 define ("K_PATH_CACHE", '../reporting/fonts/');
147 define("K_CELL_HEIGHT_RATIO", 1.25);
148
149 //require_once(dirname(__FILE__).'/config/tcpdf_config.php');
150
151 // includes some support files
152
153 /**
154  * unicode data
155  */
156 // only included if unicode
157 //include_once(dirname(__FILE__)."/unicode_data2.php");
158
159 /**
160  * html colors table
161  */
162 require_once(dirname(__FILE__).'/htmlcolors.php');
163
164 /**
165  * barcode class
166  */
167 require_once(dirname(__FILE__)."/barcodes.php");
168
169 /**
170  * HTML entity decode functions
171  */
172 require_once(dirname(__FILE__)."/html_entity_decode_php4.php");
173
174 if (!class_exists('TCPDF')) {
175         /**
176          * define default PDF document producer
177          */
178         define('PDF_PRODUCER','TCPDF 4.0.017_PHP4 (http://www.tcpdf.org)');
179
180         /**
181         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
182         * 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>
183         * @name TCPDF
184         * @package com.tecnick.tcpdf
185         * @version 4.0.017_PHP4
186         * @author Nicola Asuni - info@tecnick.com
187         * @link http://www.tcpdf.org
188         * @license http://www.gnu.org/copyleft/lesser.html LGPL
189         */
190         class TCPDF {
191                 // protected or Protected properties
192
193                 /**
194                 * @var current page number
195                 * @access protected
196                 */
197                 var $page;
198
199                 /**
200                 * @var current object number
201                 * @access protected
202                 */
203                 var $n;
204
205                 /**
206                 * @var array of object offsets
207                 * @access protected
208                 */
209                 var $offsets;
210
211                 /**
212                 * @var buffer holding in-memory PDF
213                 * @access protected
214                 */
215                 var $buffer;
216
217                 /**
218                 * @var array containing pages
219                 * @access protected
220                 */
221                 var $pages = array();
222
223                 /**
224                 * @var current document state
225                 * @access protected
226                 */
227                 var $state;
228
229                 /**
230                 * @var compression flag
231                 * @access protected
232                 */
233                 var $compress;
234
235                 /**
236                 * @var current page orientation (P = Portrait, L = Landscape)
237                 * @access protected
238                 */
239                 var $CurOrientation;
240
241                 /**
242                 * @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>
243                 * @access protected
244                 */
245                 var $pagedim = array();
246
247                 /**
248                 * @var scale factor (number of points in user unit)
249                 * @access protected
250                 */
251                 var $k;
252
253                 /**
254                 * @var width of page format in points
255                 * @access protected
256                 */
257                 var $fwPt;
258
259                 /**
260                 * @var height of page format in points
261                 * @access protected
262                 */
263                 var $fhPt;
264
265                 /**
266                 * @var current width of page in points
267                 * @access protected
268                 */
269                 var $wPt;
270
271                 /**
272                 * @var current height of page in points
273                 * @access protected
274                 */
275                 var $hPt;
276
277                 /**
278                 * @var current width of page in user unit
279                 * @access protected
280                 */
281                 var $w;
282
283                 /**
284                 * @var current height of page in user unit
285                 * @access protected
286                 */
287                 var $h;
288
289                 /**
290                 * @var left margin
291                 * @access protected
292                 */
293                 var $lMargin;
294
295                 /**
296                 * @var top margin
297                 * @access protected
298                 */
299                 var $tMargin;
300
301                 /**
302                 * @var right margin
303                 * @access protected
304                 */
305                 var $rMargin;
306
307                 /**
308                 * @var page break margin
309                 * @access protected
310                 */
311                 var $bMargin;
312
313                 /**
314                 * @var cell internal padding
315                 * @access protected
316                 */
317                 var $cMargin;
318
319                 /**
320                 * @var cell internal padding (previous value)
321                 * @access protected
322                 */
323                 var $oldcMargin;
324
325                 /**
326                 * @var current horizontal position in user unit for cell positioning
327                 * @access protected
328                 */
329                 var $x;
330
331                 /**
332                 * @var current vertical position in user unit for cell positioning
333                 * @access protected
334                 */
335                 var $y;
336
337                 /**
338                 * @var height of last cell printed
339                 * @access protected
340                 */
341                 var $lasth;
342
343                 /**
344                 * @var line width in user unit
345                 * @access protected
346                 */
347                 var $LineWidth;
348
349                 /**
350                 * @var array of standard font names
351                 * @access protected
352                 */
353                 var $CoreFonts;
354
355                 /**
356                 * @var array of used fonts
357                 * @access protected
358                 */
359                 var $fonts = array();
360
361                 /**
362                 * @var array of font files
363                 * @access protected
364                 */
365                 var $FontFiles = array();
366
367                 /**
368                 * @var array of encoding differences
369                 * @access protected
370                 */
371                 var $diffs = array();
372
373                 /**
374                 * @var array of used images
375                 * @access protected
376                 */
377                 var $images = array();
378
379                 /**
380                 * @var array of links in pages
381                 * @access protected
382                 */
383                 var $PageLinks = array();
384
385                 /**
386                 * @var array of internal links
387                 * @access protected
388                 */
389                 var $links = array();
390
391                 /**
392                 * @var current font family
393                 * @access protected
394                 */
395                 var $FontFamily;
396
397                 /**
398                 * @var current font style
399                 * @access protected
400                 */
401                 var $FontStyle;
402
403                 /**
404                 * @var current font ascent (distance between font top and baseline)
405                 * @access protected
406                 * @since 2.8.000 (2007-03-29)
407                 */
408                 var $FontAscent;
409
410                 /**
411                 * @var current font descent (distance between font bottom and baseline)
412                 * @access protected
413                 * @since 2.8.000 (2007-03-29)
414                 */
415                 var $FontDescent;
416
417                 /**
418                 * @var underlining flag
419                 * @access protected
420                 */
421                 var $underline;
422
423                 /**
424                 * @var current font info
425                 * @access protected
426                 */
427                 var $CurrentFont;
428
429                 /**
430                 * @var current font size in points
431                 * @access protected
432                 */
433                 var $FontSizePt;
434
435                 /**
436                 * @var current font size in user unit
437                 * @access protected
438                 */
439                 var $FontSize;
440
441                 /**
442                 * @var commands for drawing color
443                 * @access protected
444                 */
445                 var $DrawColor;
446
447                 /**
448                 * @var commands for filling color
449                 * @access protected
450                 */
451                 var $FillColor;
452
453                 /**
454                 * @var commands for text color
455                 * @access protected
456                 */
457                 var $TextColor;
458
459                 /**
460                 * @var indicates whether fill and text colors are different
461                 * @access protected
462                 */
463                 var $ColorFlag;
464
465                 /**
466                 * @var word spacing
467                 * @access protected
468                 */
469                 var $ws;
470
471                 /**
472                 * @var automatic page breaking
473                 * @access protected
474                 */
475                 var $AutoPageBreak;
476
477                 /**
478                 * @var threshold used to trigger page breaks
479                 * @access protected
480                 */
481                 var $PageBreakTrigger;
482
483                 /**
484                 * @var flag set when processing footer
485                 * @access protected
486                 */
487                 var $InFooter;
488
489                 /**
490                 * @var zoom display mode
491                 * @access protected
492                 */
493                 var $ZoomMode;
494
495                 /**
496                 * @var layout display mode
497                 * @access protected
498                 */
499                 var $LayoutMode;
500
501                 /**
502                 * @var title
503                 * @access protected
504                 */
505                 var $title;
506
507                 /**
508                 * @var subject
509                 * @access protected
510                 */
511                 var $subject;
512
513                 /**
514                 * @var author
515                 * @access protected
516                 */
517                 var $author;
518
519                 /**
520                 * @var keywords
521                 * @access protected
522                 */
523                 var $keywords;
524
525                 /**
526                 * @var creator
527                 * @access protected
528                 */
529                 var $creator;
530
531                 /**
532                 * @var alias for total number of pages
533                 * @access protected
534                 */
535                 var $AliasNbPages;
536
537                 /**
538                 * @var right-bottom corner X coordinate of inserted image
539                 * @since 2002-07-31
540                 * @author Nicola Asuni
541                 * @access protected
542                 */
543                 var $img_rb_x;
544
545                 /**
546                 * @var right-bottom corner Y coordinate of inserted image
547                 * @since 2002-07-31
548                 * @author Nicola Asuni
549                 * @access protected
550                 */
551                 var $img_rb_y;
552
553                 /**
554                 * @var image scale factor
555                 * @since 2004-06-14
556                 * @author Nicola Asuni
557                 * @access protected
558                 */
559                 var $imgscale = 1;
560
561                 /**
562                 * @var boolean set to true when the input text is unicode (require unicode fonts)
563                 * @since 2005-01-02
564                 * @author Nicola Asuni
565                 * @access protected
566                 */
567                 var $isunicode = false;
568
569                 /**
570                 * @var PDF version
571                 * @since 1.5.3
572                 * @access protected
573                 */
574                 var $PDFVersion = "1.7";
575
576
577                 // ----------------------
578
579                 /**
580                  * @var Minimum distance between header and top page margin.
581                  * @access protected
582                  */
583                 var $header_margin;
584
585                 /**
586                  * @var Minimum distance between footer and bottom page margin.
587                  * @access protected
588                  */
589                 var $footer_margin;
590
591                 /**
592                  * @var original left margin value
593                  * @access protected
594                  * @since 1.53.0.TC013
595                  */
596                 var $original_lMargin;
597
598                 /**
599                  * @var original right margin value
600                  * @access protected
601                  * @since 1.53.0.TC013
602                  */
603                 var $original_rMargin;
604
605                 /**
606                  * @var Header font.
607                  * @access protected
608                  */
609                 var $header_font;
610
611                 /**
612                  * @var Footer font.
613                  * @access protected
614                  */
615                 var $footer_font;
616
617                 /**
618                  * @var Language templates.
619                  * @access protected
620                  */
621                 var $l;
622
623                 /**
624                  * @var Barcode to print on page footer (only if set).
625                  * @access protected
626                  */
627                 var $barcode = false;
628
629                 /**
630                  * @var If true prints header
631                  * @access protected
632                  */
633                 var $print_header = true;
634
635                 /**
636                  * @var If true prints footer.
637                  * @access protected
638                  */
639                 var $print_footer = true;
640
641                 /**
642                  * @var Header image logo.
643                  * @access protected
644                  */
645                 var $header_logo = "";
646
647                 /**
648                  * @var Header image logo width in mm.
649                  * @access protected
650                  */
651                 var $header_logo_width = 30;
652
653                 /**
654                  * @var String to print as title on document header.
655                  * @access protected
656                  */
657                 var $header_title = "";
658
659                 /**
660                  * @var String to print on document header.
661                  * @access protected
662                  */
663                 var $header_string = "";
664
665                 /**
666                  * @var Default number of columns for html table.
667                  * @access protected
668                  */
669                 var $default_table_columns = 4;
670
671
672                 // variables for html parser
673
674                 /**
675                  * @var HTML PARSER: store current link.
676                  * @access protected
677                  */
678                 var $HREF;
679
680                 /**
681                  * @var store available fonts list.
682                  * @access protected
683                  */
684                 var $fontlist = array();
685
686                 /**
687                  * @var current foreground color
688                  * @access protected
689                  */
690                 var $fgcolor;
691
692                 /**
693                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
694                  * @access protected
695                  */
696                 var $listordered = array();
697
698                 /**
699                  * @var HTML PARSER: array count list items on nested lists.
700                  * @access protected
701                  */
702                 var $listcount = array();
703
704                 /**
705                  * @var HTML PARSER: current list nesting level.
706                  * @access protected
707                  */
708                 var $listnum = 0;
709
710                 /**
711                  * @var HTML PARSER: indent amount for lists.
712                  * @access protected
713                  */
714                 var $listindent;
715
716                 /**
717                  * @var current background color
718                  * @access protected
719                  */
720                 var $bgcolor;
721
722                 /**
723                  * @var Store temporary font size in points.
724                  * @access protected
725                  */
726                 var $tempfontsize = 10;
727
728                 /**
729                  * @var Bold font style status.
730                  * @access protected
731                  */
732                 var $b;
733
734                 /**
735                  * @var Underlined font style status.
736                  * @access protected
737                  */
738                 var $u;
739
740                 /**
741                  * @var Italic font style status.
742                  * @access protected
743                  */
744                 var $i;
745
746                 /**
747                  * @var Line through font style status.
748                  * @access protected
749                  * @since 2.8.000 (2008-03-19)
750                  */
751                 var $d;
752
753                 /**
754                  * @var spacer for LI tags.
755                  * @access protected
756                  */
757                 var $lispacer = "";
758
759                 /**
760                  * @var default encoding
761                  * @access protected
762                  * @since 1.53.0.TC010
763                  */
764                 var $encoding = "UTF-8";
765
766                 /**
767                  * @var PHP internal encoding
768                  * @access protected
769                  * @since 1.53.0.TC016
770                  */
771                 var $internal_encoding;
772
773                 /**
774                  * @var indicates if the document language is Right-To-Left
775                  * @access protected
776                  * @since 2.0.000
777                  */
778                 var $rtl = false;
779
780                 /**
781                  * @var used to force RTL or LTR string inversion
782                  * @access protected
783                  * @since 2.0.000
784                  */
785                 var $tmprtl = false;
786
787                 // --- Variables used for document encryption:
788
789                 /**
790                  * Indicates whether document is protected
791                  * @access protected
792                  * @since 2.0.000 (2008-01-02)
793                  */
794                 var $encrypted;
795
796                 /**
797                  * U entry in pdf document
798                  * @access protected
799                  * @since 2.0.000 (2008-01-02)
800                  */
801                 var $Uvalue;
802
803                 /**
804                  * O entry in pdf document
805                  * @access protected
806                  * @since 2.0.000 (2008-01-02)
807                  */
808                 var $Ovalue;
809
810                 /**
811                  * P entry in pdf document
812                  * @access protected
813                  * @since 2.0.000 (2008-01-02)
814                  */
815                 var $Pvalue;
816
817                 /**
818                  * encryption object id
819                  * @access protected
820                  * @since 2.0.000 (2008-01-02)
821                  */
822                 var $enc_obj_id;
823
824                 /**
825                  * last RC4 key encrypted (cached for optimisation)
826                  * @access protected
827                  * @since 2.0.000 (2008-01-02)
828                  */
829                 var $last_rc4_key;
830
831                 /**
832                  * last RC4 computed key
833                  * @access protected
834                  * @since 2.0.000 (2008-01-02)
835                  */
836                 var $last_rc4_key_c;
837
838                 // --- bookmark ---
839
840                 /**
841                  * Outlines for bookmark
842                  * @access protected
843                  * @since 2.1.002 (2008-02-12)
844                  */
845                 var $outlines = array();
846
847                 /**
848                  * Outline root for bookmark
849                  * @access protected
850                  * @since 2.1.002 (2008-02-12)
851                  */
852                 var $OutlineRoot;
853
854
855                 // --- javascript and form ---
856
857                 /**
858                  * javascript code
859                  * @access protected
860                  * @since 2.1.002 (2008-02-12)
861                  */
862                 var $javascript = "";
863
864                 /**
865                  * javascript counter
866                  * @access protected
867                  * @since 2.1.002 (2008-02-12)
868                  */
869                 var $n_js;
870
871                 /**
872                  * line trough state
873                  * @access protected
874                  * @since 2.8.000 (2008-03-19)
875                  */
876                 var $linethrough;
877
878                 // --- Variables used for User's Rights ---
879                 // See PDF reference chapter 8.7 Digital Signatures
880
881                 /**
882                  * If true enables user's rights on PDF reader
883                  * @access protected
884                  * @since 2.9.000 (2008-03-26)
885                  */
886                 var $ur;
887
888                 /**
889                  * Names specifying additional document-wide usage rights for the document.
890                  * @access protected
891                  * @since 2.9.000 (2008-03-26)
892                  */
893                 var $ur_document;
894
895                 /**
896                  * Names specifying additional annotation-related usage rights for the document.
897                  * @access protected
898                  * @since 2.9.000 (2008-03-26)
899                  */
900                 var $ur_annots;
901
902                 /**
903                  * Names specifying additional form-field-related usage rights for the document.
904                  * @access protected
905                  * @since 2.9.000 (2008-03-26)
906                  */
907                 var $ur_form;
908
909                 /**
910                  * Names specifying additional signature-related usage rights for the document.
911                  * @access protected
912                  * @since 2.9.000 (2008-03-26)
913                  */
914                 var $ur_signature;
915
916                 /**
917                  * Dot Per Inch Document Resolution (do not change)
918                  * @access protected
919                  * @since 3.0.000 (2008-03-27)
920                  */
921                 var $dpi = 72;
922
923                 /**
924                  * Indicates whether a new page group was requested
925                  * @access protected
926                  * @since 3.0.000 (2008-03-27)
927                  */
928                 var $newpagegroup;
929
930                 /**
931                  * Contains the number of pages of the groups
932                  * @access protected
933                  * @since 3.0.000 (2008-03-27)
934                  */
935                 var $pagegroups;
936
937                 /**
938                  * Contains the alias of the current page group
939                  * @access protected
940                  * @since 3.0.000 (2008-03-27)
941                  */
942                 var $currpagegroup;
943
944                 /**
945                  * Restrict the rendering of some elements to screen or printout.
946                  * @access protected
947                  * @since 3.0.000 (2008-03-27)
948                  */
949                 var $visibility="all";
950
951                 /**
952                  * Print visibility.
953                  * @access protected
954                  * @since 3.0.000 (2008-03-27)
955                  */
956                 var $n_ocg_print;
957
958                 /**
959                  * View visibility.
960                  * @access protected
961                  * @since 3.0.000 (2008-03-27)
962                  */
963                 var $n_ocg_view;
964
965                 /**
966                  * Array of transparency objects and parameters.
967                  * @access protected
968                  * @since 3.0.000 (2008-03-27)
969                  */
970                 var $extgstates;
971
972                 /**
973                  * Set the default JPEG compression quality (1-100)
974                  * @access protected
975                  * @since 3.0.000 (2008-03-27)
976                  */
977                 var $jpeg_quality;
978
979                 /**
980                  * Default cell height ratio.
981                  * @access protected
982                  * @since 3.0.014 (2008-05-23)
983                  */
984                 var $cell_height_ratio = K_CELL_HEIGHT_RATIO;
985
986                 /**
987                  * PDF viewer preferences.
988                  * @access protected
989                  * @since 3.1.000 (2008-06-09)
990                  */
991                 var $viewer_preferences;
992
993                 /**
994                  * A name object specifying how the document should be displayed when opened.
995                  * @access protected
996                  * @since 3.1.000 (2008-06-09)
997                  */
998                 var $PageMode;
999
1000                 /**
1001                  * Array for storing gradient information.
1002                  * @access protected
1003                  * @since 3.1.000 (2008-06-09)
1004                  */
1005                 var $gradients = array();
1006
1007                 /**
1008                  * Array used to store positions inside the pages buffer.
1009                  * keys are the page numbers
1010                  * @access protected
1011                  * @since 3.2.000 (2008-06-26)
1012                  */
1013                 var $intmrk = array();
1014
1015                 /**
1016                  * Array used to store footer positions of each page.
1017                  * @access protected
1018                  * @since 3.2.000 (2008-07-01)
1019                  */
1020                 var $footerpos = array();
1021
1022
1023                 /**
1024                  * Array used to store footer lenght of each page.
1025                  * @access protected
1026                  * @since 4.0.014 (2008-07-29)
1027                  */
1028                 var $footerlen = array();
1029
1030                 /**
1031                  * True if a newline is created.
1032                  * @access protected
1033                  * @since 3.2.000 (2008-07-01)
1034                  */
1035                 var $newline = true;
1036
1037                 /**
1038                  * End position of the latest inserted line
1039                  * @access protected
1040                  * @since 3.2.000 (2008-07-01)
1041                  */
1042                 var $endlinex = 0;
1043
1044                 /**
1045                  * PDF string for last line width
1046                  * @access protected
1047                  * @since 4.0.006 (2008-07-16)
1048                  */
1049                 var $linestyleWidth = "";
1050
1051                 /**
1052                  * PDF string for last line width
1053                  * @access protected
1054                  * @since 4.0.006 (2008-07-16)
1055                  */
1056                 var $linestyleCap = "0 J";
1057
1058                 /**
1059                  * PDF string for last line width
1060                  * @access protected
1061                  * @since 4.0.006 (2008-07-16)
1062                  */
1063                 var $linestyleJoin = "0 j";
1064
1065                 /**
1066                  * PDF string for last line width
1067                  * @access protected
1068                  * @since 4.0.006 (2008-07-16)
1069                  */
1070                 var $linestyleDash = "[] 0 d";
1071
1072                 /**
1073                  * True if marked-content sequence is open
1074                  * @access protected
1075                  * @since 4.0.013 (2008-07-28)
1076                  */
1077                 var $openMarkedContent = false;
1078
1079                 //------------------------------------------------------------
1080                 // METHODS
1081                 //------------------------------------------------------------
1082
1083                 /**
1084                  * This is the class constructor.
1085                  * It allows to set up the page format, the orientation and
1086                  * the measure unit used in all the methods (except for the font sizes).
1087                  * @since 1.0
1088                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1089                  * @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.
1090                  * @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>
1091                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1092                  * @param String $encoding charset encoding; default is UTF-8
1093                  */
1094                 function TCPDF($orientation='P', $unit='mm', $format='A4', $uni=true, $encoding="UTF-8") {
1095                         if ($uni) // Fix for FrontAccounting
1096                         {
1097                                 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
1098                                 include_once(dirname(__FILE__)."/unicode_data2.php");
1099                         }
1100                         /* Set internal character encoding to ASCII */
1101                         if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) {
1102                                 $this->internal_encoding = mb_internal_encoding();
1103                                 mb_internal_encoding("ASCII");
1104                         }
1105                         // set language direction
1106                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1107                         $this->tmprtl = false;
1108                         //Some checks
1109                         $this->_dochecks();
1110                         //Initialization of properties
1111                         $this->isunicode = $uni;
1112                         $this->page = 0;
1113                         $this->pagedim = array();
1114                         $this->n = 2;
1115                         $this->buffer = '';
1116                         $this->pages = array();
1117                         $this->state = 0;
1118                         $this->fonts = array();
1119                         $this->FontFiles = array();
1120                         $this->diffs = array();
1121                         $this->images = array();
1122                         $this->links = array();
1123                         $this->gradients = array();
1124                         $this->InFooter = false;
1125                         $this->lasth = 0;
1126                         $this->FontFamily = 'helvetica';
1127                         $this->FontStyle = '';
1128                         $this->FontSizePt = 12;
1129                         $this->underline = false;
1130                         $this->linethrough = false;
1131                         $this->DrawColor = '0 G';
1132                         $this->FillColor = '0 g';
1133                         $this->TextColor = '0 g';
1134                         $this->ColorFlag = false;
1135                         $this->ws = 0;
1136                         // encryption values
1137                         $this->encrypted = false;
1138                         $this->last_rc4_key = '';
1139                         $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";
1140                         //Standard Unicode fonts
1141                         $this->CoreFonts = array(
1142                                 'courier'=>'Courier',
1143                                 'courierB'=>'Courier-Bold',
1144                                 'courierI'=>'Courier-Oblique',
1145                                 'courierBI'=>'Courier-BoldOblique',
1146                                 'helvetica'=>'Helvetica',
1147                                 'helveticaB'=>'Helvetica-Bold',
1148                                 'helveticaI'=>'Helvetica-Oblique',
1149                                 'helveticaBI'=>'Helvetica-BoldOblique',
1150                                 'times'=>'Times-Roman',
1151                                 'timesB'=>'Times-Bold',
1152                                 'timesI'=>'Times-Italic',
1153                                 'timesBI'=>'Times-BoldItalic',
1154                                 'symbol'=>'Symbol',
1155                                 'zapfdingbats'=>'ZapfDingbats'
1156                         );
1157                         //Set scale factor
1158                         $this->setPageUnit($unit);
1159                         // set page format and orientation
1160                         $this->setPageFormat($format, $orientation);
1161                         //Page margins (1 cm)
1162                         $margin = 28.35 / $this->k;
1163                         $this->SetMargins($margin,$margin);
1164                         //Interior cell margin (1 mm)
1165                         $this->cMargin = $margin / 10;
1166                         //Line width (0.2 mm)
1167                         $this->LineWidth = 0.57 / $this->k;
1168                         $this->linestyleWidth = sprintf('%.2f w', ($this->LineWidth * $this->k));
1169                         $this->linestyleCap = "0 J";
1170                         $this->linestyleJoin = "0 j";
1171                         $this->linestyleDash = "[] 0 d";
1172                         //Automatic page break
1173                         $this->SetAutoPageBreak(true, 2*$margin);
1174                         //Full width display mode
1175                         $this->SetDisplayMode('fullwidth');
1176                         //Compression
1177                         $this->SetCompression(true);
1178                         //Set default PDF version number
1179                         $this->PDFVersion = "1.7";
1180                         $this->encoding = $encoding;
1181                         $this->HREF = '';
1182                         $this->getFontsList();
1183                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1184                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1185                         $this->extgstates = array();
1186                         // user's rights
1187                         $this->ur = false;
1188                         $this->ur_document = "/FullSave";
1189                         $this->ur_annots = "/Create/Delete/Modify/Copy/Import/Export";
1190                         $this->ur_form = "/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate";
1191                         $this->ur_signature = "/Modify";
1192                         // set default JPEG quality
1193                         $this->jpeg_quality = 75;
1194                         // initialize some settings
1195                         $this->utf8Bidi(array(""));
1196                 }
1197
1198                 /**
1199                  * Default destructor.
1200                  * @since 1.53.0.TC016
1201                  */
1202                 function TCPDFDestruct() {
1203                         // restore internal encoding
1204                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1205                                 mb_internal_encoding($this->internal_encoding);
1206                         }
1207                 }
1208
1209                 /**
1210                 * Set the units of measure for the document.
1211                 * @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.
1212                 * @since 3.0.015 (2008-06-06)
1213                 */
1214                 function setPageUnit($unit) {
1215                 //Set scale factor
1216                         switch (strtolower($unit)) {
1217                                 // points
1218                                 case 'pt': {
1219                                         $this->k = 1;
1220                                         break;
1221                                 }
1222                                 // millimeters
1223                                 case 'mm': {
1224                                         $this->k = $this->dpi / 25.4;
1225                                         break;
1226                                 }
1227                                 // centimeters
1228                                 case 'cm': {
1229                                         $this->k = $this->dpi / 2.54;
1230                                         break;
1231                                 }
1232                                 // inches
1233                                 case 'in': {
1234                                         $this->k = $this->dpi;
1235                                         break;
1236                                 }
1237                                 // unsupported unit
1238                                 default : {
1239                                         $this->Error('Incorrect unit: '.$unit);
1240                                         break;
1241                                 }
1242                         }
1243                         if (isset($this->CurOrientation)) {
1244                                         $this->setPageOrientation($this->CurOrientation);
1245                         }
1246                 }
1247
1248                 /**
1249                 * Set the page format
1250                 * @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>
1251                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1252                 * @since 3.0.015 (2008-06-06)
1253                 */
1254                 function setPageFormat($format, $orientation="P") {
1255                         //Page format
1256                         if (is_string($format)) {
1257                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1258                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1259                                 switch (strtoupper($format)){
1260                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1261                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1262                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1263                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1264                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1265                                         case 'A3': {$format = array(841.89,1190.55); break;}
1266                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1267                                         case 'A5': {$format = array(419.53,595.28); break;}
1268                                         case 'A6': {$format = array(297.64,419.53); break;}
1269                                         case 'A7': {$format = array(209.76,297.64); break;}
1270                                         case 'A8': {$format = array(147.40,209.76); break;}
1271                                         case 'A9': {$format = array(104.88,147.40); break;}
1272                                         case 'A10': {$format = array(73.70,104.88); break;}
1273                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1274                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1275                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1276                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1277                                         case 'B4': {$format = array(708.66,1000.63); break;}
1278                                         case 'B5': {$format = array(498.90,708.66); break;}
1279                                         case 'B6': {$format = array(354.33,498.90); break;}
1280                                         case 'B7': {$format = array(249.45,354.33); break;}
1281                                         case 'B8': {$format = array(175.75,249.45); break;}
1282                                         case 'B9': {$format = array(124.72,175.75); break;}
1283                                         case 'B10': {$format = array(87.87,124.72); break;}
1284                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1285                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1286                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1287                                         case 'C3': {$format = array(918.43,1298.27); break;}
1288                                         case 'C4': {$format = array(649.13,918.43); break;}
1289                                         case 'C5': {$format = array(459.21,649.13); break;}
1290                                         case 'C6': {$format = array(323.15,459.21); break;}
1291                                         case 'C7': {$format = array(229.61,323.15); break;}
1292                                         case 'C8': {$format = array(161.57,229.61); break;}
1293                                         case 'C9': {$format = array(113.39,161.57); break;}
1294                                         case 'C10': {$format = array(79.37,113.39); break;}
1295                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1296                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1297                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1298                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1299                                         case 'RA4': {$format = array(609.45,864.57); break;}
1300                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1301                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1302                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1303                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1304                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1305                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1306                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1307                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1308                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1309                                 }
1310                                 $this->fwPt = $format[0];
1311                                 $this->fhPt = $format[1];
1312                         }
1313                         else {
1314                                 $this->fwPt = $format[0] * $this->k;
1315                                 $this->fhPt = $format[1] * $this->k;
1316                         }
1317                         $this->setPageOrientation($orientation);
1318                 }
1319
1320
1321                 /**
1322                 * Set page orientation.
1323                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1324                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1325                 * @param float $bottommargin bottom margin of the page.
1326                 * @since 3.0.015 (2008-06-06)
1327                 */
1328                 function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1329                         $orientation = strtoupper($orientation);
1330                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1331                                 $this->CurOrientation = 'P';
1332                                 $this->wPt = $this->fwPt;
1333                                 $this->hPt = $this->fhPt;
1334                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1335                                 $this->CurOrientation = 'L';
1336                                 $this->wPt = $this->fhPt;
1337                                 $this->hPt = $this->fwPt;
1338                         }
1339                         else {
1340                                 $this->Error('Incorrect orientation: '.$orientation);
1341                         }
1342                         $this->w = $this->wPt / $this->k;
1343                         $this->h = $this->hPt / $this->k;
1344                         if (empty($autopagebreak)) {
1345                                 if (isset($this->AutoPageBreak)) {
1346                                         $autopagebreak = $this->AutoPageBreak;
1347                                 } else {
1348                                         $autopagebreak = true;
1349                                 }
1350                         }
1351                         if (empty($bottommargin)) {
1352                                 if (isset($this->bMargin)) {
1353                                         $bottommargin = $this->bMargin;
1354                                 } else {
1355                                         // default value = 2 cm
1356                                         $bottommargin = 2 * 28.35 / $this->k;
1357                                 }
1358                         }
1359                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1360                         // store page dimensions
1361                         $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);
1362                 }
1363
1364                 /**
1365                  * Enable or disable Right-To-Left language mode
1366                  * @param Boolean $enable if true enable Right-To-Left language mode.
1367                  * @since 2.0.000 (2008-01-03)
1368                  */
1369                 function setRTL($enable) {
1370                         $this->rtl = $enable ? true : false;
1371                         $this->tmprtl = false;
1372                 }
1373
1374                 /**
1375                  * Return the RTL status
1376                  * @return boolean
1377                  * @since 4.0.012 (2008-07-24)
1378                  */
1379                 function getRTL() {
1380                         return $this->rtl;
1381                 }
1382
1383                 /**
1384                 * Force temporary RTL language direction
1385                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1386                 * @since 2.1.000 (2008-01-09)
1387                 */
1388                 function setTempRTL($mode) {
1389                         switch ($mode) {
1390                                 case false:
1391                                 case 'L':
1392                                 case 'R': {
1393                                         $this->tmprtl = $mode;
1394                                 }
1395                         }
1396                 }
1397
1398                 /**
1399                 * Set the last cell height.
1400                 * @param float $h cell height.
1401                 * @author Nicola Asuni
1402                 * @since 1.53.0.TC034
1403                 */
1404                 function setLastH($h) {
1405                         $this->lasth = $h;
1406                 }
1407
1408                 /**
1409                 * Get the last cell height.
1410                 * @return last cell height
1411                 * @since 4.0.017 (2008-08-05)
1412                 */
1413                 function getLastH() {
1414                         return $this->lasth;
1415                 }
1416
1417                 /**
1418                 * Set the image scale.
1419                 * @param float $scale image scale.
1420                 * @author Nicola Asuni
1421                 * @since 1.5.2
1422                 */
1423                 function setImageScale($scale) {
1424                         $this->imgscale = $scale;
1425                 }
1426
1427                 /**
1428                 * Returns the image scale.
1429                 * @return float image scale.
1430                 * @author Nicola Asuni
1431                 * @since 1.5.2
1432                 */
1433                 function getImageScale() {
1434                         return $this->imgscale;
1435                 }
1436
1437                 /**
1438                 * Returns the page width in units.
1439                 * @return int page width.
1440                 * @author Nicola Asuni
1441                 * @since 1.5.2
1442                 */
1443                 function getPageWidth() {
1444                         return $this->w;
1445                 }
1446
1447                 /**
1448                 * Returns the page height in units.
1449                 * @return int page height.
1450                 * @author Nicola Asuni
1451                 * @since 1.5.2
1452                 */
1453                 function getPageHeight() {
1454                         return $this->h;
1455                 }
1456
1457                 /**
1458                 * Returns the page break margin.
1459                 * @return int page break margin.
1460                 * @author Nicola Asuni
1461                 * @since 1.5.2
1462                 */
1463                 function getBreakMargin() {
1464                         return $this->bMargin;
1465                 }
1466
1467                 /**
1468                 * Returns the scale factor (number of points in user unit).
1469                 * @return int scale factor.
1470                 * @author Nicola Asuni
1471                 * @since 1.5.2
1472                 */
1473                 function getScaleFactor() {
1474                         return $this->k;
1475                 }
1476
1477                 /**
1478                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1479                 * @param float $left Left margin.
1480                 * @param float $top Top margin.
1481                 * @param float $right Right margin. Default value is the left one.
1482                 * @since 1.0
1483                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1484                 */
1485                 function SetMargins($left, $top, $right=-1) {
1486                         //Set left, top and right margins
1487                         $this->lMargin = $left;
1488                         $this->tMargin = $top;
1489                         if ($right == -1) {
1490                                 $right = $left;
1491                         }
1492                         $this->rMargin = $right;
1493                 }
1494
1495                 /**
1496                 * 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.
1497                 * @param float $margin The margin.
1498                 * @since 1.4
1499                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1500                 */
1501                 function SetLeftMargin($margin) {
1502                         //Set left margin
1503                         $this->lMargin=$margin;
1504                         if (($this->page > 0) AND ($this->x < $margin)) {
1505                                 $this->x = $margin;
1506                         }
1507                 }
1508
1509                 /**
1510                 * Defines the top margin. The method can be called before creating the first page.
1511                 * @param float $margin The margin.
1512                 * @since 1.5
1513                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1514                 */
1515                 function SetTopMargin($margin) {
1516                         //Set top margin
1517                         $this->tMargin=$margin;
1518                         if (($this->page > 0) AND ($this->y < $margin)) {
1519                                 $this->y = $margin;
1520                         }
1521                 }
1522
1523                 /**
1524                 * Defines the right margin. The method can be called before creating the first page.
1525                 * @param float $margin The margin.
1526                 * @since 1.5
1527                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1528                 */
1529                 function SetRightMargin($margin) {
1530                         $this->rMargin=$margin;
1531                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1532                                 $this->x = $this->w - $margin;
1533                         }
1534                 }
1535
1536                 /**
1537                 * Set the internal Cell padding.
1538                 * @param float $pad internal padding.
1539                 * @since 2.1.000 (2008-01-09)
1540                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1541                 */
1542                 function SetCellPadding($pad) {
1543                         $this->cMargin = $pad;
1544                 }
1545
1546                 /**
1547                 * 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.
1548                 * @param boolean $auto Boolean indicating if mode should be on or off.
1549                 * @param float $margin Distance from the bottom of the page.
1550                 * @since 1.0
1551                 * @see Cell(), MultiCell(), AcceptPageBreak()
1552                 */
1553                 function SetAutoPageBreak($auto, $margin=0) {
1554                         //Set auto page break mode and triggering margin
1555                         $this->AutoPageBreak = $auto;
1556                         $this->bMargin = $margin;
1557                         $this->PageBreakTrigger = $this->h - $margin;
1558                 }
1559
1560                 /**
1561                 * Defines the way the document is to be displayed by the viewer.
1562                 * @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>
1563                 * @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>
1564                 * @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>
1565                 * @since 1.2
1566                 */
1567                 function SetDisplayMode($zoom, $layout='SinglePage', $mode="UseNone") {
1568                         //Set display mode in viewer
1569                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1570                                 $this->ZoomMode = $zoom;
1571                         } else {
1572                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1573                         }
1574                         switch ($layout) {
1575                                 case "default":
1576                                 case "single":
1577                                 case "SinglePage": {
1578                                         $this->LayoutMode = "SinglePage";
1579                                         break;
1580                                 }
1581                                 case "continuous":
1582                                 case "OneColumn": {
1583                                         $this->LayoutMode = "OneColumn";
1584                                         break;
1585                                 }
1586                                 case "two":
1587                                 case "TwoColumnLeft": {
1588                                         $this->LayoutMode = "TwoColumnLeft";
1589                                         break;
1590                                 }
1591                                 case "TwoColumnRight": {
1592                                         $this->LayoutMode = "TwoColumnRight";
1593                                         break;
1594                                 }
1595                                 case "TwoPageLeft": {
1596                                         $this->LayoutMode = "TwoPageLeft";
1597                                         break;
1598                                 }
1599                                 case "TwoPageRight": {
1600                                         $this->LayoutMode = "TwoPageRight";
1601                                         break;
1602                                 }
1603                                 default: {
1604                                         $this->LayoutMode = "SinglePage";
1605                                 }
1606                         }
1607                         switch ($mode) {
1608                                 case "UseNone": {
1609                                         $this->PageMode = "UseNone";
1610                                         break;
1611                                 }
1612                                 case "UseOutlines": {
1613                                         $this->PageMode = "UseOutlines";
1614                                         break;
1615                                 }
1616                                 case "UseThumbs": {
1617                                         $this->PageMode = "UseThumbs";
1618                                         break;
1619                                 }
1620                                 case "FullScreen": {
1621                                         $this->PageMode = "FullScreen";
1622                                         break;
1623                                 }
1624                                 case "UseOC": {
1625                                         $this->PageMode = "UseOC";
1626                                         break;
1627                                 }
1628                                 case "": {
1629                                         $this->PageMode = "UseAttachments";
1630                                         break;
1631                                 }
1632                                 default: {
1633                                         $this->PageMode = "UseNone";
1634                                 }
1635                         }
1636                 }
1637
1638                 /**
1639                 * 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.
1640                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1641                 * @param boolean $compress Boolean indicating if compression must be enabled.
1642                 * @since 1.4
1643                 */
1644                 function SetCompression($compress) {
1645                         //Set page compression
1646                         if (function_exists('gzcompress')) {
1647                                 $this->compress = $compress;
1648                         } else {
1649                                 $this->compress = false;
1650                         }
1651                 }
1652
1653                 /**
1654                 * Defines the title of the document.
1655                 * @param string $title The title.
1656                 * @since 1.2
1657                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1658                 */
1659                 function SetTitle($title) {
1660                         //Title of document
1661                         $this->title = $title;
1662                 }
1663
1664                 /**
1665                 * Defines the subject of the document.
1666                 * @param string $subject The subject.
1667                 * @since 1.2
1668                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1669                 */
1670                 function SetSubject($subject) {
1671                         //Subject of document
1672                         $this->subject = $subject;
1673                 }
1674
1675                 /**
1676                 * Defines the author of the document.
1677                 * @param string $author The name of the author.
1678                 * @since 1.2
1679                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
1680                 */
1681                 function SetAuthor($author) {
1682                         //Author of document
1683                         $this->author = $author;
1684                 }
1685
1686                 /**
1687                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
1688                 * @param string $keywords The list of keywords.
1689                 * @since 1.2
1690                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
1691                 */
1692                 function SetKeywords($keywords) {
1693                         //Keywords of document
1694                         $this->keywords = $keywords;
1695                 }
1696
1697                 /**
1698                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
1699                 * @param string $creator The name of the creator.
1700                 * @since 1.2
1701                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
1702                 */
1703                 function SetCreator($creator) {
1704                         //Creator of document
1705                         $this->creator = $creator;
1706                 }
1707
1708                 /**
1709                 * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
1710                 * <b>Example:</b><br />
1711                 * <pre>
1712                 *               $this->Cell(0,10,'Page '.$pdf->PageNo().'/{nb}',0,0,'C');
1713                 * </pre>
1714                 * @param string $alias The alias. Default value: {nb}.
1715                 * @since 1.4
1716                 * @see PageNo(), Footer()
1717                 */
1718                 function AliasNbPages($alias='{nb}') {
1719                         //Define an alias for total number of pages
1720                         $this->AliasNbPages = $this->_escapetext($alias);
1721                 }
1722
1723                 /**
1724                 * 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.
1725                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
1726                 * @param string $msg The error message
1727                 * @since 1.0
1728                 */
1729                 function Error($msg) {
1730                         //Fatal error
1731                         die('<strong>TCPDF error: </strong>'.$msg);
1732                 }
1733
1734                 /**
1735                 * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
1736                 * Note: no page is created by this method
1737                 * @since 1.0
1738                 * @see AddPage(), Close()
1739                 */
1740                 function Open() {
1741                         //Begin document
1742                         $this->state = 1;
1743                 }
1744
1745                 /**
1746                 * 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.
1747                 * @since 1.0
1748                 * @see Open(), Output()
1749                 */
1750                 function Close() {
1751                         //Terminate document
1752                         if ($this->state == 3) {
1753                                 return;
1754                         }
1755                         if ($this->page == 0) {
1756                                 $this->AddPage();
1757                         }
1758                         //Page footer
1759                         $this->setFooter();
1760                         //Close page
1761                         $this->_endpage();
1762                         //Close document
1763                         $this->_enddoc();
1764                 }
1765
1766                 /**
1767                 * Move pointer at the specified document page and update page dimensions.
1768                 * @param int $pnum page number
1769                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
1770                 * @since 2.1.000 (2008-01-07)
1771                 * @see getPage(), lastpage(), getNumPages()
1772                 */
1773                 function setPage($pnum, $resetmargins=false) {
1774                         if (($pnum > 0) AND ($pnum <= count($this->pages))) {
1775                                 $this->page = $pnum;
1776                                 $this->wPt = $this->pagedim[$this->page]['w'];
1777                                 $this->hPt = $this->pagedim[$this->page]['h'];
1778                                 $this->w = $this->wPt / $this->k;
1779                                 $this->h = $this->hPt / $this->k;
1780                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
1781                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
1782                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
1783                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
1784                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
1785                                 if ($resetmargins) {
1786                                         $this->lMargin = $this->pagedim[$this->page]['lm'];
1787                                         $this->rMargin = $this->pagedim[$this->page]['rm'];
1788                                         $this->SetY($this->tMargin);
1789                                 }
1790                         } else {
1791                                 $this->Error('Wrong page number on setPage() function.');
1792                         }
1793                 }
1794
1795                 /**
1796                 * Reset pointer to the last document page.
1797                 * @since 2.0.000 (2008-01-04)
1798                 * @see setPage(), getPage(), getNumPages()
1799                 */
1800                 function lastPage() {
1801                         $this->setPage($this->getNumPages());
1802                 }
1803
1804                 /**
1805                 * Get current document page number.
1806                 * @return int page number
1807                 * @since 2.1.000 (2008-01-07)
1808                 * @see setPage(), lastpage(), getNumPages()
1809                 */
1810                 function getPage() {
1811                         return $this->page;
1812                 }
1813
1814
1815                 /**
1816                 * Get the total number of insered pages.
1817                 * @return int number of pages
1818                 * @since 2.1.000 (2008-01-07)
1819                 * @see setPage(), getPage(), lastpage()
1820                 */
1821                 function getNumPages() {
1822                         return count($this->pages);
1823                 }
1824
1825                 /**
1826                 * 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.
1827                 * 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.
1828                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
1829                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1830                 * @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>
1831                 * @since 1.0
1832                 * @see TCPDF(), Header(), Footer(), SetMargins()
1833                 */
1834                 function AddPage($orientation='', $format='') {
1835                         if (!isset($this->original_lMargin)) {
1836                                 $this->original_lMargin = $this->lMargin;
1837                         }
1838                         if (!isset($this->original_rMargin)) {
1839                                 $this->original_rMargin = $this->rMargin;
1840                         }
1841                         if (count($this->pages) > $this->page) {
1842                                 // this page has been already added
1843                                 $this->setPage(($this->page + 1));
1844                                 return;
1845                         }
1846                         //Start a new page
1847                         if ($this->state == 0) {
1848                                 $this->Open();
1849                         }
1850                         // save current settings
1851                         $font_family = $this->FontFamily;
1852                         $font_style = $this->FontStyle.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
1853                         $font_size = $this->FontSizePt;
1854                         $prev_rMargin = $this->rMargin;
1855                         $prev_lMargin = $this->lMargin;
1856                         $prev_cMargin = $this->cMargin;
1857                         $prev_linestyleWidth = $this->linestyleWidth;
1858                         $prev_linestyleCap = $this->linestyleCap;
1859                         $prev_linestyleJoin = $this->linestyleJoin;
1860                         $prev_linestyleDash = $this->linestyleDash;
1861                         $prev_DrawColor = $this->DrawColor;
1862                         $prev_FillColor = $this->FillColor;
1863                         $prev_TextColor = $this->TextColor;
1864                         $prev_ColorFlag = $this->ColorFlag;
1865                         if ($this->page > 0) {
1866                                 //Page footer
1867                                 $this->setFooter();
1868                                 //Close page
1869                                 $this->_endpage();
1870                         }
1871                         //Start new page
1872                         $this->_beginpage($orientation, $format);
1873                         // restore graphic styles
1874                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1875                         if (!empty($font_family)) {
1876                                 $this->SetFont($font_family, $font_style, $font_size);
1877                         }
1878                         //Page header
1879                         $this->setHeader();
1880                         // restore graphic styles
1881                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1882                         if (!empty($font_family)) {
1883                                 $this->SetFont($font_family, $font_style, $font_size);
1884                         }
1885                         // restore settings
1886                         $this->FontFamily = $font_family;
1887                         $this->FontStyle = $font_style;
1888                         $this->FontSizePt = $font_size;
1889                         $this->rMargin = $prev_rMargin;
1890                         $this->lMargin = $prev_lMargin;
1891                         $this->cMargin = $prev_cMargin;
1892                         $this->linestyleWidth = $prev_linestyleWidth;
1893                         $this->linestyleCap = $prev_linestyleCap;
1894                         $this->linestyleJoin = $prev_linestyleJoin;
1895                         $this->linestyleDash = $prev_linestyleDash;
1896                         $this->DrawColor = $prev_DrawColor;
1897                         $this->FillColor = $prev_FillColor;
1898                         $this->TextColor = $prev_TextColor;
1899                         $this->ColorFlag = $prev_ColorFlag;
1900                         // mark this point
1901                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1902                 }
1903
1904                 /**
1905                  * Set start-writing mark on current page for multicell borders and fills.
1906                  * This function must be called after calling Image() function for a background image.
1907                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
1908                  * @access public
1909                  * @since 4.0.016 (2008-07-30)
1910                  */
1911                 function setPageMark() {
1912                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1913                 }
1914
1915                 /**
1916                  * Set header data.
1917                  * @param string $ln header image logo
1918                  * @param string $lw header image logo width in mm
1919                  * @param string $ht string to print as title on document header
1920                  * @param string $hs string to print on document header
1921                 */
1922                 function setHeaderData($ln="", $lw=0, $ht="", $hs="") {
1923                         $this->header_logo = $ln;
1924                         $this->header_logo_width = $lw;
1925                         $this->header_title = $ht;
1926                         $this->header_string = $hs;
1927                 }
1928
1929                 /**
1930                  * Returns header data:
1931                  * <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>
1932                  * @return array()
1933                  * @since 4.0.012 (2008-07-24)
1934                  */
1935                 function getHeaderData() {
1936                         $ret = array();
1937                         $ret['logo'] = $this->header_logo;
1938                         $ret['logo_width'] = $this->header_logo_width;
1939                         $ret['title'] = $this->header_title;
1940                         $ret['string'] = $this->header_string;
1941                         return $ret;
1942                 }
1943
1944                 /**
1945                  * Set header margin.
1946                  * (minimum distance between header and top page margin)
1947                  * @param int $hm distance in user units
1948                 */
1949                 function setHeaderMargin($hm=10) {
1950                         $this->header_margin = $hm;
1951                 }
1952
1953                 /**
1954                  * Returns header margin in user units.
1955                  * @return float
1956                  * @since 4.0.012 (2008-07-24)
1957                 */
1958                 function getHeaderMargin() {
1959                         return $this->header_margin;
1960                 }
1961
1962                 /**
1963                  * Set footer margin.
1964                  * (minimum distance between footer and bottom page margin)
1965                  * @param int $fm distance in user units
1966                 */
1967                 function setFooterMargin($fm=10) {
1968                         $this->footer_margin = $fm;
1969                 }
1970
1971                 /**
1972                  * Returns footer margin in user units.
1973                  * @return float
1974                  * @since 4.0.012 (2008-07-24)
1975                 */
1976                 function getFooterMargin() {
1977                         return $this->footer_margin;
1978                 }
1979                 /**
1980                  * Set a flag to print page header.
1981                  * @param boolean $val set to true to print the page header (default), false otherwise.
1982                  */
1983                 function setPrintHeader($val=true) {
1984                         $this->print_header = $val;
1985                 }
1986
1987                 /**
1988                  * Set a flag to print page footer.
1989                  * @param boolean $value set to true to print the page footer (default), false otherwise.
1990                  */
1991                 function setPrintFooter($val=true) {
1992                         $this->print_footer = $val;
1993                 }
1994
1995                 /**
1996                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
1997                  * @return float
1998                  */
1999                 function getImageRBX() {
2000                         return $this->img_rb_x;
2001                 }
2002
2003                 /**
2004                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2005                  * @return float
2006                  */
2007                 function getImageRBY() {
2008                         return $this->img_rb_y;
2009                 }
2010
2011                 /**
2012                  * This method is used to render the page header.
2013                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2014                  */
2015                 function Header1() {
2016                         $ormargins = $this->getOriginalMargins();
2017                         $headerfont = $this->getHeaderFont();
2018                         $headerdata = $this->getHeaderData();
2019                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2020                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2021                                 $imgy = $this->getImageRBY();
2022                         } else {
2023                                 $imgy = $this->GetY();
2024                         }
2025                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2026                         // set starting margin for text data cell
2027                         if ($this->getRTL()) {
2028                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2029                         } else {
2030                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2031                         }
2032                         $this->SetTextColor(0, 0, 0);
2033                         // header title
2034                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2035                         $this->SetX($header_x);
2036                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '');
2037                         // header string
2038                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2039                         $this->SetX($header_x);
2040                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, 0, 0, true, 0);
2041                         // print an ending header line
2042                         $this->SetLineStyle(array("width" => 0.85 / $this->getScaleFactor(), "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2043                         $this->SetY(1 + max($imgy, $this->GetY()));
2044                         if ($this->getRTL()) {
2045                                 $this->SetX($ormargins['right']);
2046                         } else {
2047                                 $this->SetX($ormargins['left']);
2048                         }
2049                         $this->Cell(0, 0, '', 'T', 0, 'C');
2050                 }
2051
2052                 /**
2053                  * This method is used to render the page footer.
2054                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2055                  */
2056                 function Footer() {
2057                         $cur_y = $this->GetY();
2058                         $ormargins = $this->getOriginalMargins();
2059                         $this->SetTextColor(0, 0, 0);
2060                         //set style for cell border
2061                         $line_width = 0.85 / $this->getScaleFactor();
2062                         $this->SetLineStyle(array("width" => $line_width, "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2063                         //print document barcode
2064                         $barcode = $this->getBarcode();
2065                         if (!empty($barcode)) {
2066                                 $this->Ln();
2067                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2068                                 $this->write1DBarcode($barcode, "C128B", $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2069                         }
2070                         $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / {nb}';
2071                         $this->SetY($cur_y);
2072                         //Print page number
2073                         if ($this->getRTL()) {
2074                                 $this->SetX($ormargins['right']);
2075                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2076                         } else {
2077                                 $this->SetX($ormargins['left']);
2078                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2079                         }
2080                 }
2081
2082                 /**
2083                  * This method is used to render the page header.
2084                  * @access protected
2085                  * @since 4.0.012 (2008-07-24)
2086                  */
2087                 function setHeader() {
2088                         if ($this->print_header) {
2089                                 $this->_out("q");
2090                                 $this->rMargin = $this->original_rMargin;
2091                                 $this->lMargin = $this->original_lMargin;
2092                                 //set current position
2093                                 if ($this->rtl) {
2094                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2095                                 } else {
2096                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2097                                 }
2098                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2099                                 $this->Header1();
2100                                 //restore position
2101                                 if ($this->rtl) {
2102                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2103                                 } else {
2104                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2105                                 }
2106                                 $this->_out("Q");
2107                         }
2108                 }
2109
2110                 /**
2111                  * This method is used to render the page footer.
2112                  * @access protected
2113                  * @since 4.0.012 (2008-07-24)
2114                  */
2115                 function setFooter() {
2116                         //Page footer
2117                         $this->InFooter = true;
2118                         // mark this point
2119                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
2120                         if ($this->print_footer) {
2121                                 $this->_out("q");
2122                                 $this->rMargin = $this->original_rMargin;
2123                                 $this->lMargin = $this->original_lMargin;
2124                                 //set current position
2125                                 $footer_y = $this->h - $this->footer_margin;
2126                                 if ($this->rtl) {
2127                                         $this->SetXY($this->original_rMargin, $footer_y);
2128                                 } else {
2129                                         $this->SetXY($this->original_lMargin, $footer_y);
2130                                 }
2131                                 $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]);
2132                                 $this->Footer();
2133                                 //restore position
2134                                 if ($this->rtl) {
2135                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2136                                 } else {
2137                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2138                                 }
2139                                 $this->_out("Q");
2140                         }
2141                         $this->footerlen[$this->page] = strlen($this->pages[$this->page]) - $this->footerpos[$this->page];
2142                         $this->InFooter = false;
2143                 }
2144
2145                 /**
2146                 * Returns the current page number.
2147                 * @return int page number
2148                 * @since 1.0
2149                 * @see AliasNbPages()
2150                 */
2151                 function PageNo() {
2152                         return $this->page;
2153                 }
2154
2155                 /**
2156                 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
2157                 * It can be expressed in RGB components or gray scale.
2158                 * The method can be called before the first page is created and the value is retained from page to page.
2159                 * @param array $color array of colors
2160                 * @since 3.1.000 (2008-6-11)
2161                 * @see SetDrawColor()
2162                 */
2163                 function SetDrawColorArray($color) {
2164                         if (isset($color)) {
2165                                 $color = array_values($color);
2166                                 $r = isset($color[0]) ? $color[0] : -1;
2167                                 $g = isset($color[1]) ? $color[1] : -1;
2168                                 $b = isset($color[2]) ? $color[2] : -1;
2169                                 $k = isset($color[3]) ? $color[3] : -1;
2170                                 if ($r >= 0) {
2171                                         $this->SetDrawColor($r, $g, $b, $k);
2172                                 }
2173                         }
2174                 }
2175
2176                 /**
2177                 * 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.
2178                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2179                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2180                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2181                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2182                 * @since 1.3
2183                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2184                 */
2185                 function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2186                         // set default values
2187                         if (!is_numeric($col1)) {
2188                                 $col1 = 0;
2189                         }
2190                         if (!is_numeric($col2)) {
2191                                 $col2 = -1;
2192                         }
2193                         if (!is_numeric($col3)) {
2194                                 $col3 = -1;
2195                         }
2196                         if (!is_numeric($col4)) {
2197                                 $col4 = -1;
2198                         }
2199                         //Set color for all stroking operations
2200                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2201                                 // Grey scale
2202                                 $this->DrawColor=sprintf('%.3f G', $col1/255);
2203                         } elseif ($col4 == -1) {
2204                                 // RGB
2205                                 $this->DrawColor=sprintf('%.3f %.3f %.3f RG', $col1/255, $col2/255, $col3/255);
2206                         } else {
2207                                 // CMYK
2208                                 $this->DrawColor = sprintf('%.3f %.3f %.3f %.3f K', $col1/100, $col2/100, $col3/100, $col4/100);
2209                         }
2210                         if ($this->page>0) {
2211                                 $this->_out($this->DrawColor);
2212                         }
2213                 }
2214
2215                 /**
2216                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
2217                 * It can be expressed in RGB components or gray scale.
2218                 * The method can be called before the first page is created and the value is retained from page to page.
2219                 * @param array $color array of colors
2220                 * @since 3.1.000 (2008-6-11)
2221                 * @see SetFillColor()
2222                 */
2223                 function SetFillColorArray($color) {
2224                         if (isset($color)) {
2225                                 $color = array_values($color);
2226                                 $r = isset($color[0]) ? $color[0] : -1;
2227                                 $g = isset($color[1]) ? $color[1] : -1;
2228                                 $b = isset($color[2]) ? $color[2] : -1;
2229                                 $k = isset($color[3]) ? $color[3] : -1;
2230                                 if ($r >= 0) {
2231                                         $this->SetFillColor($r, $g, $b, $k);
2232                                 }
2233                         }
2234                 }
2235
2236                 /**
2237                 * 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.
2238                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2239                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2240                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2241                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2242                 * @since 1.3
2243                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2244                 */
2245                 function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2246                         // set default values
2247                         if (!is_numeric($col1)) {
2248                                 $col1 = 0;
2249                         }
2250                         if (!is_numeric($col2)) {
2251                                 $col2 = -1;
2252                         }
2253                         if (!is_numeric($col3)) {
2254                                 $col3 = -1;
2255                         }
2256                         if (!is_numeric($col4)) {
2257                                 $col4 = -1;
2258                         }
2259                         //Set color for all filling operations
2260                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2261                                 // Grey scale
2262                                 $this->FillColor = sprintf('%.3f g', $col1/255);
2263                                 $this->bgcolor = array('G' => $col1);
2264                         } elseif ($col4 == -1) {
2265                                 // RGB
2266                                 $this->FillColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2267                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2268                         } else {
2269                                 // CMYK
2270                                 $this->FillColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2271                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2272                         }
2273                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2274                         if ($this->page>0) {
2275                                 $this->_out($this->FillColor);
2276                         }
2277                 }
2278
2279                 /**
2280                 * Defines the color used for text. It can be expressed in RGB components or gray scale.
2281                 * The method can be called before the first page is created and the value is retained from page to page.
2282                 * @param array $color array of colors
2283                 * @since 3.1.000 (2008-6-11)
2284                 * @see SetFillColor()
2285                 */
2286                 function SetTextColorArray($color) {
2287                         if (isset($color)) {
2288                                 $color = array_values($color);
2289                                 $r = isset($color[0]) ? $color[0] : -1;
2290                                 $g = isset($color[1]) ? $color[1] : -1;
2291                                 $b = isset($color[2]) ? $color[2] : -1;
2292                                 $k = isset($color[3]) ? $color[3] : -1;
2293                                 if ($r >= 0) {
2294                                         $this->SetTextColor($r, $g, $b, $k);
2295                                 }
2296                         }
2297                 }
2298
2299                 /**
2300                 * 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.
2301                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2302                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2303                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2304                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2305                 * @since 1.3
2306                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2307                 */
2308                 function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2309                         // set default values
2310                         if (!is_numeric($col1)) {
2311                                 $col1 = 0;
2312                         }
2313                         if (!is_numeric($col2)) {
2314                                 $col2 = -1;
2315                         }
2316                         if (!is_numeric($col3)) {
2317                                 $col3 = -1;
2318                         }
2319                         if (!is_numeric($col4)) {
2320                                 $col4 = -1;
2321                         }
2322                         //Set color for text
2323                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2324                                 // Grey scale
2325                                 $this->TextColor = sprintf('%.3f g', $col1/255);
2326                                 $this->fgcolor = array('G' => $col1);
2327                         } elseif ($col4 == -1) {
2328                                 // RGB
2329                                 $this->TextColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2330                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2331                         } else {
2332                                 // CMYK
2333                                 $this->TextColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2334                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2335                         }
2336                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2337                 }
2338
2339                 /**
2340                 * Returns the length of a string in user unit. A font must be selected.<br>
2341                 * @param string $s The string whose length is to be computed
2342                 * @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.
2343                 * @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.
2344                 * @param float $fontsize Font size in points. The default value is the current size.
2345                 * @return int string length
2346                 * @author Nicola Asuni
2347                 * @since 1.2
2348                 */
2349                 function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2350                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $this->tmprtl), $fontname, $fontstyle, $fontsize);
2351                 }
2352
2353                 /**
2354                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2355                 * @param string $arr The array of chars whose total length is to be computed
2356                 * @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.
2357                 * @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.
2358                 * @param float $fontsize Font size in points. The default value is the current size.
2359                 * @return int string length
2360                 * @author Nicola Asuni
2361                 * @since 2.4.000 (2008-03-06)
2362                 */
2363                 function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2364                         // store current values
2365                         if (!empty($fontname)) {
2366                                 $prev_FontFamily = $this->FontFamily;
2367                                 $prev_FontStyle = $this->FontStyle;
2368                                 $prev_FontSizePt = $this->FontSizePt;
2369                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2370                         }
2371                         $w = 0;
2372                         foreach($sa as $char) {
2373                                 $w += $this->GetCharWidth($char);
2374                         }
2375                         // restore previous values
2376                         if (!empty($fontname)) {
2377                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2378                         }
2379                         return $w;
2380                 }
2381
2382                 /**
2383                 * Returns the length of the char in user unit for the current font.<br>
2384                 * @param string $char The char whose length is to be returned
2385                 * @return int char width
2386                 * @author Nicola Asuni
2387                 * @since 2.4.000 (2008-03-06)
2388                 */
2389                 function GetCharWidth($char) {
2390                         $cw = &$this->CurrentFont['cw'];
2391                         if (isset($cw[$char])) {
2392                                 $w = $cw[$char];
2393                                 /*
2394                         } elseif (isset($cw[ord($char)])) {
2395                                 $w = $cw[ord($char)];
2396                         } elseif (isset($cw[chr($char)])) {
2397                                 $w = $cw[chr($char)];
2398                                 */
2399                         } elseif (isset($this->CurrentFont['dw'])) {
2400                                 $w = $this->CurrentFont['dw'];
2401                         } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
2402                                 $w = $this->CurrentFont['desc']['MissingWidth']; // set default size
2403                         } else {
2404                                 $w = 500; // default width
2405                         }
2406                         return ($w * $this->FontSize / 1000);
2407                 }
2408
2409                 /**
2410                 * Returns the numbero of characters in a string.
2411                 * @param string $s The input string.
2412                 * @return int number of characters
2413                 * @since 2.0.0001 (2008-01-07)
2414                 */
2415                 function GetNumChars($s) {
2416                         if ($this->isunicode) {
2417                                 return count($this->UTF8StringToArray($s));
2418                         }
2419                         return strlen($s);
2420                 }
2421
2422                 /**
2423                 * Fill the list of available fonts ($this->fontlist).
2424                 * @access protected
2425                 * @since 4.0.013 (2008-07-28)
2426                 */
2427                 function getFontsList() {
2428                         $fontsdir = opendir($this->_getfontpath());
2429                         while (($file = readdir($fontsdir)) !== false) {
2430                                 if (substr($file, -4) == ".php") {
2431                                                 array_push($this->fontlist, strtolower(basename($file, ".php")));
2432                                 }
2433                         }
2434                         closedir($fontsdir);
2435                 }
2436
2437                 /**
2438                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2439                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
2440                 * 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.
2441                 * Changed to support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
2442                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2443                 * @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>
2444                 * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space.
2445                 * @return array containing the font data, or false in case of error.
2446                 * @since 1.5
2447                 * @see SetFont()
2448                 */
2449                 function AddFont($family, $style='', $file='') {
2450                         if (empty($family)) {
2451                                 if (!empty($this->FontFamily)) {
2452                                         $family = $this->FontFamily;
2453                                 } else {
2454                                         $this->Error('Empty font family');
2455                                 }
2456                         }
2457                         $family = strtolower($family);
2458                         if ((!$this->isunicode) AND ($family == 'arial')) {
2459                                 $family = 'helvetica';
2460                         }
2461                         if (($family == "symbol") OR ($family == "zapfdingbats")) {
2462                                 $style = '';
2463                         }
2464                         $style = strtoupper($style);
2465                         // underline
2466                         if (strpos($style,'U') !== false) {
2467                                 $this->underline = true;
2468                                 $style = str_replace('U', '', $style);
2469                         } else {
2470                                 $this->underline = false;
2471                         }
2472                         //line through (deleted)
2473                         if (strpos($style,'D') !== false) {
2474                                 $this->linethrough = true;
2475                                 $style = str_replace('D', '', $style);
2476                         } else {
2477                                 $this->linethrough = false;
2478                         }
2479                         if ($style == 'IB') {
2480                                 $style = 'BI';
2481                         }
2482                         $fontkey = $family.$style;
2483                         $fontdata = array("fontkey" => $fontkey, "family" => $family, "style" => $style);
2484                         // check if the font has been already added
2485                         if (isset($this->fonts[$fontkey])) {
2486                                 return $fontdata;
2487                         }
2488                         if ($file == '') {
2489                                 $file = str_replace(' ', '', $family).strtolower($style).'.php';
2490                         }
2491
2492                         if (!file_exists($this->_getfontpath().$file)) {
2493                                 // try to load the basic file without styles
2494                                 $file = str_replace(' ', '', $family).'.php';
2495                         }
2496                         if (isset($type)) {
2497                                 unset($type);
2498                         }
2499                         if (isset($cw)) {
2500                                 unset($cw);
2501                         }
2502                         include($this->_getfontpath().$file);
2503                         if ((!isset($type)) OR (!isset($cw))) {
2504                                 $this->Error('Could not include font definition file');
2505                         }
2506                         $i = count($this->fonts) + 1;
2507                         // register CID font (all styles at once)
2508                         if ($type == 'cidfont0') {
2509                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
2510                                 foreach ($styles as $skey => $qual) {
2511                                         $sname = $name.$qual;
2512                                         $sfontkey = $family.$skey;
2513                                         $this->fonts[$sfontkey] = array('i' => $i, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc);
2514                                         $i = count($this->fonts) + 1;
2515                                 }
2516                                 $file = '';
2517                         } elseif ($type == 'core') {
2518                                 $def_width = $cw[ord('?')];
2519                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $def_width);
2520                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
2521                                 if (!isset($file)) {
2522                                         $file = '';
2523                                 }
2524                                 if (!isset($enc)) {
2525                                         $enc = '';
2526                                 }
2527                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'file' => $file, 'enc' => $enc, 'desc' => $desc);
2528                         } elseif ($type == 'TrueTypeUnicode') {
2529                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg);
2530                         } else {
2531                                 $this->Error('Unknow font type');
2532                         }
2533                         if (isset($diff) AND (!empty($diff))) {
2534                                 //Search existing encodings
2535                                 $d = 0;
2536                                 $nb = count($this->diffs);
2537                                 for($i=1; $i <= $nb; $i++) {
2538                                         if ($this->diffs[$i] == $diff) {
2539                                                 $d = $i;
2540                                                 break;
2541                                         }
2542                                 }
2543                                 if ($d == 0) {
2544                                         $d = $nb + 1;
2545                                         $this->diffs[$d] = $diff;
2546                                 }
2547                                 $this->fonts[$fontkey]['diff'] = $d;
2548                         }
2549                         if (!empty($file)) {
2550                                 if ((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) {
2551                                         $this->FontFiles[$file] = array('length1' => $originalsize);
2552                                 } elseif ($type != 'core') {
2553                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2);
2554                                 }
2555                         }
2556                         return $fontdata;
2557                 }
2558
2559                 /**
2560                 * Sets the font used to print character strings.
2561                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
2562                 * The method can be called before the first page is created and the font is retained from page to page.
2563                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
2564                 * 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 />
2565                 * @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.
2566                 * @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.
2567                 * @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
2568                 * @since 1.0
2569                 * @see AddFont(), SetFontSize()
2570                 */
2571                 function SetFont($family, $style='', $size=0) {
2572                         //Select a font; size given in points
2573                         if ($size == 0) {
2574                                 $size = $this->FontSizePt;
2575                         }
2576                         // try to add font (if not already added)
2577                         $fontdata =  $this->AddFont($family, $style);
2578                         $this->FontFamily = $fontdata['family'];
2579                         $this->FontStyle = $fontdata['style'];
2580                         $this->CurrentFont = &$this->fonts[$fontdata['fontkey']];
2581                         $this->SetFontSize($size);
2582                 }
2583
2584                 /**
2585                 * Defines the size of the current font.
2586                 * @param float $size The size (in points)
2587                 * @since 1.0
2588                 * @see SetFont()
2589                 */
2590                 function SetFontSize($size) {
2591                         //Set font size in points
2592                         $this->FontSizePt = $size;
2593                         $this->FontSize = $size / $this->k;
2594                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
2595                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
2596                         } else {
2597                                 $this->FontAscent = 0.8 * $this->FontSize;
2598                         }
2599                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
2600                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
2601                         } else {
2602                                 $this->FontDescent = 0.2 * $this->FontSize;
2603                         }
2604                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
2605                                 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2606                         }
2607                 }
2608
2609                 /**
2610                 * 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 />
2611                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
2612                 * @since 1.5
2613                 * @see Cell(), Write(), Image(), Link(), SetLink()
2614                 */
2615                 function AddLink() {
2616                         //Create a new internal link
2617                         $n = count($this->links) + 1;
2618                         $this->links[$n] = array(0, 0);
2619                         return $n;
2620                 }
2621
2622                 /**
2623                 * Defines the page and position a link points to.
2624                 * @param int $link The link identifier returned by AddLink()
2625                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
2626                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
2627                 * @since 1.5
2628                 * @see AddLink()
2629                 */
2630                 function SetLink($link, $y=0, $page=-1) {
2631                         if ($y == -1) {
2632                                 $y=$this->y;
2633                         }
2634                         if ($page == -1) {
2635                                 $page = $this->page;
2636                         }
2637                         $this->links[$link] = array($page, $y);
2638                 }
2639
2640                 /**
2641                 * Puts a link on a rectangular area of the page.
2642                 * 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.
2643                 * @param float $x Abscissa of the upper-left corner of the rectangle (or upper-right for RTL languages)
2644                 * @param float $y Ordinate of the upper-left corner of the rectangle (or upper-right for RTL languages)
2645                 * @param float $w Width of the rectangle
2646                 * @param float $h Height of the rectangle
2647                 * @param mixed $link URL or identifier returned by AddLink()
2648                 * @since 1.5
2649                 * @see AddLink(), Cell(), Write(), Image()
2650                 */
2651                 function Link($x, $y, $w, $h, $link) {
2652                         $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h*$this->k, $link);
2653                 }
2654
2655                 /**
2656                 * 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.
2657                 * @param float $x Abscissa of the origin
2658                 * @param float $y Ordinate of the origin
2659                 * @param string $txt String to print
2660                 * @param int $stroke outline size in points (0 = disable)
2661                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
2662                 * @since 1.0
2663                 * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
2664                 */
2665                 function Text($x, $y, $txt, $stroke=0, $clip=false) {
2666                         //Output a string
2667                         if ($this->rtl) {
2668                                 // bidirectional algorithm (some chars may be changed affecting the line length)
2669                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $this->tmprtl);
2670                                 $l = $this->GetArrStringWidth($s);
2671                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
2672                         } else {
2673                                 $xr = $x;
2674                         }
2675                         $opt = "";
2676                         if (($stroke > 0) AND (!$clip)) {
2677                                 $opt .= "1 Tr ".intval($stroke)." w ";
2678                         } elseif (($stroke > 0) AND $clip) {
2679                                 $opt .= "5 Tr ".intval($stroke)." w ";
2680                         } elseif ($clip) {
2681                                 $opt .= "7 Tr ";
2682                         }
2683                         $s = sprintf('BT %.2f %.2f Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
2684                         if ($this->underline AND ($txt!='')) {
2685                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
2686                         }
2687                         if ($this->linethrough AND ($txt!='')) {
2688                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt);
2689                         }
2690                         if ($this->ColorFlag AND (!$clip)) {
2691                                 $s='q '.$this->TextColor.' '.$s.' Q';
2692                         }
2693                         $this->_out($s);
2694                 }
2695
2696                 /**
2697                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
2698                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
2699                 * This method is called automatically and should not be called directly by the application.
2700                 * @return boolean
2701                 * @since 1.4
2702                 * @see SetAutoPageBreak()
2703                 */
2704                 function AcceptPageBreak() {
2705                         return $this->AutoPageBreak;
2706                 }
2707
2708                 /**
2709                 * Add page if needed.
2710                 * @param float $h Cell height. Default value: 0.
2711                 * @since 3.2.000 (2008-07-01)
2712                 * @access protected
2713                 */
2714                 function checkPageBreak($h) {
2715                         if ((($this->y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
2716                                 $rs = "";
2717                                 //Automatic page break
2718                                 $x = $this->x;
2719                                 $ws = $this->ws;
2720                                 if ($ws > 0) {
2721                                         $this->ws = 0;
2722                                         $rs .= '0 Tw';
2723                                 }
2724                                 $this->AddPage($this->CurOrientation);
2725                                 if ($ws > 0) {
2726                                         $this->ws = $ws;
2727                                         $rs .= sprintf('%.3f Tw', $ws * $k);
2728                                 }
2729                                 $this->_out($rs);
2730                                 $this->y = $this->tMargin;
2731                                 $this->x = $x;
2732                         }
2733                 }
2734
2735                 /**
2736                 * 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 />
2737                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2738                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2739                 * @param float $h Cell height. Default value: 0.
2740                 * @param string $txt String to print. Default value: empty string.
2741                 * @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>
2742                 * @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>
2743                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2744                 * @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>
2745                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2746                 * @param mixed $link URL or identifier returned by AddLink().
2747                 * @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>
2748                 * @since 1.0
2749                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
2750                 */
2751                 function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2752                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
2753                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2754                         if ($h < $min_cell_height) {
2755                                 $h = $min_cell_height;
2756                         }
2757                         $this->checkPageBreak($h);
2758                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch));
2759                 }
2760
2761                 /**
2762                 * 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 />
2763                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2764                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2765                 * @param float $h Cell height. Default value: 0.
2766                 * @param string $txt String to print. Default value: empty string.
2767                 * @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>
2768                 * @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.
2769                 * @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>
2770                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2771                 * @param mixed $link URL or identifier returned by AddLink().
2772                 * @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>
2773                 * @since 1.0
2774                 * @see Cell()
2775                 */
2776                 function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2777                         $rs = ""; //string to be returned
2778                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2779                         if ($h < $min_cell_height) {
2780                                 $h = $min_cell_height;
2781                         }
2782                         $k = $this->k;
2783                         if (empty($w) OR ($w <= 0)) {
2784                                 if ($this->rtl) {
2785                                         $w = $this->x - $this->lMargin;
2786                                 } else {
2787                                         $w = $this->w - $this->rMargin - $this->x;
2788                                 }
2789                         }
2790                         $s = '';
2791                         if (($fill == 1) OR ($border == 1)) {
2792                                 if ($fill == 1) {
2793                                         $op = ($border == 1) ? 'B' : 'f';
2794                                 } else {
2795                                         $op = 'S';
2796                                 }
2797                                 if ($this->rtl) {
2798                                         $xk = (($this->x  - $w) * $k);
2799                                 } else {
2800                                         $xk = ($this->x * $k);
2801                                 }
2802                                 $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
2803                         }
2804
2805                         if (is_string($border)) {
2806                                 $x = $this->x;
2807                                 $y = $this->y;
2808                                 if (strpos($border,'L') !== false) {
2809                                         if ($this->rtl) {
2810                                                 $xk = ($x - $w) * $k;
2811                                         } else {
2812                                                 $xk = $x * $k;
2813                                         }
2814                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h)) * $k));
2815                                 }
2816                                 if (strpos($border,'T') !== false) {
2817                                         if ($this->rtl) {
2818                                                 $xk = ($x - $w) * $k;
2819                                                 $xwk = $x * $k;
2820                                         } else {
2821                                                 $xk = $x * $k;
2822                                                 $xwk = ($x + $w) * $k;
2823                                         }
2824                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
2825                                 }
2826                                 if (strpos($border,'R') !== false) {
2827                                         if ($this->rtl) {
2828                                                 $xk = $x * $k;
2829                                         } else {
2830                                                 $xk = ($x + $w) * $k;
2831                                         }
2832                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h))* $k));
2833                                 }
2834                                 if (strpos($border,'B') !== false) {
2835                                         if ($this->rtl) {
2836                                                 $xk = ($x - $w) * $k;
2837                                                 $xwk = $x * $k;
2838                                         } else {
2839                                                 $xk = $x * $k;
2840                                                 $xwk = ($x + $w) * $k;
2841                                         }
2842                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
2843                                 }
2844                         }
2845                         if ($txt != '') {
2846                                 // text lenght
2847                                 $width = $this->GetStringWidth($txt);
2848                                 // ratio between cell lenght and text lenght
2849                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
2850
2851                                 // stretch text if required
2852                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
2853                                         if ($stretch > 2) {
2854                                                 // spacing
2855                                                 //Calculate character spacing in points
2856                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
2857                                                 //Set character spacing
2858                                                 $rs .= sprintf('BT %.2f Tc ET ', $char_space);
2859                                         } else {
2860                                                 // scaling
2861                                                 //Calculate horizontal scaling
2862                                                 $horiz_scale = $ratio * 100.0;
2863                                                 //Set horizontal scaling
2864                                                 $rs .= sprintf('BT %.2f Tz ET ', $horiz_scale);
2865                                         }
2866                                         $align = '';
2867                                         $width = $w - (2 * $this->cMargin);
2868                                 } else {
2869                                         $stretch == 0;
2870                                 }
2871                                 if ($align == 'L') {
2872                                         if ($this->rtl) {
2873                                                 $dx = $w - $width - $this->cMargin;
2874                                         } else {
2875                                                 $dx = $this->cMargin;
2876                                         }
2877                                 } elseif ($align == 'R') {
2878                                         if ($this->rtl) {
2879                                                 $dx = $this->cMargin;
2880                                         } else {
2881                                                 $dx = $w - $width - $this->cMargin;
2882                                         }
2883                                 } elseif ($align == 'C') {
2884                                         $dx = ($w - $width) / 2;
2885                                 } elseif ($align == 'J') {
2886                                         if ($this->rtl) {
2887                                                 $dx = $w - $width - $this->cMargin;
2888                                         } else {
2889                                                 $dx = $this->cMargin;
2890                                         }
2891                                 } else {
2892                                         $dx = $this->cMargin;
2893                                 }
2894                                 if ($this->ColorFlag) {
2895                                         $s .= 'q '.$this->TextColor.' ';
2896                                 }
2897                                 $txt2 = $this->_escapetext($txt);
2898                                 if ($this->rtl) {
2899                                         $xdk = ($this->x - $dx - $width) * $k;
2900                                 } else {
2901                                         $xdk = ($this->x + $dx) * $k;
2902                                 }
2903                                 // Justification
2904                                 if ($align == 'J') {
2905                                         // count number of spaces
2906                                         $ns = substr_count($txt, ' ');
2907                                         //if ($this->isunicode) {
2908                                         if (($this->CurrentFont['type'] == "TrueTypeUnicode") OR ($this->CurrentFont['type'] == "cidfont0")) {
2909                                                 // get string width without spaces
2910                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
2911                                                 // calculate average space width
2912                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
2913                                                 // set word position to be used with TJ operator
2914                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
2915                                         } else {
2916                                                 // get string width
2917                                                 $width = $this->GetStringWidth($txt);
2918                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
2919                                                 $rs .= sprintf('BT %.3f Tw ET ', $spacewidth);
2920                                         }
2921                                 }
2922                                 // calculate approximate position of the font base line
2923                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
2924                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
2925                                 // print text
2926                                 $s .= sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
2927                                 if ($this->rtl) {
2928                                         $xdx = $this->x - $dx - $width;
2929                                 } else {
2930                                         $xdx = $this->x + $dx;
2931                                 }
2932                                 if ($this->underline)  {
2933                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
2934                                 }
2935                                 if ($this->linethrough) {
2936                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
2937                                 }
2938                                 if ($this->ColorFlag) {
2939                                         $s .= ' Q';
2940                                 }
2941                                 if ($link) {
2942                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link);
2943                                 }
2944                         }
2945                         // output cell
2946                         if ($s) {
2947                                 // output cell
2948                                 $rs .= $s;
2949                                 // reset text stretching
2950                                 if ($stretch > 2) {
2951                                         //Reset character horizontal spacing
2952                                         $rs .= ' BT 0 Tc ET';
2953                                 } elseif ($stretch > 0) {
2954                                         //Reset character horizontal scaling
2955                                         $rs .= ' BT 100 Tz ET';
2956                                 }
2957                         }
2958                         // reset word spacing
2959                         if ((!$this->isunicode) AND ($align == 'J')) {
2960                                 $rs .= ' BT 0 Tw ET';
2961                         }
2962                         $this->lasth = $h;
2963                         if ($ln > 0) {
2964                                 //Go to the beginning of the next line
2965                                 $this->y += $h;
2966                                 if ($ln == 1) {
2967                                         if ($this->rtl) {
2968                                                 $this->x = $this->w - $this->rMargin;
2969                                         } else {
2970                                                 $this->x = $this->lMargin;
2971                                         }
2972                                 }
2973                         } else {
2974                                 // go left or right by case
2975                                 if ($this->rtl) {
2976                                         $this->x -= $w;
2977                                 } else {
2978                                         $this->x += $w;
2979                                 }
2980                         }
2981                         $gstyles = $this->linestyleWidth." ".$this->linestyleCap." ".$this->linestyleJoin." ".$this->linestyleDash." ".$this->DrawColor." ".$this->FillColor."\n";
2982                         $rs = $gstyles.$rs;
2983                         return $rs;
2984                 }
2985
2986                 /**
2987                 * This method allows printing text with line breaks.
2988                 * 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 />
2989                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
2990                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
2991                 * @param float $h Cell minimum height. The cell extends automatically if needed.
2992                 * @param string $txt String to print
2993                 * @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>
2994                 * @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>
2995                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2996                 * @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>
2997                 * @param int $x x position in user units
2998                 * @param int $y y position in user units
2999                 * @param boolean $reseth if true reset the last cell height (default true).
3000                 * @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>
3001                 * @param boolean $ishtml se to true if $txt is HTML content (default = false).
3002                 * @return int Return the number of cells or 1 for html mode.
3003                 * @since 1.3
3004                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3005                 */
3006                 function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false) {
3007                         if ((empty($this->lasth))OR ($reseth)) {
3008                                 //set row height
3009                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3010                         }
3011                         if (!empty($y)) {
3012                                 $this->SetY($y);
3013                         } else {
3014                                 $y = $this->GetY();
3015                         }
3016                         // check for page break
3017                         $this->checkPageBreak($h);
3018                         $y = $this->GetY();
3019                         // get current page number
3020                         $startpage = $this->page;
3021                         if (!empty($x)) {
3022                                 $this->SetX($x);
3023                         } else {
3024                                 $x = $this->GetX();
3025                         }
3026                         if (empty($w) OR ($w <= 0)) {
3027                                 if ($this->rtl) {
3028                                         $w = $this->x - $this->lMargin;
3029                                 } else {
3030                                         $w = $this->w - $this->rMargin - $this->x;
3031                                 }
3032                         }
3033                         // store original margin values
3034                         $lMargin = $this->lMargin;
3035                         $rMargin = $this->rMargin;
3036                         if ($this->rtl) {
3037                                 $this->SetRightMargin($this->w - $this->x);
3038                                 $this->SetLeftMargin($this->x - $w);
3039                         } else {
3040                                 $this->SetLeftMargin($this->x);
3041                                 $this->SetRightMargin($this->w - $this->x - $w);
3042                         }
3043                         // calculate remaining vertical space on first page ($startpage)
3044                         $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
3045                         // Adjust internal padding
3046                         if ($this->cMargin < ($this->LineWidth / 2)) {
3047                                 $this->cMargin = ($this->LineWidth / 2);
3048                         }
3049                         // Add top space if needed
3050                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3051                                 $this->y += $this->LineWidth / 2;
3052                         }
3053                         // add top padding
3054                         $this->y += $this->cMargin;
3055                         if ($ishtml) {
3056                                 // Write HTML text
3057                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3058                                 $nl = 1;
3059                         } else {
3060                                 // Write text
3061                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false);
3062                         }
3063                         // add bottom padding
3064                         $this->y += $this->cMargin;
3065                         // Add bottom space if needed
3066                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3067                                 $this->y += $this->LineWidth / 2;
3068                         }
3069                         // Get end-of-text Y position
3070                         $currentY = $this->GetY();
3071                         // get latest page number
3072                         $endpage = $this->page;
3073                         // check if a new page has been created
3074                         if ($endpage > $startpage) {
3075                                 // design borders around HTML cells.
3076                                 for ($page=$startpage; $page <= $endpage; $page++) {
3077                                         $this->setPage($page);
3078                                         if ($page == $startpage) {
3079                                                 $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
3080                                                 $h = $restspace;
3081                                         } elseif ($page == $endpage) {
3082                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3083                                                 $h = $currentY - $this->tMargin;
3084                                         } else {
3085                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3086                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3087                                         }
3088                                         $this->SetX($x);
3089                                         $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3090                                         if ($border OR $fill) {
3091                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3092                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3093                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3094                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3095                                         }
3096                                 }
3097                         } else {
3098                                 $h = max($h, ($currentY - $y));
3099                                 // put cursor at the beginning of text
3100                                 $this->SetY($y);
3101                                 $this->SetX($x);
3102                                 $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3103                                 if ($border OR $fill) {
3104                                         // design a cell around the text
3105                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3106                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3107                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3108                                         $this->intmrk[$this->page] += strlen($ccode."\n");
3109                                 }
3110                         }
3111                         // Get end-of-cell Y position
3112                         $currentY = $this->GetY();
3113                         // restore original margin values
3114                         $this->SetLeftMargin($lMargin);
3115                         $this->SetRightMargin($rMargin);
3116                         if ($ln > 0) {
3117                                 //Go to the beginning of the next line
3118                                 $this->SetY($currentY);
3119                                 if ($ln == 2) {
3120                                         $this->SetX($x + $w);
3121                                 }
3122                         } else {
3123                                 // go left or right by case
3124                                 $this->setPage($startpage);
3125                                 $this->y = $y;
3126                                 $this->SetX($x + $w);
3127                         }
3128                         return $nl;
3129                 }
3130
3131                 /**
3132                 * This method prints text from the current position.<br />
3133                 * @param float $h Line height
3134                 * @param string $txt String to print
3135                 * @param mixed $link URL or identifier returned by AddLink()
3136                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3137                 * @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>
3138                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3139                 * @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>
3140                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3141                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3142                 * @since 1.5
3143                 */
3144                 function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false) {
3145                         // remove carriage returns
3146                         $s = str_replace("\r", '', $txt);
3147                         // check if string contains arabic text
3148                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3149                                 $arabic = true;
3150                         } else {
3151                                 $arabic = false;
3152                         }
3153                         // get array of chars
3154                         $chars = $this->UTF8StringToArray($s);
3155                         // get the number of characters
3156                         $nb = count($chars);
3157                         // handle single space character
3158                         if (($nb == 1) AND preg_match("/[\s]/u", $s)) {
3159                                 if ($this->rtl) {
3160                                         $this->x -= $this->GetStringWidth($s);
3161                                 } else {
3162                                         $this->x += $this->GetStringWidth($s);
3163                                 }
3164                                 return;
3165                         }
3166                         // store current position
3167                         $prevx = $this->x;
3168                         $prevy = $this->y;
3169                         // calculate remaining line width ($w)
3170                         if ($this->rtl) {
3171                                 $w = $this->x - $this->lMargin;
3172                         } else {
3173                                 $w = $this->w - $this->rMargin - $this->x;
3174                         }
3175                         // max column width
3176                         $wmax = $w - (2 * $this->cMargin);
3177                         $i = 0; // character position
3178                         $j = 0; // current starting position
3179                         $sep = -1; // position of the last blank space
3180                         $l = 0; // current string lenght
3181                         $nl = 0; //number of lines
3182                         $linebreak = false;
3183                         // for each character
3184                         while ($i < $nb) {
3185                                 //Get the current character
3186                                 $c = $chars[$i];
3187                                 if ($c == 10) { // 10 = "\n" = new line
3188                                         //Explicit line break
3189                                         if ($align == "J") {
3190                                                 if ($this->rtl) {
3191                                                         $talign = "R";
3192                                                 } else {
3193                                                         $talign = "L";
3194                                                 }
3195                                         } else {
3196                                                 $talign = $align;
3197                                         }
3198                                         if ($firstline) {
3199                                                 $startx = $this->x;
3200                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3201                                                 if ($this->rtl) {
3202                                                         $this->endlinex = $startx - $linew;
3203                                                 } else {
3204                                                         $this->endlinex = $startx + $linew;
3205                                                 }
3206                                                 $w = $linew;
3207                                                 $tmpcmargin = $this->cMargin;
3208                                                 $this->cMargin = 0;
3209                                         }
3210                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $talign, $fill, $link, $stretch);
3211                                         if ($firstline) {
3212                                                 $this->cMargin = $tmpcmargin;
3213                                                 return ($this->UTF8ArrSubString($chars, $i));
3214                                         }
3215                                         $nl++;
3216                                         $j = $i + 1;
3217                                         $l = 0;
3218                                         $sep = -1;
3219                                         $w = $this->getRemainingWidth();
3220                                         $wmax = $w - (2 * $this->cMargin);
3221                                 } else {
3222                                         if (preg_match("/[\s]/u", $this->unichr($c))) {
3223                                                 // update last blank space position
3224                                                 $sep = $i;
3225                                         }
3226                                         // update string length
3227                                         if (($this->isunicode) AND ($arabic)) {
3228                                                 // with bidirectional algorithm some chars may be changed affecting the line length
3229                                                 // *** very slow ***
3230                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), $this->tmprtl));
3231                                         } else {
3232                                                 $l += $this->GetCharWidth($c);
3233                                         }
3234                                         if ($l > $wmax) {
3235                                                 // we have reached the end of column
3236                                                 if ($sep == -1) {
3237                                                         // check if the line was already started
3238                                                         if (($this->rtl AND ($this->x < ($this->w - $this->rMargin)))
3239                                                                 OR ((!$this->rtl) AND ($this->x > $this->lMargin))) {
3240                                                                 // print a void cell and go to next line
3241                                                                 $this->Cell($w, $h, "", 0, 1);
3242                                                                 $linebreak = true;
3243                                                         } else {
3244                                                                 // truncate the word because do not fit on column
3245                                                                 if ($firstline) {
3246                                                                         $startx = $this->x;
3247                                                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3248                                                                         if ($this->rtl) {
3249                                                                                 $this->endlinex = $startx - $linew;
3250                                                                         } else {
3251                                                                                 $this->endlinex = $startx + $linew;
3252                                                                         }
3253                                                                         $w = $linew;
3254                                                                         $tmpcmargin = $this->cMargin;
3255                                                                         $this->cMargin = 0;
3256                                                                 }
3257                                                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $align, $fill, $link, $stretch);
3258                                                                 if ($firstline) {
3259                                                                         $this->cMargin = $tmpcmargin;
3260                                                                         return ($this->UTF8ArrSubString($chars, $i));
3261                                                                 }
3262                                                                 $j = $i;
3263                                                                 $i--;
3264                                                         }
3265                                                 } else {
3266                                                         // word wrapping
3267                                                         if ($firstline) {
3268                                                                 $startx = $this->x;
3269                                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $sep), $this->tmprtl));
3270                                                                 if ($this->rtl) {
3271                                                                         $this->endlinex = $startx - $linew;
3272                                                                 } else {
3273                                                                         $this->endlinex = $startx + $linew;
3274                                                                 }
3275                                                                 $w = $linew;
3276                                                                 $tmpcmargin = $this->cMargin;
3277                                                                 $this->cMargin = 0;
3278                                                         }
3279                                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $sep), 0, 1, $align, $fill, $link, $stretch);
3280                                                         if ($firstline) {
3281                                                                 $this->cMargin = $tmpcmargin;
3282                                                                 return ($this->UTF8ArrSubString($chars, $sep));
3283                                                         }
3284                                                         $i = $sep;
3285                                                         $sep = -1;
3286                                                         $j = ($i+1);
3287                                                 }
3288                                                 $w = $this->getRemainingWidth();
3289                                                 $wmax = $w - (2 * $this->cMargin);
3290                                                 if ($linebreak) {
3291                                                         $linebreak = false;
3292                                                 } else {
3293                                                         $nl++;
3294                                                         $l = 0;
3295                                                 }
3296                                         }
3297                                 }
3298                                 $i++;
3299                         } // end while i < nb
3300                         // print last substring (if any)
3301                         if ($l > 0) {
3302                                 switch ($align) {
3303                                         case "J":
3304                                         case "C": {
3305                                                 $w = $w;
3306                                                 break;
3307                                         }
3308                                         case "L": {
3309                                                 if ($this->rtl) {
3310                                                         $w = $w;
3311                                                 } else {
3312                                                         $w = $l;
3313                                                 }
3314                                                 break;
3315                                         }
3316                                         case "R": {
3317                                                 if ($this->rtl) {
3318                                                         $w = $l;
3319                                                 } else {
3320                                                         $w = $w;
3321                                                 }
3322                                                 break;
3323                                         }
3324                                         default: {
3325                                                 $w = $l;
3326                                                 break;
3327                                         }
3328                                 }
3329                                 if ($firstline) {
3330                                         $startx = $this->x;
3331                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $nb), $this->tmprtl));
3332                                         if ($this->rtl) {
3333                                                 $this->endlinex = $startx - $linew;
3334                                         } else {
3335                                                 $this->endlinex = $startx + $linew;
3336                                         }
3337                                         $w = $linew;
3338                                         $tmpcmargin = $this->cMargin;
3339                                         $this->cMargin = 0;
3340                                 }
3341                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $nb), 0, $ln, $align, $fill, $link, $stretch);
3342                                 if ($firstline) {
3343                                         $this->cMargin = $tmpcmargin;
3344                                         return ($this->UTF8ArrSubString($chars, $nb));
3345                                 }
3346                                 $nl++;
3347                         }
3348                         return $nl;
3349                 }
3350
3351                 /**
3352                 * Returns the remaining width between the current position and margins.
3353                 * @return int Return the remaining width
3354                 * @access protected
3355                 */
3356                 function getRemainingWidth() {
3357                         if ($this->rtl) {
3358                                 return ($this->x - $this->lMargin);
3359                         } else {
3360                                 return ($this->w - $this->rMargin - $this->x);
3361                         }
3362                 }
3363
3364          /**
3365                 * Extract a slice of the $strarr array and return it as string.
3366                 * @param string $strarr The input array of characters.
3367                 * @param int $start the starting element of $strarr.
3368                 * @param int $end first element that will not be returned.
3369                 * @return Return part of a string
3370                 */
3371                 function UTF8ArrSubString($strarr, $start='', $end='') {
3372                         if (strlen($start) == 0) {
3373                                 $start = 0;
3374                         }
3375                         if (strlen($end) == 0) {
3376                                 $end = count($strarr);
3377                         }
3378                         $string = "";
3379                         for ($i=$start; $i < $end; $i++) {
3380                                 $string .= $this->unichr($strarr[$i]);
3381                         }
3382                         return $string;
3383                 }
3384
3385                 /**
3386                 * Returns the unicode caracter specified by UTF-8 code
3387                 * @param int $c UTF-8 code
3388                 * @return Returns the specified character.
3389                 * @author Miguel Perez, Nicola Asuni
3390                 * @since 2.3.000 (2008-03-05)
3391                 */
3392                 function unichr($c) {
3393                         if (!$this->isunicode) {
3394                                 return chr($c);
3395                         } elseif ($c <= 0x7F) {
3396                                 // one byte
3397                                 return chr($c);
3398                         } elseif ($c <= 0x7FF) {
3399                                 // two bytes
3400                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
3401                         } elseif ($c <= 0xFFFF) {
3402                                 // three bytes
3403                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3404                         } elseif ($c <= 0x10FFFF) {
3405                                 // four bytes
3406                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3407                         } else {
3408                                 return "";
3409                         }
3410                 }
3411
3412                 /**
3413                 * Puts an image in the page.
3414                 * The upper-left corner must be given.
3415                 * The dimensions can be specified in different ways:<ul>
3416                 * <li>explicit width and height (expressed in user unit)</li>
3417                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
3418                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
3419                 * 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;
3420                 * The format can be specified explicitly or inferred from the file extension.<br />
3421                 * It is possible to put a link on the image.<br />
3422                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
3423                 * @param string $file Name of the file containing the image.
3424                 * @param float $x Abscissa of the upper-left corner.
3425                 * @param float $y Ordinate of the upper-left corner.
3426                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
3427                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
3428                 * @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.
3429                 * @param mixed $link URL or identifier returned by AddLink().
3430                 * @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>
3431                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
3432                 * @param int $dpi dot-per-inch resolution used on resize
3433                 * @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>
3434                 * @since 1.1
3435                 * @see AddLink()
3436                 */
3437                 function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
3438                         // get image size
3439                         list($pixw, $pixh) = getimagesize($file);
3440                         // calculate image width and height on document
3441                         if (($w <= 0) AND ($h <= 0)) {
3442                                 // convert image size to document unit
3443                                 $w = $pixw / ($this->imgscale * $this->k);
3444                                 $h = $pixh / ($this->imgscale * $this->k);
3445                         } elseif ($w <= 0) {
3446                                 $w = $h * $pixw / $pixh;
3447                         } elseif ($h <= 0) {
3448                                 $h = $w * $pixh / $pixw;
3449                         }
3450                         // calculate new minimum dimensions in pixels
3451                         $neww = round($w * $this->k * $dpi / $this->dpi);
3452                         $newh = round($h * $this->k * $dpi / $this->dpi);
3453                         // check if resize is necessary (resize is used only to reduce the image)
3454                         if (($neww * $newh) >= ($pixw * $pixh)) {
3455                                 $resize = false;
3456                         }
3457                         // check if image has been already added on document
3458                         if (!isset($this->images[$file])) {
3459                                 //First use of image, get info
3460                                 if ($type == '') {
3461                                         $fileinfo = pathinfo($file);
3462                                         if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
3463                                                 $type = $fileinfo['extension'];
3464                                         } else {
3465                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
3466                                         }
3467                                 }
3468                                 $type = strtolower($type);
3469                                 if ($type == "jpg") {
3470                                         $type = "jpeg";
3471                                 }
3472                                 $mqr = get_magic_quotes_runtime();
3473                                 set_magic_quotes_runtime(0);
3474                                 // Specific image handlers
3475                                 $mtd = '_parse'.$type;
3476                                 // GD image handler function
3477                                 $gdfunction = "imagecreatefrom".$type;
3478                                 $info = false;
3479                                 if ((method_exists($this,$mtd)) AND (!($resize AND function_exists($gdfunction)))) {
3480                                         $info = $this->$mtd($file);
3481                                 }
3482                                 if (!$info) {
3483                                         if (function_exists($gdfunction)) {
3484                                                 $img = $gdfunction($file);
3485                                                 if ($resize) {
3486                                                         $imgr = imagecreatetruecolor($neww, $newh);
3487                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
3488                                                         $info = $this->_toJPEG($imgr);
3489                                                 } else {
3490                                                         $info = $this->_toJPEG($img);
3491                                                 }
3492                                         } else {
3493                                                 return;
3494                                         }
3495                                 }
3496                                 if ($info === false) {
3497                                         //If false, we cannot process image
3498                                         return;
3499                                 }
3500                                 set_magic_quotes_runtime($mqr);
3501                                 $info['i'] = count($this->images) + 1;
3502                                 // add image to document
3503                                 $this->images[$file] = $info;
3504                         } else {
3505                                 $info = $this->images[$file];
3506                         }
3507                         // 2007-10-19 Warren Sherliker
3508                         // Check whether we need a new page first as this does not fit
3509                         // Copied from Cell()
3510                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
3511                                 // Automatic page break
3512                                 $this->AddPage($this->CurOrientation);
3513                                 // Reset coordinates to top fo next page
3514                                 $x = $this->GetX();
3515                                 $y = $this->GetY();
3516                         }
3517                         // 2007-10-19 Warren Sherliker: End Edit
3518                         // set bottomcoordinates
3519                         $this->img_rb_y = $y + $h;
3520                         // set alignment
3521                         if ($this->rtl) {
3522                                 if ($palign == 'L') {
3523                                         $ximg = $this->lMargin;
3524                                         // set right side coordinate
3525                                         $this->img_rb_x = $ximg + $w;
3526                                 } elseif ($palign == 'C') {
3527                                         $ximg = ($this->w - $x - $w) / 2;
3528                                         // set right side coordinate
3529                                         $this->img_rb_x = $ximg + $w;
3530                                 } else {
3531                                         $ximg = $this->w - $x - $w;
3532                                         // set left side coordinate
3533                                         $this->img_rb_x = $ximg;
3534                                 }
3535                         } else {
3536                                 if ($palign == 'R') {
3537                                         $ximg = $this->w - $this->rMargin - $w;
3538                                         // set left side coordinate
3539                                         $this->img_rb_x = $ximg;
3540                                 } elseif ($palign == 'C') {
3541                                         $ximg = ($this->w - $x - $w) / 2;
3542                                         // set right side coordinate
3543                                         $this->img_rb_x = $ximg + $w;
3544                                 } else {
3545                                         $ximg = $x;
3546                                         // set right side coordinate
3547                                         $this->img_rb_x = $ximg + $w;
3548                                 }
3549                         }
3550                         $xkimg = $ximg * $this->k;
3551                         $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']));
3552                         if ($link) {
3553                                 $this->Link($ximg, $y, $w, $h, $link);
3554                         }
3555                         // set pointer to align the successive text/objects
3556                         switch($align) {
3557                                 case 'T':{
3558                                         $this->y = $y;
3559                                         $this->x = $this->img_rb_x;
3560                                         break;
3561                                 }
3562                                 case 'M':{
3563                                         $this->y = $y + round($h/2);
3564                                         $this->x = $this->img_rb_x;
3565                                         break;
3566                                 }
3567                                 case 'B':{
3568                                         $this->y = $this->img_rb_y;
3569                                         $this->x = $this->img_rb_x;
3570                                         break;
3571                                 }
3572                                 case 'N':{
3573                                         $this->SetY($this->img_rb_y);
3574                                         break;
3575                                 }
3576                                 default:{
3577                                         break;
3578                                 }
3579                         }
3580                         $this->endlinex = $this->img_rb_x;
3581                 }
3582
3583                 /**
3584                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
3585                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
3586                 * @param string $file Image file name.
3587                 * @param image $image Image object.
3588                 * return image JPEG image object.
3589                 * @access protected
3590                 */
3591                 function _toJPEG($image) {
3592                         $tempname = tempnam(K_PATH_CACHE,'jpg');
3593                         imagejpeg($image, $tempname, $this->jpeg_quality);
3594                         imagedestroy($image);
3595                         $retvars = $this->_parsejpeg($tempname);
3596                         // tidy up by removing temporary image
3597                         unlink($tempname);
3598                         return $retvars;
3599                 }
3600
3601                 /**
3602                 * Extract info from a JPEG file without using the GD library.
3603                 * @param string $file image file to parse
3604                 * @return array structure containing the image data
3605                 * @access protected
3606                 */
3607                 function _parsejpeg($file) {
3608                         $a = getimagesize($file);
3609                         if (empty($a)) {
3610                                 $this->Error('Missing or incorrect image file: '.$file);
3611                         }
3612                         if ($a[2] != 2) {
3613                                 $this->Error('Not a JPEG file: '.$file);
3614                         }
3615                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
3616                                 $colspace = 'DeviceRGB';
3617                         } elseif ($a['channels'] == 4) {
3618                                 $colspace = 'DeviceCMYK';
3619                         }       else {
3620                                 $colspace = 'DeviceGray';
3621                         }
3622                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
3623                         $data = file_get_contents($file);
3624                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
3625                 }
3626
3627                 /**
3628                 * Extract info from a PNG file without using the GD library.
3629                 * @param string $file image file to parse
3630                 * @return array structure containing the image data
3631                 * @access protected
3632                 */
3633                 function _parsepng($file) {
3634                         $f = fopen($file,'rb');
3635                         if (empty($f)) {
3636                                 $this->Error('Can\'t open image file: '.$file);
3637                         }
3638                         //Check signature
3639                         if (fread($f,8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3640                                 $this->Error('Not a PNG file: '.$file);
3641                         }
3642                         //Read header chunk
3643                         fread($f,4);
3644                         if (fread($f,4) != 'IHDR') {
3645                                 $this->Error('Incorrect PNG file: '.$file);
3646                         }
3647                         $w = $this->_freadint($f);
3648                         $h = $this->_freadint($f);
3649                         $bpc = ord(fread($f,1));
3650                         if ($bpc > 8) {
3651                                 //$this->Error('16-bit depth not supported: '.$file);
3652                                 return false;
3653                         }
3654                         $ct = ord(fread($f,1));
3655                         if ($ct == 0) {
3656                                 $colspace = 'DeviceGray';
3657                         } elseif ($ct == 2) {
3658                                 $colspace = 'DeviceRGB';
3659                         } elseif ($ct == 3) {
3660                                 $colspace = 'Indexed';
3661                         } else {
3662                                 //$this->Error('Alpha channel not supported: '.$file);
3663                                 return false;
3664                         }
3665                         if (ord(fread($f,1)) != 0) {
3666                                 //$this->Error('Unknown compression method: '.$file);
3667                                 return false;
3668                         }
3669                         if (ord(fread($f,1)) != 0) {
3670                                 //$this->Error('Unknown filter method: '.$file);
3671                                 return false;
3672                         }
3673                         if (ord(fread($f,1)) != 0) {
3674                                 //$this->Error('Interlacing not supported: '.$file);
3675                                 return false;
3676                         }
3677                         fread($f,4);
3678                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3679                         //Scan chunks looking for palette, transparency and image data
3680                         $pal = '';
3681                         $trns = '';
3682                         $data = '';
3683                         do {
3684                                 $n = $this->_freadint($f);
3685                                 $type = fread($f,4);
3686                                 if ($type == 'PLTE') {
3687                                         //Read palette
3688                                         $pal = fread($f,$n);
3689                                         fread($f,4);
3690                                 } elseif ($type == 'tRNS') {
3691                                         //Read transparency info
3692                                         $t = fread($f,$n);
3693                                         if ($ct == 0) {
3694                                                 $trns = array(ord(substr($t,1,1)));
3695                                         }
3696                                         elseif ($ct == 2) {
3697                                                 $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
3698                                         } else {
3699                                                 $pos = strpos($t,chr(0));
3700                                                 if ($pos !== false) {
3701                                                         $trns = array($pos);
3702                                                 }
3703                                         }
3704                                         fread($f, 4);
3705                                 } elseif ($type == 'IDAT') {
3706                                         //Read image data block
3707                                         $data .= fread($f,$n);
3708                                         fread($f, 4);
3709                                 } elseif ($type == 'IEND') {
3710                                         break;
3711                                 } else {
3712                                         fread($f, $n+4);
3713                                 }
3714                         }
3715                         while ($n);
3716                         if (($colspace == 'Indexed') AND (empty($pal))) {
3717                                 //$this->Error('Missing palette in '.$file);
3718                                 return false;
3719                         }
3720                         fclose($f);
3721                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
3722                 }
3723
3724                 /**
3725                 * Performs a line break.
3726                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
3727                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
3728                 * @param boolean $cell if true add a cMargin to the x coordinate
3729                 * @since 1.0
3730                 * @see Cell()
3731                 */
3732                 function Ln($h='', $cell=false) {
3733                         //Line feed; default value is last cell height
3734                         if ($cell) {
3735                                 $cellmargin = $this->cMargin;
3736                         } else {
3737                                 $cellmargin = 0;
3738                         }
3739                         if ($this->rtl) {
3740                                 $this->x = $this->w - $this->rMargin - $cellmargin;
3741                         } else {
3742                                 $this->x = $this->lMargin + $cellmargin;
3743                         }
3744                         if (is_string($h)) {
3745                                 $this->y += $this->lasth;
3746                         } else {
3747                                 $this->y += $h;
3748                         }
3749                         $this->newline = true;
3750                 }
3751
3752                 /**
3753                 * Returns the relative X value of current position.
3754                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
3755                 * @return float
3756                 * @since 1.2
3757                 * @see SetX(), GetY(), SetY()
3758                 */
3759                 function GetX() {
3760                         //Get x position
3761                         if ($this->rtl) {
3762                                 return ($this->w - $this->x);
3763                         } else {
3764                                 return $this->x;
3765                         }
3766                 }
3767
3768                 /**
3769                 * Returns the absolute X value of current position.
3770                 * @return float
3771                 * @since 1.2
3772                 * @see SetX(), GetY(), SetY()
3773                 */
3774                 function GetAbsX() {
3775                         return $this->x;
3776                 }
3777
3778                 /**
3779                 * Returns the ordinate of the current position.
3780                 * @return float
3781                 * @since 1.0
3782                 * @see SetY(), GetX(), SetX()
3783                 */
3784                 function GetY() {
3785                         //Get y position
3786                         return $this->y;
3787                 }
3788
3789                 /**
3790                 * Defines the abscissa of the current position.
3791                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
3792                 * @param float $x The value of the abscissa.
3793                 * @since 1.2
3794                 * @see GetX(), GetY(), SetY(), SetXY()
3795                 */
3796                 function SetX($x) {
3797                         //Set x position
3798                         if ($this->rtl) {
3799                                 if ($x >= 0) {
3800                                         $this->x = $this->w - $x;
3801                                 } else {
3802                                         $this->x = abs($x);
3803                                 }
3804                         } else {
3805                                 if ($x >= 0) {
3806                                         $this->x = $x;
3807                                 } else {
3808                                         $this->x = $this->w + $x;
3809                                 }
3810                         }
3811                 }
3812
3813                 /**
3814                 * Moves the current abscissa back to the left margin and sets the ordinate.
3815                 * If the passed value is negative, it is relative to the bottom of the page.
3816                 * @param float $y The value of the ordinate.
3817                 * @since 1.0
3818                 * @see GetX(), GetY(), SetY(), SetXY()
3819                 */
3820                 function SetY($y) {
3821                         //Set y position and reset x
3822                         if ($this->rtl) {
3823                                 $this->x = $this->w - $this->rMargin;
3824                         } else {
3825                                 $this->x = $this->lMargin;
3826                         }
3827                         if ($y >= 0) {
3828                                 $this->y = $y;
3829                         } else {
3830                                 $this->y = $this->h + $y;
3831                         }
3832                 }
3833
3834                 /**
3835                 * Defines the abscissa and ordinate of the current position.
3836                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
3837                 * @param float $x The value of the abscissa
3838                 * @param float $y The value of the ordinate
3839                 * @since 1.2
3840                 * @see SetX(), SetY()
3841                 */
3842                 function SetXY($x, $y) {
3843                         //Set x and y positions
3844                         $this->SetY($y);
3845                         $this->SetX($x);
3846                 }
3847
3848                 /**
3849                 * Send the document to a given destination: string, local file or browser.
3850                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
3851                 * The method first calls Close() if necessary to terminate the document.
3852                 * @param string $name The name of the file when saved.
3853                 * @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>
3854                 * @since 1.0
3855                 * @see Close()
3856                 */
3857                 function Output($name='doc.pdf', $dest='I') {
3858                         //Output PDF to some destination
3859                         //Finish document if necessary
3860                         if ($this->state < 3) {
3861                                 $this->Close();
3862                         }
3863                         //Normalize parameters
3864                         if (is_bool($dest)) {
3865                                 $dest = $dest ? 'D' : 'F';
3866                         }
3867                         $dest = strtoupper($dest);
3868                         if ($dest != 'F') {
3869                                 $name = str_replace("+", "%20", urlencode($name));
3870                                 $name = preg_replace('/[\r\n]+\s*/', '' , $name);
3871                         }
3872                         switch($dest) {
3873                                 case 'I': {
3874                                         //Send to standard output
3875                                         if (ob_get_contents()) {
3876                                                 $this->Error('Some data has already been output, can\'t send PDF file');
3877                                         }
3878                                         if (php_sapi_name() != 'cli') {
3879                                                 //We send to a browser
3880                                                 header('Content-Type: application/pdf');
3881                                                 if (headers_sent()) {
3882                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
3883                                                 }
3884                                                 // Disable caching
3885                                                 header('Cache-Control: private, must-revalidate');
3886                                                 header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
3887                                                 header('Content-Length: '.strlen($this->buffer));
3888                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
3889                                         }
3890                                         echo $this->buffer;
3891                                         break;
3892                                 }
3893                                 case 'D': {
3894                                         //Download file
3895                                         if (ob_get_contents()) {
3896                                                 $this->Error('Some data has already been output, can\'t send PDF file');
3897                                         }
3898                                         header('Content-Description: File Transfer');
3899                                         if (headers_sent()) {
3900                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
3901                                         }
3902                                         header('Cache-Control: private, must-revalidate');
3903                                         header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
3904                                         // always modified
3905                                         header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
3906                                         // force download dialog
3907                                         header("Content-Type: application/force-download");
3908                                         header("Content-Type: application/octet-stream", false);
3909                                         header("Content-Type: application/download", false);
3910                                         // use the Content-Disposition header to supply a recommended filename
3911                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
3912                                         header("Content-Transfer-Encoding: binary");
3913                                         header("Content-Length: ".strlen($this->buffer));
3914                                         echo $this->buffer;
3915                                         break;
3916                                 }
3917                                 case 'F': {
3918                                         //Save to local file
3919                                         $f = fopen($name, 'wb');
3920                                         if (!$f) {
3921                                                 $this->Error('Unable to create output file: '.$name);
3922                                         }
3923                                         fwrite($f, $this->buffer,strlen($this->buffer));
3924                                         fclose($f);
3925                                         break;
3926                                 }
3927                                 case 'S': {
3928                                         //Return as a string
3929                                         return $this->buffer;
3930                                 }
3931                                 default: {
3932                                         $this->Error('Incorrect output destination: '.$dest);
3933                                 }
3934                         }
3935                         return '';
3936                 }
3937
3938                 /**
3939                 * Check for locale-related bug
3940                 * @access protected
3941                 */
3942                 function _dochecks() {
3943                         //Check for locale-related bug
3944                         if (1.1 == 1) {
3945                                 $this->Error('Don\'t alter the locale before including class file');
3946                         }
3947                         //Check for decimal separator
3948                         if (sprintf('%.1f', 1.0) != '1.0') {
3949                                 setlocale(LC_NUMERIC, 'C');
3950                         }
3951                 }
3952
3953                 /**
3954                 * Return fonts path
3955                 * @return string
3956                 * @access protected
3957                 */
3958                 function _getfontpath() {
3959                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
3960                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
3961                         }
3962                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
3963                 }
3964
3965                 /**
3966                 * Output pages.
3967                 * @access protected
3968                 */
3969                 function _putpages() {
3970                         $nb = count($this->pages);
3971                         if (!empty($this->pagegroups)) {
3972                                 // do page number replacement
3973                                 foreach ($this->pagegroups as $k => $v) {
3974                                         $alias = $this->_escapetext($k);
3975                                         $nbstr = $this->UTF8ToUTF16BE($v, false);
3976                                         for ($n = 1; $n <= $nb; $n++) {
3977                                                 $this->pages[$n] = str_replace($alias, $nbstr, $this->pages[$n]);
3978                                         }
3979                                 }
3980                         }
3981                         if (!empty($this->AliasNbPages)) {
3982                                 $nbstr = $this->UTF8ToUTF16BE($nb, false);
3983                                 //Replace number of pages
3984                                 for($n = 1; $n <= $nb; $n++) {
3985                                         $this->pages[$n] = str_replace($this->AliasNbPages, $nbstr, $this->pages[$n]);
3986                                 }
3987                         }
3988                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
3989                         for($n=1; $n <= $nb; $n++) {
3990                                 //Page
3991                                 $this->_newobj();
3992                                 $this->_out('<</Type /Page');
3993                                 $this->_out('/Parent 1 0 R');
3994                                 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
3995                                 $this->_out('/Resources 2 0 R');
3996                                 if (isset($this->PageLinks[$n])) {
3997                                         //Links
3998                                         $annots = '/Annots [';
3999                                         foreach($this->PageLinks[$n] as $pl) {
4000                                                 $rect = sprintf('%.2f %.2f %.2f %.2f', $pl[0], $pl[1], $pl[0]+$pl[2], $pl[1]-$pl[3]);
4001                                                 $annots .= '<</Type /Annot /Subtype /Link /Rect ['.$rect.'] /Border [0 0 0] ';
4002                                                 if (is_string($pl[4])) {
4003                                                         $annots .= '/A <</S /URI /URI '.$this->_uristring($pl[4]).'>>>>';
4004                                                 }
4005                                                 else {
4006                                                         $l = $this->links[$pl[4]];
4007                                                         $h = $this->pagedim[$l[0]]['h'];
4008                                                         $annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>', 1+2*$l[0], $h-$l[1]*$this->k);
4009                                                 }
4010                                         }
4011                                         $this->_out($annots.']');
4012                                 }
4013                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
4014                                 $this->_out('endobj');
4015                                 //Page content
4016                                 $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
4017                                 $this->_newobj();
4018                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
4019                                 $this->_putstream($p);
4020                                 $this->_out('endobj');
4021                         }
4022                         //Pages root
4023                         $this->offsets[1] = strlen($this->buffer);
4024                         $this->_out('1 0 obj');
4025                         $this->_out('<</Type /Pages');
4026                         $kids='/Kids [';
4027                         for($i=0; $i < $nb; $i++) {
4028                                 $kids .= (3+2*$i).' 0 R ';
4029                         }
4030                         $this->_out($kids.']');
4031                         $this->_out('/Count '.$nb);
4032                         //$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
4033                         $this->_out('>>');
4034                         $this->_out('endobj');
4035                 }
4036
4037                 /**
4038                 * Output fonts.
4039                 * _putfonts
4040                 * @access protected
4041                 */
4042                 function _putfonts() {
4043                         $nf = $this->n;
4044                         foreach($this->diffs as $diff) {
4045                                 //Encodings
4046                                 $this->_newobj();
4047                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
4048                                 $this->_out('endobj');
4049                         }
4050                         $mqr = get_magic_quotes_runtime();
4051                         set_magic_quotes_runtime(0);
4052                         foreach($this->FontFiles as $file => $info) {
4053                                 //Font file embedding
4054                                 $this->_newobj();
4055                                 $this->FontFiles[$file]['n'] = $this->n;
4056                                 $font = file_get_contents($this->_getfontpath().strtolower($file));
4057                                 $compressed = (substr($file,-2)=='.z');
4058                                 if ((!$compressed) AND (isset($info['length2']))) {
4059                                         $header = (ord($font{0}) == 128);
4060                                         if ($header) {
4061                                                 //Strip first binary header
4062                                                 $font = substr($font,6);
4063                                         }
4064                                         if ($header AND (ord($font{$info['length1']}) == 128)) {
4065                                                 //Strip second binary header
4066                                                 $font = substr($font, 0, $info['length1']).substr($font, $info['length1']+6);
4067                                         }
4068                                 }
4069                                 $this->_out('<</Length '.strlen($font));
4070                                 if ($compressed) {
4071                                         $this->_out('/Filter /FlateDecode');
4072                                 }
4073                                 $this->_out('/Length1 '.$info['length1']);
4074                                 if (isset($info['length2'])) {
4075                                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
4076                                 }
4077                                 $this->_out('>>');
4078                                 $this->_putstream($font);
4079                                 $this->_out('endobj');
4080                         }
4081                         set_magic_quotes_runtime($mqr);
4082                         foreach($this->fonts as $k => $font) {
4083                                 //Font objects
4084                                 $this->fonts[$k]['n'] = $this->n + 1;
4085                                 $type = $font['type'];
4086                                 $name = $font['name'];
4087                                 if ($type == 'core') {
4088                                         //Standard font
4089                                         $this->_newobj();
4090                                         $this->_out('<</Type /Font');
4091                                         $this->_out('/BaseFont /'.$name);
4092                                         $this->_out('/Subtype /Type1');
4093                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
4094                                                 $this->_out('/Encoding /WinAnsiEncoding');
4095                                         }
4096                                         $this->_out('>>');
4097                                         $this->_out('endobj');
4098                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
4099                                         //Additional Type1 or TrueType font
4100                                         $this->_newobj();
4101                                         $this->_out('<</Type /Font');
4102                                         $this->_out('/BaseFont /'.$name);
4103                                         $this->_out('/Subtype /'.$type);
4104                                         $this->_out('/FirstChar 32 /LastChar 255');
4105                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
4106                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
4107                                         if ($font['enc']) {
4108                                                 if (isset($font['diff'])) {
4109                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
4110                                                 } else {
4111                                                         $this->_out('/Encoding /WinAnsiEncoding');
4112                                                 }
4113                                         }
4114                                         $this->_out('>>');
4115                                         $this->_out('endobj');
4116                                         //Widths
4117                                         $this->_newobj();
4118                                         $cw = &$font['cw'];
4119                                         $s = '[';
4120                                         for($i=32; $i <= 255; $i++) {
4121                                                 //$s .= $cw[chr($i)].' ';
4122                                                 $s .= $cw[$i].' ';
4123                                         }
4124                                         $this->_out($s.']');
4125                                         $this->_out('endobj');
4126                                         //Descriptor
4127                                         $this->_newobj();
4128                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4129                                         foreach($font['desc'] as $k => $v) {
4130                                                 $s .= ' /'.$k.' '.$v;
4131                                         }
4132                                         $file = $font['file'];
4133                                         if ($file) {
4134                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
4135                                         }
4136                                         $this->_out($s.'>>');
4137                                         $this->_out('endobj');
4138                                 } else {
4139                                         //Allow for additional types
4140                                         $mtd = '_put'.strtolower($type);
4141                                         if (!method_exists($this, $mtd)) {
4142                                                 $this->Error('Unsupported font type: '.$type);
4143                                         }
4144                                         $this->$mtd($font);
4145                                 }
4146                         }
4147                 }
4148
4149                 /**
4150                  * Output CID-0 fonts.
4151                  * @param array $font font data
4152                  * @access protected
4153                  * @author Andrew Whitehead, Nicola Asuni
4154                  * @since 3.2.000 (2008-06-23)
4155                  */
4156                 function _putcidfont0($font) {
4157                         $longname = $name = $font['name'];
4158                         $enc = $font['enc'];
4159                         if ($enc) {
4160                                 $longname .= "-$enc";
4161                         }
4162                         $this->_newobj();
4163                         $this->_out('<</Type /Font');
4164                         $this->_out('/BaseFont /'.$longname);
4165                         $this->_out('/Subtype /Type0');
4166                         if ($enc) {
4167                                 $this->_out('/Encoding /'.$enc);
4168                         }
4169                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4170                         $this->_out('>>');
4171                         $this->_out('endobj');
4172                         $this->_newobj();
4173                         $this->_out('<</Type /Font');
4174                         $this->_out('/BaseFont /'.$name);
4175                         $this->_out('/Subtype /CIDFontType0');
4176                         $cidinfo = '/Registry ('.$font['cidinfo']['Registry'].') ';
4177                         $cidinfo .= '/Ordering ('.$font['cidinfo']['Ordering'].') ';
4178                         $cidinfo .= '/Supplement '.$font['cidinfo']['Supplement'];
4179                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
4180                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
4181                         $codes = array_keys($font['cw']);
4182                         $first = current($codes);
4183                         $last = end($codes);
4184                         $this->_out('/DW '.$font['dw']);
4185                         $w = '/W [';
4186                         $ranges = array();
4187                         $currange = 0;
4188                         for($i = $first; $i <= $last; $i++) {
4189                                 if (isset($font['cw'][$i]) AND (!$currange)) {
4190                                         $currange = $i - 31;
4191                                 } elseif (!isset($font['cw'][$i])) {
4192                                         $currange = 0;
4193                                 }
4194                                 if ($currange) {
4195                                         $ranges[$currange][] = $font['cw'][$i];
4196                                 }
4197                         }
4198                         foreach($ranges as $k => $ws) {
4199                                 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
4200                         }
4201                         $w .= ' ]';
4202                         $this->_out($w);
4203                         $this->_out('>>');
4204                         $this->_out('endobj');
4205                         $this->_newobj();
4206                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4207                         foreach($font['desc'] as $k => $v) {
4208                                 $s .= ' /'.$k.' '.$v;
4209                         }
4210                         $this->_out($s.'>>');
4211                         $this->_out('endobj');
4212                 }
4213
4214                 /**
4215                  * Output images.
4216                  * @access protected
4217                  */
4218                 function _putimages() {
4219                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4220                         reset($this->images);
4221                         while (list($file, $info) = each($this->images)) {
4222                                 $this->_newobj();
4223                                 $this->images[$file]['n'] = $this->n;
4224                                 $this->_out('<</Type /XObject');
4225                                 $this->_out('/Subtype /Image');
4226                                 $this->_out('/Width '.$info['w']);
4227                                 $this->_out('/Height '.$info['h']);
4228                                 if (isset($info["masked"])) {
4229                                         $this->_out('/SMask '.($this->n-1).' 0 R');
4230                                 }
4231                                 if ($info['cs'] == 'Indexed') {
4232                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]');
4233                                 } else {
4234                                         $this->_out('/ColorSpace /'.$info['cs']);
4235                                         if ($info['cs'] == 'DeviceCMYK') {
4236                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
4237                                         }
4238                                 }
4239                                 $this->_out('/BitsPerComponent '.$info['bpc']);
4240                                 if (isset($info['f'])) {
4241                                         $this->_out('/Filter /'.$info['f']);
4242                                 }
4243                                 if (isset($info['parms'])) {
4244                                         $this->_out($info['parms']);
4245                                 }
4246                                 if (isset($info['trns']) and is_array($info['trns'])) {
4247                                         $trns='';
4248                                         for($i=0; $i < count($info['trns']); $i++) {
4249                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
4250                                         }
4251                                         $this->_out('/Mask ['.$trns.']');
4252                                 }
4253                                 $this->_out('/Length '.strlen($info['data']).'>>');
4254                                 $this->_putstream($info['data']);
4255                                 unset($this->images[$file]['data']);
4256                                 $this->_out('endobj');
4257                                 //Palette
4258                                 if ($info['cs'] == 'Indexed') {
4259                                         $this->_newobj();
4260                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
4261                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
4262                                         $this->_putstream($pal);
4263                                         $this->_out('endobj');
4264                                 }
4265                         }
4266                 }
4267
4268                 /**
4269                 * Output object dictionary for images.
4270                 * @access protected
4271                 */
4272                 function _putxobjectdict() {
4273                         foreach($this->images as $image) {
4274                                 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
4275                         }
4276                 }
4277
4278                 /**
4279                 * Output Resources Dictionary.
4280                 * @access protected
4281                 */
4282                 function _putresourcedict(){
4283                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
4284                         $this->_out('/Font <<');
4285                         foreach($this->fonts as $font) {
4286                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
4287                         }
4288                         $this->_out('>>');
4289                         $this->_out('/XObject <<');
4290                         $this->_putxobjectdict();
4291                         $this->_out('>>');
4292                         // visibility
4293                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
4294                         // transparency
4295                         $this->_out('/ExtGState <<');
4296                         foreach($this->extgstates as $k => $extgstate) {
4297                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
4298                         }
4299                         $this->_out('>>');
4300                         // gradients
4301                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
4302                                 $this->_out('/Shading <<');
4303                                 foreach($this->gradients as $id => $grad) {
4304                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
4305                                 }
4306                                 $this->_out('>>');
4307                         }
4308                 }
4309
4310                 /**
4311                 * Output Resources.
4312                 * @access protected
4313                 */
4314                 function _putresources() {
4315                         $this->_putextgstates();
4316                         $this->_putocg();
4317                         $this->_putfonts();
4318                         $this->_putimages();
4319                         $this->_putshaders();
4320                         //Resource dictionary
4321                         $this->offsets[2] = strlen($this->buffer);
4322                         $this->_out('2 0 obj');
4323                         $this->_out('<<');
4324                         $this->_putresourcedict();
4325                         $this->_out('>>');
4326                         $this->_out('endobj');
4327                         $this->_putjavascript();
4328                         $this->_putbookmarks();
4329                         // encryption
4330                         if ($this->encrypted) {
4331                                 $this->_newobj();
4332                                 $this->enc_obj_id = $this->n;
4333                                 $this->_out('<<');
4334                                 $this->_putencryption();
4335                                 $this->_out('>>');
4336                                 $this->_out('endobj');
4337                         }
4338                 }
4339
4340                 /**
4341                 * Adds some Metadata information
4342                 * (see Chapter 10.2 of PDF Reference)
4343                 * @access protected
4344                 */
4345                 function _putinfo() {
4346                         if (!empty($this->title)) {
4347                                 $this->_out('/Title '.$this->_textstring($this->title));
4348                         }
4349                         if (!empty($this->author)) {
4350                                 $this->_out('/Author '.$this->_textstring($this->author));
4351                         }
4352                         if (!empty($this->subject)) {
4353                                 $this->_out('/Subject '.$this->_textstring($this->subject));
4354                         }
4355                         if (!empty($this->keywords)) {
4356                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
4357                         }
4358                         if (!empty($this->creator)) {
4359                                 $this->_out('/Creator '.$this->_textstring($this->creator));
4360                         }
4361                         if (defined('PDF_PRODUCER')) {
4362                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
4363                         }
4364                         $this->_out('/CreationDate '.$this->_datestring('D:'.date('YmdHis')));
4365                         $this->_out('/ModDate '.$this->_datestring('D:'.date('YmdHis')));
4366                 }
4367
4368                 /**
4369                 * Format a date string for meta information
4370                 * @param string $s date string to escape.
4371                 * @return string escaped string.
4372                 * @access protected
4373                 */
4374                 function _datestring($s) {
4375                         if ($this->encrypted) {
4376                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4377                         }
4378                         return '('. $this->_escape($s).')';
4379                 }
4380
4381                 /**
4382                 * Output Catalog.
4383                 * @access protected
4384                 */
4385                 function _putcatalog() {
4386                         $this->_out('/Type /Catalog');
4387                         $this->_out('/Pages 1 0 R');
4388
4389                         if ($this->ZoomMode == 'fullpage') {
4390                                 $this->_out('/OpenAction [3 0 R /Fit]');
4391                         } elseif ($this->ZoomMode == 'fullwidth') {
4392                                 $this->_out('/OpenAction [3 0 R /FitH null]');
4393                         } elseif ($this->ZoomMode == 'real') {
4394                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
4395                         } elseif (!is_string($this->ZoomMode)) {
4396                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
4397                         }
4398                         if (isset($this->LayoutMode) AND (!empty($this->LayoutMode))) {
4399                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
4400                         }
4401                         if (isset($this->PageMode) AND (!empty($this->PageMode))) {
4402                                 $this->_out('/PageMode /'.$this->PageMode);
4403                         }
4404                         if (isset($this->l['a_meta_language'])) {
4405                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
4406                         }
4407                         if (!empty($this->javascript)) {
4408                                 $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
4409                         }
4410                         if (count($this->outlines) > 0) {
4411                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
4412                                 $this->_out('/PageMode /UseOutlines');
4413                         }
4414                         $this->_putviewerpreferences();
4415                         $p = $this->n_ocg_print.' 0 R';
4416                         $v = $this->n_ocg_view.' 0 R';
4417                         $as = "<</Event /Print /OCGs [".$p." ".$v."] /Category [/Print]>> <</Event /View /OCGs [".$p." ".$v."] /Category [/View]>>";
4418                         $this->_out("/OCProperties <</OCGs [".$p." ".$v."] /D <</ON [".$p."] /OFF [".$v."] /AS [".$as."]>>>>");
4419                         $this->_putuserrights();
4420                 }
4421
4422                 /**
4423                 * Output viewer preferences.
4424                 * @author Nicola asuni
4425                 * @since 3.1.000 (2008-06-09)
4426                 * @access protected
4427                 */
4428                 function _putviewerpreferences() {
4429                         $this->_out('/ViewerPreferences<<');
4430                         if ($this->rtl) {
4431                                 $this->_out('/Direction /R2L');
4432                         } else {
4433                                 $this->_out('/Direction /L2R');
4434                         }
4435                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
4436                                 $this->_out('/HideToolbar true');
4437                         }
4438                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
4439                                 $this->_out('/HideMenubar true');
4440                         }
4441                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
4442                                 $this->_out('/HideWindowUI true');
4443                         }
4444                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
4445                                 $this->_out('/FitWindow true');
4446                         }
4447                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
4448                                 $this->_out('/CenterWindow true');
4449                         }
4450                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
4451                                 $this->_out('/DisplayDocTitle true');
4452                         }
4453                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
4454                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
4455                         }
4456                         if (isset($this->viewer_preferences['ViewArea'])) {
4457                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
4458                         }
4459                         if (isset($this->viewer_preferences['ViewClip'])) {
4460                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
4461                         }
4462                         if (isset($this->viewer_preferences['PrintArea'])) {
4463                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
4464                         }
4465                         if (isset($this->viewer_preferences['PrintClip'])) {
4466                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
4467                         }
4468                         if (isset($this->viewer_preferences['PrintScaling'])) {
4469                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
4470                         }
4471                         if (isset($this->viewer_preferences['Duplex']) AND (!empty($this->viewer_preferences['Duplex']))) {
4472                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
4473                         }
4474                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
4475                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
4476                                         $this->_out('/PickTrayByPDFSize true');
4477                                 } else {
4478                                         $this->_out('/PickTrayByPDFSize false');
4479                                 }
4480                         }
4481                         if (isset($this->viewer_preferences['PrintPageRange'])) {
4482                                 $PrintPageRangeNum = "";
4483                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
4484                                         $PrintPageRangeNum .= " ".($v-1)."";
4485                                 }
4486                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
4487                         }
4488                         if (isset($this->viewer_preferences['NumCopies'])) {
4489                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
4490                         }
4491                         $this->_out('>>');
4492                 }
4493
4494                 /**
4495                 * Output trailer.
4496                 * @access protected
4497                 */
4498                 function _puttrailer() {
4499                         $this->_out('/Size '.($this->n + 1));
4500                         $this->_out('/Root '.$this->n.' 0 R');
4501                         $this->_out('/Info '.($this->n - 1).' 0 R');
4502                         if ($this->encrypted) {
4503                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
4504                                 $this->_out('/ID [()()]');
4505                         }
4506                 }
4507
4508                 /**
4509                 * Output PDF header.
4510                 * @access protected
4511                 */
4512                 function _putheader() {
4513                         $this->_out('%PDF-'.$this->PDFVersion);
4514                 }
4515
4516                 /**
4517                 * Output end of document (EOF).
4518                 * @access protected
4519                 */
4520                 function _enddoc() {
4521                         $this->_putheader();
4522                         $this->_putpages();
4523                         $this->_putresources();
4524                         //Info
4525                         $this->_newobj();
4526                         $this->_out('<<');
4527                         $this->_putinfo();
4528                         $this->_out('>>');
4529                         $this->_out('endobj');
4530                         //Catalog
4531                         $this->_newobj();
4532                         $this->_out('<<');
4533                         $this->_putcatalog();
4534                         $this->_out('>>');
4535                         $this->_out('endobj');
4536                         //Cross-ref
4537                         $o = strlen($this->buffer);
4538                         $this->_out('xref');
4539                         $this->_out('0 '.($this->n + 1));
4540                         $this->_out('0000000000 65535 f ');
4541                         for($i=1; $i <= $this->n; $i++) {
4542                                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
4543                         }
4544                         //Trailer
4545                         $this->_out('trailer');
4546                         $this->_out('<<');
4547                         $this->_puttrailer();
4548                         $this->_out('>>');
4549                         $this->_out('startxref');
4550                         $this->_out($o);
4551                         $this->_out('%%EOF');
4552                         $this->state = 3;
4553                 }
4554
4555                 /**
4556                 * Initialize a new page.
4557                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
4558                 * @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>
4559                 * @access protected
4560                 */
4561                 function _beginpage($orientation='', $format='') {
4562                         $this->page++;
4563                         $this->pages[$this->page] = ""; // this mark should be removed before output
4564                         $this->state = 2;
4565                         if (empty($orientation)) {
4566                                 if (isset($this->CurOrientation)) {
4567                                         $orientation = $this->CurOrientation;
4568                                 } else {
4569                                         $orientation = 'P';
4570                                 }
4571                         }
4572                         if (!empty($format)) {
4573                                 $this->setPageFormat($format, $orientation);
4574                         } else {
4575                                 $this->setPageOrientation($orientation);
4576                         }
4577                         if ($this->rtl) {
4578                                 $this->x = $this->w - $this->rMargin;
4579                         } else {
4580                                 $this->x = $this->lMargin;
4581                         }
4582                         $this->y = $this->tMargin;
4583                         if ($this->newpagegroup){
4584                                 // start a new group
4585                                 $n = sizeof($this->pagegroups) + 1;
4586                                 $alias = "{nb".$n."}";
4587                                 $this->pagegroups[$alias] = 1;
4588                                 $this->currpagegroup = $alias;
4589                                 $this->newpagegroup = false;
4590                         } elseif ($this->currpagegroup) {
4591                                 $this->pagegroups[$this->currpagegroup]++;
4592                         }
4593                 }
4594
4595                 /**
4596                 * Mark end of page.
4597                 * @access protected
4598                 */
4599                 function _endpage() {
4600                         $this->setVisibility("all");
4601                         $this->state = 1;
4602                 }
4603
4604                 /**
4605                 * Begin a new object.
4606                 * @access protected
4607                 */
4608                 function _newobj() {
4609                         $this->n++;
4610                         $this->offsets[$this->n] = strlen($this->buffer);
4611                         $this->_out($this->n.' 0 obj');
4612                 }
4613
4614                 /**
4615                 * Underline text.
4616                 * @param int $x X coordinate
4617                 * @param int $y Y coordinate
4618                 * @param string $txt text to underline
4619                 * @access protected
4620                 */
4621                 function _dounderline($x, $y, $txt) {
4622                         $up = $this->CurrentFont['up'];
4623                         $ut = $this->CurrentFont['ut'];
4624                         $w = $this->GetStringWidth($txt);
4625                         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);
4626                 }
4627
4628                 /**
4629                 * Line through text.
4630                 * @param int $x X coordinate
4631                 * @param int $y Y coordinate
4632                 * @param string $txt text to underline
4633                 * @access protected
4634                 */
4635                 function _dolinethrough($x, $y, $txt) {
4636                         $up = $this->CurrentFont['up'];
4637                         $ut = $this->CurrentFont['ut'];
4638                         $w = $this->GetStringWidth($txt);
4639                         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);
4640                 }
4641
4642                 /**
4643                 * Read a 4-byte integer from file.
4644                 * @param string $f file name.
4645                 * @return 4-byte integer
4646                 * @access protected
4647                 */
4648                 function _freadint($f) {
4649                         $a = unpack('Ni', fread($f,4));
4650                         return $a['i'];
4651                 }
4652
4653                 /**
4654                 * Format a text string for meta information
4655                 * @param string $s string to escape.
4656                 * @return string escaped string.
4657                 * @access protected
4658                 */
4659                 function _textstring($s) {
4660                         if ($this->isunicode) {
4661                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
4662                                         $s = $this->UTF8ToLatin1($s);
4663                                 } else {
4664                                         //Convert string to UTF-16BE
4665                                         $s = $this->UTF8ToUTF16BE($s, true);
4666                                 }
4667                         }
4668                         if ($this->encrypted) {
4669                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4670                         }
4671                         return '('. $this->_escape($s).')';
4672                 }
4673
4674                 /**
4675                 * Format an URI string
4676                 * @param string $s string to escape.
4677                 * @return string escaped string.
4678                 * @access protected
4679                 */
4680                 function _uristring($s) {
4681                         if ($this->encrypted) {
4682                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4683                         }
4684                         return '('.$this->_escape($s).')';
4685                 }
4686
4687                 /**
4688                 * Format a text string
4689                 * @param string $s string to escape.
4690                 * @return string escaped string.
4691                 * @access protected
4692                 */
4693                 function _escapetext($s) {
4694                         if ($this->isunicode) {
4695                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
4696                                         $s = $this->UTF8ToLatin1($s);
4697                                 } else {
4698                                         //Convert string to UTF-16BE and reverse RTL language
4699                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
4700                                 }
4701                         }
4702                         return $this->_escape($s);
4703                 }
4704
4705                 /**
4706                 * Add "\" before "\", "(" and ")"
4707                 * @param string $s string to escape.
4708                 * @return string escaped string.
4709                 * @access protected
4710                 */
4711                 function _escape($s) {
4712                         // the chr(13) substitution fixes the Bugs item #1421290.
4713                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
4714                 }
4715
4716                 /**
4717                 * Output a stream.
4718                 * @param string $s string to output.
4719                 * @access protected
4720                 */
4721                 function _putstream($s) {
4722                         if ($this->encrypted) {
4723                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4724                         }
4725                         $this->_out('stream');
4726                         $this->_out($s);
4727                         $this->_out('endstream');
4728                 }
4729
4730                 /**
4731                 * Output a string to the document.
4732                 * @param string $s string to output.
4733                 * @access protected
4734                 */
4735                 function _out($s) {
4736                         if ($this->state == 2) {
4737                                 if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
4738                                         // puts data before page footer
4739                                         $page = substr($this->pages[$this->page], 0, -$this->footerlen[$this->page]);
4740                                         $footer = substr($this->pages[$this->page], -$this->footerlen[$this->page]);
4741                                         $this->pages[$this->page] = $page." ".$s."\n".$footer;
4742                                 } else {
4743                                         $this->pages[$this->page] .= $s."\n";
4744                                 }
4745                         } else {
4746                                 $this->buffer .= $s."\n";
4747                         }
4748                 }
4749
4750                 /**
4751                 * Adds unicode fonts.<br>
4752                 * Based on PDF Reference 1.3 (section 5)
4753                 * @access protected
4754                 * @author Nicola Asuni
4755                 * @since 1.52.0.TC005 (2005-01-05)
4756                 */
4757                 function _puttruetypeunicode($font) {
4758                         // Type0 Font
4759                         // A composite font composed of other fonts, organized hierarchically
4760                         $this->_newobj();
4761                         $this->_out('<</Type /Font');
4762                         $this->_out('/Subtype /Type0');
4763                         $this->_out('/BaseFont /'.$font['name'].'');
4764                         $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.
4765                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4766                         $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
4767                         $this->_out('>>');
4768                         $this->_out('endobj');
4769                         // CIDFontType2
4770                         // A CIDFont whose glyph descriptions are based on TrueType font technology
4771                         $this->_newobj();
4772                         $this->_out('<</Type /Font');
4773                         $this->_out('/Subtype /CIDFontType2');
4774                         $this->_out('/BaseFont /'.$font['name'].'');
4775                         $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
4776                         $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
4777                         if (isset($font['desc']['MissingWidth'])){
4778                                 $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth
4779                         }
4780                         $w = "";
4781                         foreach ($font['cw'] as $cid => $width) {
4782                                 $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID
4783                         }
4784                         $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont
4785                         $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
4786                         $this->_out('>>');
4787                         $this->_out('endobj');
4788                         // ToUnicode
4789                         // is a stream object that contains the definition of the CMap
4790                         // (PDF Reference 1.3 chap. 5.9)
4791                         $this->_newobj();
4792                         $this->_out('<</Length 345>>');
4793                         $this->_out('stream');
4794                         $this->_out('/CIDInit /ProcSet findresource begin');
4795                         $this->_out('12 dict begin');
4796                         $this->_out('begincmap');
4797                         $this->_out('/CIDSystemInfo');
4798                         $this->_out('<</Registry (Adobe)');
4799                         $this->_out('/Ordering (UCS)');
4800                         $this->_out('/Supplement 0');
4801                         $this->_out('>> def');
4802                         $this->_out('/CMapName /Adobe-Identity-UCS def');
4803                         $this->_out('/CMapType 2 def');
4804                         $this->_out('1 begincodespacerange');
4805                         $this->_out('<0000> <FFFF>');
4806                         $this->_out('endcodespacerange');
4807                         $this->_out('1 beginbfrange');
4808                         $this->_out('<0000> <FFFF> <0000>');
4809                         $this->_out('endbfrange');
4810                         $this->_out('endcmap');
4811                         $this->_out('CMapName currentdict /CMap defineresource pop');
4812                         $this->_out('end');
4813                         $this->_out('end');
4814                         $this->_out('endstream');
4815                         $this->_out('endobj');
4816                         // CIDSystemInfo dictionary
4817                         // A dictionary containing entries that define the character collection of the CIDFont.
4818                         $this->_newobj();
4819                         $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections
4820                         $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry
4821                         $this->_out('/Supplement 0'); // The supplement number of the character collection.
4822                         $this->_out('>>');
4823                         $this->_out('endobj');
4824                         // Font descriptor
4825                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
4826                         $this->_newobj();
4827                         $this->_out('<</Type /FontDescriptor');
4828                         $this->_out('/FontName /'.$font['name']);
4829                         foreach ($font['desc'] as $key => $value) {
4830                                 $this->_out('/'.$key.' '.$value);
4831                         }
4832                         if ($font['file']) {
4833                                 // A stream containing a TrueType font program
4834                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
4835                         }
4836                         $this->_out('>>');
4837                         $this->_out('endobj');
4838                         // Embed CIDToGIDMap
4839                         // A specification of the mapping from CIDs to glyph indices
4840                         $this->_newobj();
4841                         $ctgfile = $this->_getfontpath().strtolower($font['ctg']);
4842                         if (!file_exists($ctgfile)) {
4843                                 $this->Error('Font file not found: '.$ctgfile);
4844                         }
4845                         $size = filesize($ctgfile);
4846                         $this->_out('<</Length '.$size.'');
4847                         if (substr($ctgfile, -2) == '.z') { // check file extension
4848                                 /* Decompresses data encoded using the public-domain
4849                                 zlib/deflate compression method, reproducing the
4850                                 original text or binary data */
4851                                 $this->_out('/Filter /FlateDecode');
4852                         }
4853                         $this->_out('>>');
4854                         $this->_putstream(file_get_contents($ctgfile));
4855                         $this->_out('endobj');
4856                 }
4857
4858                  /**
4859                  * Converts UTF-8 strings to codepoints array.<br>
4860                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
4861                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
4862                  * <pre>
4863                  *        Char. number range  |        UTF-8 octet sequence
4864                  *       (hexadecimal)    |              (binary)
4865                  *    --------------------+-----------------------------------------------
4866                  *    0000 0000-0000 007F | 0xxxxxxx
4867                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
4868                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
4869                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
4870                  *    ---------------------------------------------------------------------
4871                  *
4872                  *   ABFN notation:
4873                  *   ---------------------------------------------------------------------
4874                  *   UTF8-octets = *( UTF8-char )
4875                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
4876                  *   UTF8-1      = %x00-7F
4877                  *   UTF8-2      = %xC2-DF UTF8-tail
4878                  *
4879                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
4880                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
4881                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
4882                  *                 %xF4 %x80-8F 2( UTF8-tail )
4883                  *   UTF8-tail   = %x80-BF
4884                  *   ---------------------------------------------------------------------
4885                  * </pre>
4886                  * @param string $str string to process.
4887                  * @return array containing codepoints (UTF-8 characters values)
4888                  * @access protected
4889                  * @author Nicola Asuni
4890                  * @since 1.53.0.TC005 (2005-01-05)
4891                  */
4892                 function UTF8StringToArray($str) {
4893                         if (!$this->isunicode) {
4894                                 // split string into array of equivalent codes
4895                                 $strarr = array();
4896                                 $strlen = strlen($str);
4897                                 for($i=0; $i < $strlen; $i++) {
4898                                         $strarr[] = ord($str{$i});
4899                                 }
4900                                 return $strarr;
4901                         }
4902                         $unicode = array(); // array containing unicode values
4903                         $bytes  = array(); // array containing single character byte sequences
4904                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
4905                         $str .= ""; // force $str to be a string
4906                         $length = strlen($str);
4907                         for($i = 0; $i < $length; $i++) {
4908                                 $char = ord($str{$i}); // get one string character at time
4909                                 if (count($bytes) == 0) { // get starting octect
4910                                         if ($char <= 0x7F) {
4911                                                 $unicode[] = $char; // use the character "as is" because is ASCII
4912                                                 $numbytes = 1;
4913                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
4914                                                 $bytes[] = ($char - 0xC0) << 0x06;
4915                                                 $numbytes = 2;
4916                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
4917                                                 $bytes[] = ($char - 0xE0) << 0x0C;
4918                                                 $numbytes = 3;
4919                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
4920                                                 $bytes[] = ($char - 0xF0) << 0x12;
4921                                                 $numbytes = 4;
4922                                         } else {
4923                                                 // use replacement character for other invalid sequences
4924                                                 $unicode[] = 0xFFFD;
4925                                                 $bytes = array();
4926                                                 $numbytes = 1;
4927                                         }
4928                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
4929                                         $bytes[] = $char - 0x80;
4930                                         if (count($bytes) == $numbytes) {
4931                                                 // compose UTF-8 bytes to a single unicode value
4932                                                 $char = $bytes[0];
4933                                                 for($j = 1; $j < $numbytes; $j++) {
4934                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
4935                                                 }
4936                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
4937                                                         /* The definition of UTF-8 prohibits encoding character numbers between
4938                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
4939                                                         encoding form (as surrogate pairs) and do not directly represent
4940                                                         characters. */
4941                                                         $unicode[] = 0xFFFD; // use replacement character
4942                                                 } else {
4943                                                         $unicode[] = $char; // add char to array
4944                                                 }
4945                                                 // reset data for next char
4946                                                 $bytes = array();
4947                                                 $numbytes = 1;
4948                                         }
4949                                 } else {
4950                                         // use replacement character for other invalid sequences
4951                                         $unicode[] = 0xFFFD;
4952                                         $bytes = array();
4953                                         $numbytes = 1;
4954                                 }
4955                         }
4956                         return $unicode;
4957                 }
4958
4959                 /**
4960                  * Converts UTF-8 strings to UTF16-BE.<br>
4961                  * @param string $str string to process.
4962                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
4963                  * @return string
4964                  * @access protected
4965                  * @author Nicola Asuni
4966                  * @since 1.53.0.TC005 (2005-01-05)
4967                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
4968                  */
4969                 function UTF8ToUTF16BE($str, $setbom=true) {
4970                         if (!$this->isunicode) {
4971                                 return $str; // string is not in unicode
4972                         }
4973                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
4974                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
4975                 }
4976
4977                 /**
4978                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
4979                  * @param string $str string to process.
4980                  * @return string
4981                  * @author Andrew Whitehead, Nicola Asuni
4982                  * @access protected
4983                  * @since 3.2.000 (2008-06-23)
4984                  */
4985                 function UTF8ToLatin1($str) {
4986                         if (!$this->isunicode) {
4987                                 return $str; // string is not in unicode
4988                         }
4989                         $outstr = ""; // string to be returned
4990                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
4991                         foreach ($unicode as $char) {
4992                                 if ($char == 0xFFFD) {
4993                                         // skip
4994                                 } elseif ($char == 0x2022) {
4995                                         // fix for middot
4996                                         $outstr .= chr(183);
4997                                 } elseif ($char < 256) {
4998                                         $outstr .= chr($char);
4999                                 } else {
5000                                         $outstr .= '?';
5001                                 }
5002                         }
5003                         return $outstr;
5004                 }
5005
5006                 /**
5007                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
5008                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
5009                  * <pre>
5010                  *   Encoding UTF-16:
5011                  *
5012                  *   Encoding of a single character from an ISO 10646 character value to
5013                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
5014                  *    than 0x10FFFF.
5015                  *
5016                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
5017                  *       terminate.
5018                  *
5019                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
5020                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
5021                  *       represented in 20 bits.
5022                  *
5023                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
5024                  *       0xDC00, respectively. These integers each have 10 bits free to
5025                  *       encode the character value, for a total of 20 bits.
5026                  *
5027                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
5028                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
5029                  *       bits of W2. Terminate.
5030                  *
5031                  *    Graphically, steps 2 through 4 look like:
5032                  *    U' = yyyyyyyyyyxxxxxxxxxx
5033                  *    W1 = 110110yyyyyyyyyy
5034                  *    W2 = 110111xxxxxxxxxx
5035                  * </pre>
5036                  * @param array $unicode array containing UTF-8 unicode values
5037                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5038                  * @return string
5039                  * @access protected
5040                  * @author Nicola Asuni
5041                  * @since 2.1.000 (2008-01-08)
5042                  * @see UTF8ToUTF16BE()
5043                  */
5044                 function arrUTF8ToUTF16BE($unicode, $setbom=true) {
5045                         $outstr = ""; // string to be returned
5046                         if ($setbom) {
5047                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
5048                         }
5049                         foreach($unicode as $char) {
5050                                 if ($char == 0xFFFD) {
5051                                         $outstr .= "\xFF\xFD"; // replacement character
5052                                 } elseif ($char < 0x10000) {
5053                                         $outstr .= chr($char >> 0x08);
5054                                         $outstr .= chr($char & 0xFF);
5055                                 } else {
5056                                         $char -= 0x10000;
5057                                         $w1 = 0xD800 | ($char >> 0x10);
5058                                         $w2 = 0xDC00 | ($char & 0x3FF);
5059                                         $outstr .= chr($w1 >> 0x08);
5060                                         $outstr .= chr($w1 & 0xFF);
5061                                         $outstr .= chr($w2 >> 0x08);
5062                                         $outstr .= chr($w2 & 0xFF);
5063                                 }
5064                         }
5065                         return $outstr;
5066                 }
5067                 // ====================================================
5068
5069                 /**
5070                  * Set header font.
5071                  * @param array $font font
5072                  * @since 1.1
5073                  */
5074                 function setHeaderFont($font) {
5075                         $this->header_font = $font;
5076                 }
5077
5078                 /**
5079                  * Get header font.
5080                  * @return array()
5081                  * @since 4.0.012 (2008-07-24)
5082                  */
5083                 function getHeaderFont() {
5084                         return $this->header_font;
5085                 }
5086
5087                 /**
5088                  * Set footer font.
5089                  * @param array $font font
5090                  * @since 1.1
5091                  */
5092                 function setFooterFont($font) {
5093                         $this->footer_font = $font;
5094                 }
5095
5096                 /**
5097                  * Get Footer font.
5098                  * @return array()
5099                  * @since 4.0.012 (2008-07-24)
5100                  */
5101                 function getFooterFont() {
5102                         return $this->footer_font;
5103                 }
5104
5105                 /**
5106                  * Set language array.
5107                  * @param array $language
5108                  * @since 1.1
5109                  */
5110                 function setLanguageArray($language) {
5111                         $this->l = $language;
5112                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
5113                 }
5114
5115                 /**
5116                  * Returns the PDF data.
5117                  */
5118                 function getPDFData() {
5119                         if ($this->state < 3) {
5120                                 $this->Close();
5121                         }
5122                         return $this->buffer;
5123                 }
5124
5125                 /**
5126                  * Sets font style.
5127                  * @param string $tag tag name in lowercase. Supported tags are:<ul>
5128                  * <li>b : bold text</li>
5129                  * <li>i : italic</li>
5130                  * <li>u : underlined</li>
5131                  * <li>lt : line-through</li></ul>
5132                  * @param boolean $enable
5133                  * @access protected
5134                  */
5135                 function setStyle($tag, $enable) {
5136                         //Modify style and select corresponding font
5137                         $this->$tag += ($enable ? 1 : -1);
5138                         $style = '';
5139                         foreach(array('b', 'i', 'u', 'd') as $s) {
5140                                 if ($this->$s > 0) {
5141                                         $style .= $s;
5142                                 }
5143                         }
5144                         $this->SetFont('', $style);
5145                 }
5146
5147                 /**
5148                  * Output anchor link.
5149                  * @param string $url link URL
5150                  * @param string $name link name
5151                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
5152                  * @param boolean $firstline if true prints only the first line and return the remaining string.
5153                  * @return the number of cells used or the remaining text if $firstline = true;
5154                  * @access public
5155                  */
5156                 function addHtmlLink($url, $name, $fill=0, $firstline=false) {
5157                         $prevcolor = $this->fgcolor;
5158                         $this->SetTextColor(0, 0, 255);
5159                         $this->setStyle('u', true);
5160                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
5161                         $this->setStyle('u', false);
5162                         $this->SetTextColorArray($prevcolor);
5163                         return $ret;
5164                 }
5165
5166                 /**
5167                  * 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).
5168                  * @param string $color html color
5169                  * @return array
5170                  * @access protected
5171                  */
5172                 function convertHTMLColorToDec($color="#000000") {
5173                         global $webcolor;
5174                         // set default color to be returned in case of error
5175                         $returncolor = array ('R' => 0, 'G' => 0, 'B' => 0);
5176                         if (empty($color)) {
5177                                 return $returncolor;
5178                         }
5179                         if (substr($color, 0, 1) != "#") {
5180                                 // decode color name
5181                                 if (isset($webcolor[strtolower($color)])) {
5182                                         $color_code = $webcolor[strtolower($color)];
5183                                 } else {
5184                                         return $returncolor;
5185                                 }
5186                         } else {
5187                                 $color_code = substr($color, 1);
5188                         }
5189                         switch (strlen($color_code)) {
5190                                 case 3: {
5191                                         // three-digit hexadecimal representation
5192                                         $r = substr($color_code, 0, 1);
5193                                         $g = substr($color_code, 1, 1);
5194                                         $b = substr($color_code, 2, 1);
5195                                         $returncolor['R'] = hexdec($r.$r);
5196                                         $returncolor['G'] = hexdec($g.$g);
5197                                         $returncolor['B'] = hexdec($b.$b);
5198                                         break;
5199                                 }
5200                                 case 6: {
5201                                         // six-digit hexadecimal representation
5202                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
5203                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
5204                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
5205                                         break;
5206                                 }
5207                         }
5208                         return $returncolor;
5209                 }
5210
5211                 /**
5212                  * Converts pixels to Units.
5213                  * @param int $px pixels
5214                  * @return float millimeters
5215                  * @access public
5216                  */
5217                 function pixelsToUnits($px){
5218                         return $px / $this->k;
5219                 }
5220
5221                 /**
5222                  * Reverse function for htmlentities.
5223                  * Convert entities in UTF-8.
5224                  * @param $text_to_convert Text to convert.
5225                  * @return string converted
5226                  */
5227                 function unhtmlentities($text_to_convert) {
5228                         if (!$this->isunicode) {
5229                                 return html_entity_decode($text_to_convert);
5230                         }
5231                         return html_entity_decode_php4($text_to_convert);
5232                 }
5233
5234                 // ENCRYPTION METHODS ----------------------------------
5235                 // SINCE 2.0.000 (2008-01-02)
5236                 /**
5237                 * Compute encryption key depending on object number where the encrypted data is stored
5238                 * @param int $n object number
5239                 * @since 2.0.000 (2008-01-02)
5240                 */
5241                 function _objectkey($n) {
5242                         return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10);
5243                 }
5244
5245                 /**
5246                  * Put encryption on PDF document.
5247                  * @since 2.0.000 (2008-01-02)
5248                  */
5249                 function _putencryption() {
5250                         $this->_out('/Filter /Standard');
5251                         $this->_out('/V 1');
5252                         $this->_out('/R 2');
5253                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
5254                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
5255                         $this->_out('/P '.$this->Pvalue);
5256                 }
5257
5258                 /**
5259                 * Returns the input text exrypted using RC4 algorithm and the specified key.
5260                 * RC4 is the standard encryption algorithm used in PDF format
5261                 * @param string $key encryption key
5262                 * @param String $text input text to be encrypted
5263                 * @return String encrypted text
5264                 * @since 2.0.000 (2008-01-02)
5265                 * @author Klemen Vodopivec
5266                 */
5267                 function _RC4($key, $text) {
5268                         if ($this->last_rc4_key != $key) {
5269                                 $k = str_repeat($key, 256/strlen($key)+1);
5270                                 $rc4 = range(0,255);
5271                                 $j = 0;
5272                                 for ($i=0; $i < 256; $i++) {
5273                                         $t = $rc4[$i];
5274                                         $j = ($j + $t + ord($k{$i})) % 256;
5275                                         $rc4[$i] = $rc4[$j];
5276                                         $rc4[$j] = $t;
5277                                 }
5278                                 $this->last_rc4_key = $key;
5279                                 $this->last_rc4_key_c = $rc4;
5280                         } else {
5281                                 $rc4 = $this->last_rc4_key_c;
5282                         }
5283                         $len = strlen($text);
5284                         $a = 0;
5285                         $b = 0;
5286                         $out = '';
5287                         for ($i=0; $i < $len; $i++) {
5288                                 $a = ($a + 1) % 256;
5289                                 $t = $rc4[$a];
5290                                 $b = ($b + $t) % 256;
5291                                 $rc4[$a] = $rc4[$b];
5292                                 $rc4[$b] = $t;
5293                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
5294                                 $out .= chr(ord($text{$i}) ^ $k);
5295                         }
5296                         return $out;
5297                 }
5298
5299                 /**
5300                 * Encrypts a string using MD5 and returns it's value as a binary string.
5301                 * @param string $str input string
5302                 * @return String MD5 encrypted binary string
5303                 * @since 2.0.000 (2008-01-02)
5304                 * @author Klemen Vodopivec
5305                 */
5306                 function _md5_16($str) {
5307                         return pack('H*',md5($str));
5308                 }
5309
5310                 /**
5311                 * Compute O value (used for RC4 encryption)
5312                 * @param String $user_pass user password
5313                 * @param String $owner_pass user password
5314                 * @return String O value
5315                 * @since 2.0.000 (2008-01-02)
5316                 * @author Klemen Vodopivec
5317                 */
5318                 function _Ovalue($user_pass, $owner_pass) {
5319                         $tmp = $this->_md5_16($owner_pass);
5320                         $owner_RC4_key = substr($tmp,0,5);
5321                         return $this->_RC4($owner_RC4_key, $user_pass);
5322                 }
5323
5324                 /**
5325                 * Compute U value (used for RC4 encryption)
5326                 * @return String U value
5327                 * @since 2.0.000 (2008-01-02)
5328                 * @author Klemen Vodopivec
5329                 */
5330                 function _Uvalue() {
5331                         return $this->_RC4($this->encryption_key, $this->padding);
5332                 }
5333
5334                 /**
5335                 * Compute encryption key
5336                 * @param String $user_pass user password
5337                 * @param String $owner_pass user password
5338                 * @param String $protection protection type
5339                 * @since 2.0.000 (2008-01-02)
5340                 * @author Klemen Vodopivec
5341                 */
5342                 function _generateencryptionkey($user_pass, $owner_pass, $protection) {
5343                         // Pad passwords
5344                         $user_pass = substr($user_pass.$this->padding,0,32);
5345                         $owner_pass = substr($owner_pass.$this->padding,0,32);
5346                         // Compute O value
5347                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
5348                         // Compute encyption key
5349                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
5350                         $this->encryption_key = substr($tmp,0,5);
5351                         // Compute U value
5352                         $this->Uvalue = $this->_Uvalue();
5353                         // Compute P value
5354                         $this->Pvalue = -(($protection^255)+1);
5355                 }
5356
5357                 /**
5358                 * Set document protection
5359                 * The permission array is composed of values taken from the following ones:
5360                 * - copy: copy text and images to the clipboard
5361                 * - print: print the document
5362                 * - modify: modify it (except for annotations and forms)
5363                 * - annot-forms: add annotations and forms
5364                 * Remark: the protection against modification is for people who have the full Acrobat product.
5365                 * 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.
5366                 * 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.
5367                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
5368                 * @param String $user_pass user password. Empty by default.
5369                 * @param String $owner_pass owner password. If not specified, a random value is used.
5370                 * @since 2.0.000 (2008-01-02)
5371                 * @author Klemen Vodopivec
5372                 */
5373                 function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
5374                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
5375                         $protection = 192;
5376                         foreach($permissions as $permission) {
5377                                 if (!isset($options[$permission])) {
5378                                         $this->Error('Incorrect permission: '.$permission);
5379                                 }
5380                                 $protection += $options[$permission];
5381                         }
5382                         if ($owner_pass === null) {
5383                                 $owner_pass = uniqid(rand());
5384                         }
5385                         $this->encrypted = true;
5386                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
5387                 }
5388
5389                 // END OF ENCRYPTION FUNCTIONS -------------------------
5390
5391                 // START TRANSFORMATIONS SECTION -----------------------
5392                 // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni
5393
5394                 /**
5395                 * Starts a 2D tranformation saving current graphic state.
5396                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
5397                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5398                 * @since 2.1.000 (2008-01-07)
5399                 * @see StartTransform(), StopTransform()
5400                 */
5401                 function StartTransform() {
5402                         $this->_out('q');
5403                 }
5404
5405                 /**
5406                 * Stops a 2D tranformation restoring previous graphic state.
5407                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
5408                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5409                 * @since 2.1.000 (2008-01-07)
5410                 * @see StartTransform(), StopTransform()
5411                 */
5412                 function StopTransform() {
5413                         $this->_out('Q');
5414                 }
5415                 /**
5416                 * Horizontal Scaling.
5417                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5418                 * @param int $x abscissa of the scaling center. Default is current x position
5419                 * @param int $y ordinate of the scaling center. Default is current y position
5420                 * @since 2.1.000 (2008-01-07)
5421                 * @see StartTransform(), StopTransform()
5422                 */
5423                 function ScaleX($s_x, $x='', $y=''){
5424                         $this->Scale($s_x, 100, $x, $y);
5425                 }
5426
5427                 /**
5428                 * Vertical Scaling.
5429                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5430                 * @param int $x abscissa of the scaling center. Default is current x position
5431                 * @param int $y ordinate of the scaling center. Default is current y position
5432                 * @since 2.1.000 (2008-01-07)
5433                 * @see StartTransform(), StopTransform()
5434                 */
5435                 function ScaleY($s_y, $x='', $y=''){
5436                         $this->Scale(100, $s_y, $x, $y);
5437                 }
5438
5439                 /**
5440                 * Vertical and horizontal proportional Scaling.
5441                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
5442                 * @param int $x abscissa of the scaling center. Default is current x position
5443                 * @param int $y ordinate of the scaling center. Default is current y position
5444                 * @since 2.1.000 (2008-01-07)
5445                 * @see StartTransform(), StopTransform()
5446                 */
5447                 function ScaleXY($s, $x='', $y=''){
5448                         $this->Scale($s, $s, $x, $y);
5449                 }
5450
5451                 /**
5452                 * Vertical and horizontal non-proportional Scaling.
5453                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5454                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5455                 * @param int $x abscissa of the scaling center. Default is current x position
5456                 * @param int $y ordinate of the scaling center. Default is current y position
5457                 * @since 2.1.000 (2008-01-07)
5458                 * @see StartTransform(), StopTransform()
5459                 */
5460                 function Scale($s_x, $s_y, $x='', $y=''){
5461                         if ($x === '') {
5462                                 $x=$this->x;
5463                         }
5464                         if ($y === '') {
5465                                 $y=$this->y;
5466                         }
5467                         if ($this->rtl) {
5468                                 $x = $this->w - $x;
5469                         }
5470                         if (($s_x == 0) OR ($s_y == 0)) {
5471                                 $this->Error('Please use values unequal to zero for Scaling');
5472                         }
5473                         $y = ($this->h - $y) * $this->k;
5474                         $x *= $this->k;
5475                         //calculate elements of transformation matrix
5476                         $s_x /= 100;
5477                         $s_y /= 100;
5478                         $tm[0] = $s_x;
5479                         $tm[1] = 0;
5480                         $tm[2] = 0;
5481                         $tm[3] = $s_y;
5482                         $tm[4] = $x * (1 - $s_x);
5483                         $tm[5] = $y * (1 - $s_y);
5484                         //scale the coordinate system
5485                         $this->Transform($tm);
5486                 }
5487
5488                 /**
5489                 * Horizontal Mirroring.
5490                 * @param int $x abscissa of the point. Default is current x position
5491                 * @since 2.1.000 (2008-01-07)
5492                 * @see StartTransform(), StopTransform()
5493                 */
5494                 function MirrorH($x=''){
5495                         $this->Scale(-100, 100, $x);
5496                 }
5497
5498                 /**
5499                 * Verical Mirroring.
5500                 * @param int $y ordinate of the point. Default is current y position
5501                 * @since 2.1.000 (2008-01-07)
5502                 * @see StartTransform(), StopTransform()
5503                 */
5504                 function MirrorV($y=''){
5505                         $this->Scale(100, -100, '', $y);
5506                 }
5507
5508                 /**
5509                 * Point reflection mirroring.
5510                 * @param int $x abscissa of the point. Default is current x position
5511                 * @param int $y ordinate of the point. Default is current y position
5512                 * @since 2.1.000 (2008-01-07)
5513                 * @see StartTransform(), StopTransform()
5514                 */
5515                 function MirrorP($x='',$y=''){
5516                         $this->Scale(-100, -100, $x, $y);
5517                 }
5518
5519                 /**
5520                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
5521                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
5522                 * @param int $x abscissa of the point. Default is current x position
5523                 * @param int $y ordinate of the point. Default is current y position
5524                 * @since 2.1.000 (2008-01-07)
5525                 * @see StartTransform(), StopTransform()
5526                 */
5527                 function MirrorL($angle=0, $x='',$y=''){
5528                         $this->Scale(-100, 100, $x, $y);
5529                         $this->Rotate(-2*($angle-90), $x, $y);
5530                 }
5531
5532                 /**
5533                 * Translate graphic object horizontally.
5534                 * @param int $t_x movement to the right (or left for RTL)
5535                 * @since 2.1.000 (2008-01-07)
5536                 * @see StartTransform(), StopTransform()
5537                 */
5538                 function TranslateX($t_x){
5539                         $this->Translate($t_x, 0);
5540                 }
5541
5542                 /**
5543                 * Translate graphic object vertically.
5544                 * @param int $t_y movement to the bottom
5545                 * @since 2.1.000 (2008-01-07)
5546                 * @see StartTransform(), StopTransform()
5547                 */
5548                 function TranslateY($t_y){
5549                         $this->Translate(0, $t_y);
5550                 }
5551
5552                 /**
5553                 * Translate graphic object horizontally and vertically.
5554                 * @param int $t_x movement to the right
5555                 * @param int $t_y movement to the bottom
5556                 * @since 2.1.000 (2008-01-07)
5557                 * @see StartTransform(), StopTransform()
5558                 */
5559                 function Translate($t_x, $t_y){
5560                         if ($this->rtl) {
5561                                 $t_x = -$t_x;
5562                         }
5563                         //calculate elements of transformation matrix
5564                         $tm[0] = 1;
5565                         $tm[1] = 0;
5566                         $tm[2] = 0;
5567                         $tm[3] = 1;
5568                         $tm[4] = $t_x * $this->k;
5569                         $tm[5] = -$t_y * $this->k;
5570                         //translate the coordinate system
5571                         $this->Transform($tm);
5572                 }
5573
5574                 /**
5575                 * Rotate object.
5576                 * @param float $angle angle in degrees for counter-clockwise rotation
5577                 * @param int $x abscissa of the rotation center. Default is current x position
5578                 * @param int $y ordinate of the rotation center. Default is current y position
5579                 * @since 2.1.000 (2008-01-07)
5580                 * @see StartTransform(), StopTransform()
5581                 */
5582                 function Rotate($angle, $x='', $y=''){
5583                         if ($x === '') {
5584                                 $x=$this->x;
5585                         }
5586                         if ($y === '') {
5587                                 $y=$this->y;
5588                         }
5589                         if ($this->rtl) {
5590                                 $x = $this->w - $x;
5591                                 $angle = -$angle;
5592                         }
5593                         $y = ($this->h - $y) * $this->k;
5594                         $x *= $this->k;
5595                         //calculate elements of transformation matrix
5596                         $tm[0] = cos(deg2rad($angle));
5597                         $tm[1] = sin(deg2rad($angle));
5598                         $tm[2] = -$tm[1];
5599                         $tm[3] = $tm[0];
5600                         $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
5601                         $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
5602                         //rotate the coordinate system around ($x,$y)
5603                         $this->Transform($tm);
5604                 }
5605
5606                 /**
5607                 * Skew horizontally.
5608                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
5609                 * @param int $x abscissa of the skewing center. default is current x position
5610                 * @param int $y ordinate of the skewing center. default is current y position
5611                 * @since 2.1.000 (2008-01-07)
5612                 * @see StartTransform(), StopTransform()
5613                 */
5614                 function SkewX($angle_x, $x='', $y=''){
5615                         $this->Skew($angle_x, 0, $x, $y);
5616                 }
5617
5618                 /**
5619                 * Skew vertically.
5620                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
5621                 * @param int $x abscissa of the skewing center. default is current x position
5622                 * @param int $y ordinate of the skewing center. default is current y position
5623                 * @since 2.1.000 (2008-01-07)
5624                 * @see StartTransform(), StopTransform()
5625                 */
5626                 function SkewY($angle_y, $x='', $y=''){
5627                         $this->Skew(0, $angle_y, $x, $y);
5628                 }
5629
5630                 /**
5631                 * Skew.
5632                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
5633                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
5634                 * @param int $x abscissa of the skewing center. default is current x position
5635                 * @param int $y ordinate of the skewing center. default is current y position
5636                 * @since 2.1.000 (2008-01-07)
5637                 * @see StartTransform(), StopTransform()
5638                 */
5639                 function Skew($angle_x, $angle_y, $x='', $y=''){
5640                         if ($x === '') {
5641                                 $x = $this->x;
5642                         }
5643                         if ($y === '') {
5644                                 $y = $this->y;
5645                         }
5646                         if ($this->rtl) {
5647                                 $x = $this->w - $x;
5648                                 $angle_x = -$angle_x;
5649                         }
5650                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
5651                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
5652                         }
5653                         $x *= $this->k;
5654                         $y = ($this->h - $y) * $this->k;
5655                         //calculate elements of transformation matrix
5656                         $tm[0] = 1;
5657                         $tm[1] = tan(deg2rad($angle_y));
5658                         $tm[2] = tan(deg2rad($angle_x));
5659                         $tm[3] = 1;
5660                         $tm[4] = -$tm[2] * $y;
5661                         $tm[5] = -$tm[1] * $x;
5662                         //skew the coordinate system
5663                         $this->Transform($tm);
5664                 }
5665
5666                 /**
5667                 * Apply graphic transformations.
5668                 * @since 2.1.000 (2008-01-07)
5669                 * @see StartTransform(), StopTransform()
5670                 */
5671                 function Transform($tm){
5672                         $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
5673                 }
5674
5675                 // END TRANSFORMATIONS SECTION -------------------------
5676
5677
5678                 // START GRAPHIC FUNCTIONS SECTION ---------------------
5679                 // The following section is based on the code provided by David Hernandez Sanz
5680
5681                 /**
5682                 * 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.
5683                 * @param float $width The width.
5684                 * @since 1.0
5685                 * @see Line(), Rect(), Cell(), MultiCell()
5686                 */
5687                 function SetLineWidth($width) {
5688                         //Set line width
5689                         $this->LineWidth = $width;
5690                         $this->linestyleWidth = sprintf('%.2f w', ($width * $this->k));
5691                         $this->_out($this->linestyleWidth);
5692                 }
5693
5694                 /**
5695                 * Returns the current the line width.
5696                 * @return int Line width
5697                 * @since 2.1.000 (2008-01-07)
5698                 * @see Line(), SetLineWidth()
5699                 */
5700                 function GetLineWidth() {
5701                         return $this->LineWidth;
5702                 }
5703
5704                 /**
5705                 * Set line style.
5706                 * @param array $style Line style. Array with keys among the following:
5707                 * <ul>
5708                 *        <li>width (float): Width of the line in user units.</li>
5709                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
5710                 * butt, round, square. The difference between "square" and "butt" is that
5711                 * "square" projects a flat end past the end of the line.</li>
5712                 *        <li>join (string): Type of join. Possible values are: miter, round,
5713                 * bevel.</li>
5714                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
5715                 * series of length values, which are the lengths of the on and off dashes.
5716                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
5717                 * 1 off, 2 on, 1 off, ...</li>
5718                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
5719                 * the point at which the pattern starts.</li>
5720                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
5721                 * </ul>
5722                 * @access public
5723                 * @since 2.1.000 (2008-01-08)
5724                 */
5725                 function SetLineStyle($style) {
5726                         extract($style);
5727                         if (isset($width)) {
5728                                 $width_prev = $this->LineWidth;
5729                                 $this->SetLineWidth($width);
5730                                 $this->LineWidth = $width_prev;
5731                         }
5732                         if (isset($cap)) {
5733                                 $ca = array("butt" => 0, "round"=> 1, "square" => 2);
5734                                 if (isset($ca[$cap])) {
5735                                         $this->linestyleCap = $ca[$cap]." J";
5736                                         $this->_out($this->linestyleCap);
5737                                 }
5738                         }
5739                         if (isset($join)) {
5740                                 $ja = array("miter" => 0, "round" => 1, "bevel" => 2);
5741                                 if (isset($ja[$join])) {
5742                                         $this->linestyleJoin = $ja[$join]." j";
5743                                         $this->_out($this->linestyleJoin);
5744                                 }
5745                         }
5746                         if (isset($dash)) {
5747                                 $dash_string = "";
5748                                 if ($dash) {
5749                                         if (ereg("^.+,", $dash)) {
5750                                                 $tab = explode(",", $dash);
5751                                         } else {
5752                                                 $tab = array($dash);
5753                                         }
5754                                         $dash_string = "";
5755                                         foreach ($tab as $i => $v) {
5756                                                 if ($i) {
5757                                                         $dash_string .= " ";
5758                                                 }
5759                                                 $dash_string .= sprintf("%.2f", $v);
5760                                         }
5761                                 }
5762                                 if (!isset($phase) OR !$dash) {
5763                                         $phase = 0;
5764                                 }
5765                                 $this->linestyleDash = sprintf("[%s] %.2f d", $dash_string, $phase);
5766                                 $this->_out($this->linestyleDash);
5767                         }
5768                         if (isset($color)) {
5769                                 $this->SetDrawColorArray($color);
5770                         }
5771                 }
5772
5773                 /*
5774                 * Set a draw point.
5775                 * @param float $x Abscissa of point.
5776                 * @param float $y Ordinate of point.
5777                 * @access protected
5778                 * @since 2.1.000 (2008-01-08)
5779                 */
5780                 function _outPoint($x, $y) {
5781                         if ($this->rtl) {
5782                                 $x = $this->w - $x;
5783                         }
5784                         $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k));
5785                 }
5786
5787                 /*
5788                 * Draws a line from last draw point.
5789                 * @param float $x Abscissa of end point.
5790                 * @param float $y Ordinate of end point.
5791                 * @access protected
5792                 * @since 2.1.000 (2008-01-08)
5793                 */
5794                 function _outLine($x, $y) {
5795                         if ($this->rtl) {
5796                                 $x = $this->w - $x;
5797                         }
5798                         $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k));
5799                 }
5800
5801                 /**
5802                 * Draws a rectangle.
5803                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
5804                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
5805                 * @param float $w Width.
5806                 * @param float $h Height.
5807                 * @param string $op options
5808                 * @access protected
5809                 * @since 2.1.000 (2008-01-08)
5810                 */
5811                 function _outRect($x, $y, $w, $h, $op) {
5812                         if ($this->rtl) {
5813                                 $x = $this->w - $x - $w;
5814                         }
5815                         $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
5816                 }
5817
5818                 /*
5819                 * Draws a Bezier curve from last draw point.
5820                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
5821                 * @param float $x1 Abscissa of control point 1.
5822                 * @param float $y1 Ordinate of control point 1.
5823                 * @param float $x2 Abscissa of control point 2.
5824                 * @param float $y2 Ordinate of control point 2.
5825                 * @param float $x3 Abscissa of end point.
5826                 * @param float $y3 Ordinate of end point.
5827                 * @access protected
5828                 * @since 2.1.000 (2008-01-08)
5829                 */
5830                 function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
5831                         if ($this->rtl) {
5832                                 $x1 = $this->w - $x1;
5833                                 $x2 = $this->w - $x2;
5834                                 $x3 = $this->w - $x3;
5835                         }
5836                         $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));
5837                 }
5838
5839                 /**
5840                 * Draws a line between two points.
5841                 * @param float $x1 Abscissa of first point.
5842                 * @param float $y1 Ordinate of first point.
5843                 * @param float $x2 Abscissa of second point.
5844                 * @param float $y2 Ordinate of second point.
5845                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5846                 * @access public
5847                 * @since 1.0
5848                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
5849                 */
5850                 function Line($x1, $y1, $x2, $y2, $style=array()) {
5851                         if ($style) {
5852                                 $this->SetLineStyle($style);
5853                         }
5854                         $this->_outPoint($x1, $y1);
5855                         $this->_outLine($x2, $y2);
5856                         $this->_out(" S");
5857                 }
5858
5859                 /**
5860                 * Draws a rectangle.
5861                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
5862                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
5863                 * @param float $w Width.
5864                 * @param float $h Height.
5865                 * @param string $style Style of rendering. Possible values are:
5866                 * <ul>
5867                 *        <li>D or empty string: Draw (default).</li>
5868                 *        <li>F: Fill.</li>
5869                 *        <li>DF or FD: Draw and fill.</li>
5870                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
5871                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
5872                 * </ul>
5873                 * @param array $border_style Border style of rectangle. Array with keys among the following:
5874                 * <ul>
5875                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
5876                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
5877                 * </ul>
5878                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
5879                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5880                 * @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).
5881                 * @access public
5882                 * @since 1.0
5883                 * @see SetLineStyle()
5884                 */
5885                 function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
5886                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
5887                                 $this->SetFillColorArray($fill_color);
5888                         }
5889                         switch ($style) {
5890                                 case "F": {
5891                                         $op = 'f';
5892                                         $border_style = array();
5893                                         $this->_outRect($x, $y, $w, $h, $op);
5894                                         break;
5895                                 }
5896                                 case "DF":
5897                                 case "FD": {
5898                                         if ((!$border_style) OR (isset($border_style["all"]))) {
5899                                                 $op = 'B';
5900                                                 if (isset($border_style["all"])) {
5901                                                         $this->SetLineStyle($border_style["all"]);
5902                                                         $border_style = array();
5903                                                 }
5904                                         } else {
5905                                                 $op = 'f';
5906                                         }
5907                                         $this->_outRect($x, $y, $w, $h, $op);
5908                                         break;
5909                                 }
5910                                 case "CNZ": {
5911                                         $op = "W n";
5912                                         break;
5913                                 }
5914                                 case "CEO": {
5915                                         $op = "W* n";
5916                                         break;
5917                                 }
5918                                 default: {
5919                                         $op = 'S';
5920                                         if ((!$border_style) OR (isset($border_style["all"]))) {
5921                                                 if (isset($border_style["all"]) AND $border_style["all"]) {
5922                                                         $this->SetLineStyle($border_style["all"]);
5923                                                         $border_style = array();
5924                                                 }
5925                                                 $this->_outRect($x, $y, $w, $h, $op);
5926                                         }
5927                                         break;
5928                                 }
5929                         }
5930                         if ($border_style) {
5931                                 $border_style2 = array();
5932                                 foreach ($border_style as $line => $value) {
5933                                         $lenght = strlen($line);
5934                                         for ($i = 0; $i < $lenght; $i++) {
5935                                                 $border_style2[$line[$i]] = $value;
5936                                         }
5937                                 }
5938                                 $border_style = $border_style2;
5939                                 if (isset($border_style["L"]) AND $border_style["L"]) {
5940                                         $this->Line($x, $y, $x, $y + $h, $border_style["L"]);
5941                                 }
5942                                 if (isset($border_style["T"]) AND $border_style["T"]) {
5943                                         $this->Line($x, $y, $x + $w, $y, $border_style["T"]);
5944                                 }
5945                                 if (isset($border_style["R"]) AND $border_style["R"]) {
5946                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]);
5947                                 }
5948                                 if (isset($border_style["B"]) AND $border_style["B"]) {
5949                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]);
5950                                 }
5951                         }
5952                 }
5953
5954
5955                 /**
5956                 * Draws a Bezier curve.
5957                 * The Bezier curve is a tangent to the line between the control points at
5958                 * either end of the curve.
5959                 * @param float $x0 Abscissa of start point.
5960                 * @param float $y0 Ordinate of start point.
5961                 * @param float $x1 Abscissa of control point 1.
5962                 * @param float $y1 Ordinate of control point 1.
5963                 * @param float $x2 Abscissa of control point 2.
5964                 * @param float $y2 Ordinate of control point 2.
5965                 * @param float $x3 Abscissa of end point.
5966                 * @param float $y3 Ordinate of end point.
5967                 * @param string $style Style of rendering. Possible values are:
5968                 * <ul>
5969                 *        <li>D or empty string: Draw (default).</li>
5970                 *        <li>F: Fill.</li>
5971                 *        <li>DF or FD: Draw and fill.</li>
5972                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
5973                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
5974                 * </ul>
5975                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
5976                 * @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).
5977                 * @access public
5978                 * @see SetLineStyle()
5979                 * @since 2.1.000 (2008-01-08)
5980                 */
5981                 function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style="", $line_style=array(), $fill_color=array()) {
5982                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
5983                                 $this->SetFillColorArray($fill_color);
5984                         }
5985                         switch ($style) {
5986                                 case "F": {
5987                                         $op = "f";
5988                                         $line_style = array();
5989                                         break;
5990                                 }
5991                                 case "FD":
5992                                 case "DF": {
5993                                         $op = "B";
5994                                         break;
5995                                 }
5996                                 case "CNZ": {
5997                                         $op = "W n";
5998                                         break;
5999                                 }
6000                                 case "CEO": {
6001                                         $op = "W* n";
6002                                         break;
6003                                 }
6004                                 default: {
6005                                         $op = "S";
6006                                         break;
6007                                 }
6008                         }
6009                         if ($line_style) {
6010                                 $this->SetLineStyle($line_style);
6011                         }
6012                         $this->_outPoint($x0, $y0);
6013                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6014                         $this->_out($op);
6015                 }
6016
6017                 /**
6018                 * Draws a poly-Bezier curve.
6019                 * Each Bezier curve segment is a tangent to the line between the control points at
6020                 * either end of the curve.
6021                 * @param float $x0 Abscissa of start point.
6022                 * @param float $y0 Ordinate of start point.
6023                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
6024                 * @param string $style Style of rendering. Possible values are:
6025                 * <ul>
6026                 *        <li>D or empty string: Draw (default).</li>
6027                 *        <li>F: Fill.</li>
6028                 *        <li>DF or FD: Draw and fill.</li>
6029                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6030                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6031                 * </ul>
6032                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6033                 * @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).
6034                 * @access public
6035                 * @see SetLineStyle()
6036                 * @since 3.0008 (2008-05-12)
6037                 */
6038                 function Polycurve($x0, $y0, $segments, $style="", $line_style=array(), $fill_color=array()) {
6039                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6040                                 $this->SetFillColorArray($fill_color);
6041                         }
6042                         switch ($style) {
6043                                 case "F": {
6044                                         $op = "f";
6045                                         $line_style = array();
6046                                         break;
6047                                 }
6048                                 case "FD":
6049                                 case "DF": {
6050                                         $op = "B";
6051                                         break;
6052                                 }
6053                                 case "CNZ": {
6054                                         $op = "W n";
6055                                         break;
6056                                 }
6057                                 case "CEO": {
6058                                         $op = "W* n";
6059                                         break;
6060                                 }
6061                                 default: {
6062                                         $op = "S";
6063                                         break;
6064                                 }
6065                         }
6066                         if ($line_style) {
6067                                 $this->SetLineStyle($line_style);
6068                         }
6069                         $this->_outPoint($x0, $y0);
6070                         foreach ($segments as $segment) {
6071                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
6072                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6073                         }
6074                         $this->_out($op);
6075                 }
6076
6077                 /**
6078                 * Draws an ellipse.
6079                 * An ellipse is formed from n Bezier curves.
6080                 * @param float $x0 Abscissa of center point.
6081                 * @param float $y0 Ordinate of center point.
6082                 * @param float $rx Horizontal radius.
6083                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
6084                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6085                 * @param float $astart: Angle start of draw line. Default value: 0.
6086                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6087                 * @param string $style Style of rendering. Possible values are:
6088                 * <ul>
6089                 *        <li>D or empty string: Draw (default).</li>
6090                 *        <li>F: Fill.</li>
6091                 *        <li>DF or FD: Draw and fill.</li>
6092                 *        <li>C: Draw close.</li>
6093                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6094                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6095                 * </ul>
6096                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6097                 * @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).
6098                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
6099                 * @access public
6100                 * @since 2.1.000 (2008-01-08)
6101                 */
6102                 function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6103                         if ($angle) {
6104                                 $this->StartTransform();
6105                                 $this->Rotate($angle, $x0, $y0);
6106                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6107                                 $this->StopTransform();
6108                                 return;
6109                         }
6110                         if ($rx) {
6111                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6112                                         $this->SetFillColorArray($fill_color);
6113                                 }
6114                                 switch ($style) {
6115                                         case "F": {
6116                                                 $op = "f";
6117                                                 $line_style = array();
6118                                                 break;
6119                                         }
6120                                         case "FD":
6121                                         case "DF": {
6122                                                 $op = "B";
6123                                                 break;
6124                                         }
6125                                         case "C": {
6126                                                 $op = "s"; // Small "s" signifies closing the path as well
6127                                                 break;
6128                                         }
6129                                         case "CNZ": {
6130                                                 $op = "W n";
6131                                                 break;
6132                                         }
6133                                         case "CEO": {
6134                                                 $op = "W* n";
6135                                                 break;
6136                                         }
6137                                         default: {
6138                                                 $op = "S";
6139                                                 break;
6140                                         }
6141                                 }
6142                                 if ($line_style) {
6143                                         $this->SetLineStyle($line_style);
6144                                 }
6145                                 if (!$ry) {
6146                                         $ry = $rx;
6147                                 }
6148                                 $rx *= $this->k;
6149                                 $ry *= $this->k;
6150                                 if ($nc < 2){
6151                                         $nc = 2;
6152                                 }
6153                                 $astart = deg2rad((float) $astart);
6154                                 $afinish = deg2rad((float) $afinish);
6155                                 $total_angle = $afinish - $astart;
6156                                 $dt = $total_angle / $nc;
6157                                 $dtm = $dt / 3;
6158                                 $x0 *= $this->k;
6159                                 $y0 = ($this->h - $y0) * $this->k;
6160                                 $t1 = $astart;
6161                                 $a0 = $x0 + ($rx * cos($t1));
6162                                 $b0 = $y0 + ($ry * sin($t1));
6163                                 $c0 = -$rx * sin($t1);
6164                                 $d0 = $ry * cos($t1);
6165                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
6166                                 for ($i = 1; $i <= $nc; $i++) {
6167                                         // Draw this bit of the total curve
6168                                         $t1 = ($i * $dt) + $astart;
6169                                         $a1 = $x0 + ($rx * cos($t1));
6170                                         $b1 = $y0 + ($ry * sin($t1));
6171                                         $c1 = -$rx * sin($t1);
6172                                         $d1 = $ry * cos($t1);
6173                                         $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));
6174                                         $a0 = $a1;
6175                                         $b0 = $b1;
6176                                         $c0 = $c1;
6177                                         $d0 = $d1;
6178                                 }
6179                                 $this->_out($op);
6180                         }
6181                 }
6182
6183                 /**
6184                 * Draws a circle.
6185                 * A circle is formed from n Bezier curves.
6186                 * @param float $x0 Abscissa of center point.
6187                 * @param float $y0 Ordinate of center point.
6188                 * @param float $r Radius.
6189                 * @param float $astart: Angle start of draw line. Default value: 0.
6190                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6191                 * @param string $style Style of rendering. Possible values are:
6192                 * <ul>
6193                 *        <li>D or empty string: Draw (default).</li>
6194                 *        <li>F: Fill.</li>
6195                 *        <li>DF or FD: Draw and fill.</li>
6196                 *        <li>C: Draw close.</li>
6197                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6198                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6199                 * </ul>
6200                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6201                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6202                 * @param integer $nc Number of curves used in circle. Default value: 8.
6203                 * @access public
6204                 * @since 2.1.000 (2008-01-08)
6205                 */
6206                 function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6207                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6208                 }
6209
6210                 /**
6211                 * Draws a polygon.
6212                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
6213                 * @param string $style Style of rendering. Possible values are:
6214                 * <ul>
6215                 *        <li>D or empty string: Draw (default).</li>
6216                 *        <li>F: Fill.</li>
6217                 *        <li>DF or FD: Draw and fill.</li>
6218                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6219                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6220                 * </ul>
6221                 * @param array $line_style Line style of polygon. Array with keys among the following:
6222                 * <ul>
6223                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
6224                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
6225                 * </ul>
6226                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
6227                 * @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).
6228                 * @access public
6229                 * @since 2.1.000 (2008-01-08)
6230                 */
6231                 function Polygon($p, $style="", $line_style=array(), $fill_color=array()) {
6232                         $np = count($p) / 2;
6233                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6234                                 $this->SetFillColorArray($fill_color);
6235                         }
6236                         switch ($style) {
6237                                 case "F": {
6238                                         $line_style = array();
6239                                         $op = "f";
6240                                         break;
6241                                 }
6242                                 case "FD":
6243                                 case "DF": {
6244                                         $op = "B";
6245                                         break;
6246                                 }
6247                                 case "CNZ": {
6248                                         $op = "W n";
6249                                         break;
6250                                 }
6251                                 case "CEO": {
6252                                         $op = "W* n";
6253                                         break;
6254                                 }
6255                                 default: {
6256                                         $op = "S";
6257                                         break;
6258                                 }
6259                         }
6260                         $draw = true;
6261                         if ($line_style) {
6262                                 if (isset($line_style["all"])) {
6263                                         $this->SetLineStyle($line_style["all"]);
6264                                 } else { // 0 .. (np - 1), op = {B, S}
6265                                         $draw = false;
6266                                         if ("B" == $op) {
6267                                                 $op = "f";
6268                                                 $this->_outPoint($p[0], $p[1]);
6269                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6270                                                         $this->_outLine($p[$i], $p[$i + 1]);
6271                                                 }
6272                                                 $this->_outLine($p[0], $p[1]);
6273                                                 $this->_out($op);
6274                                         }
6275                                         $p[($np * 2)] = $p[0];
6276                                         $p[(($np * 2) + 1)] = $p[1];
6277                                         for ($i = 0; $i < $np; $i++) {
6278                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
6279                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
6280                                                 }
6281                                         }
6282                                 }
6283                         }
6284                         if ($draw) {
6285                                 $this->_outPoint($p[0], $p[1]);
6286                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6287                                         $this->_outLine($p[$i], $p[$i + 1]);
6288                                 }
6289                                 $this->_outLine($p[0], $p[1]);
6290                                 $this->_out($op);
6291                         }
6292                 }
6293
6294                 /**
6295                 * Draws a regular polygon.
6296                 * @param float $x0 Abscissa of center point.
6297                 * @param float $y0 Ordinate of center point.
6298                 * @param float $r: Radius of inscribed circle.
6299                 * @param integer $ns Number of sides.
6300                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
6301                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
6302                 * @param string $style Style of rendering. Possible values are:
6303                 * <ul>
6304                 *        <li>D or empty string: Draw (default).</li>
6305                 *        <li>F: Fill.</li>
6306                 *        <li>DF or FD: Draw and fill.</li>
6307                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6308                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6309                 * </ul>
6310                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6311                 * <ul>
6312                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
6313                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6314                 * </ul>
6315                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6316                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6317                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6318                 * <ul>
6319                 *        <li>D or empty string: Draw (default).</li>
6320                 *        <li>F: Fill.</li>
6321                 *        <li>DF or FD: Draw and fill.</li>
6322                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6323                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6324                 * </ul>
6325                 * @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).
6326                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6327                 * @access public
6328                 * @since 2.1.000 (2008-01-08)
6329                 */
6330                 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()) {
6331                         if (3 > $ns) {
6332                                 $ns = 3;
6333                         }
6334                         if ($draw_circle) {
6335                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6336                         }
6337                         $p = array();
6338                         for ($i = 0; $i < $ns; $i++) {
6339                                 $a = $angle + ($i * 360 / $ns);
6340                                 $a_rad = deg2rad((float) $a);
6341                                 $p[] = $x0 + ($r * sin($a_rad));
6342                                 $p[] = $y0 + ($r * cos($a_rad));
6343                         }
6344                         $this->Polygon($p, $style, $line_style, $fill_color);
6345                 }
6346
6347                 /**
6348                 * Draws a star polygon
6349                 * @param float $x0 Abscissa of center point.
6350                 * @param float $y0 Ordinate of center point.
6351                 * @param float $r Radius of inscribed circle.
6352                 * @param integer $nv Number of vertices.
6353                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
6354                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6355                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
6356                 * @param string $style Style of rendering. Possible values are:
6357                 * <ul>
6358                 *        <li>D or empty string: Draw (default).</li>
6359                 *        <li>F: Fill.</li>
6360                 *        <li>DF or FD: Draw and fill.</li>
6361                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6362                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6363                 * </ul>
6364                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6365                 * <ul>
6366                 *        <li>all: Line style of all sides. Array like for
6367                 * {@link SetLineStyle SetLineStyle}.</li>
6368                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6369                 * </ul>
6370                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6371                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6372                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6373                 * <ul>
6374                 *        <li>D or empty string: Draw (default).</li>
6375                 *        <li>F: Fill.</li>
6376                 *        <li>DF or FD: Draw and fill.</li>
6377                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6378                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6379                 * </ul>
6380                 * @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).
6381                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6382                 * @access public
6383                 * @since 2.1.000 (2008-01-08)
6384                 */
6385                 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()) {
6386                         if (2 > $nv) {
6387                                 $nv = 2;
6388                         }
6389                         if ($draw_circle) {
6390                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6391                         }
6392                         $p2 = array();
6393                         $visited = array();
6394                         for ($i = 0; $i < $nv; $i++) {
6395                                 $a = $angle + ($i * 360 / $nv);
6396                                 $a_rad = deg2rad((float) $a);
6397                                 $p2[] = $x0 + ($r * sin($a_rad));
6398                                 $p2[] = $y0 + ($r * cos($a_rad));
6399                                 $visited[] = false;
6400                         }
6401                         $p = array();
6402                         $i = 0;
6403                         do {
6404                                 $p[] = $p2[$i * 2];
6405                                 $p[] = $p2[($i * 2) + 1];
6406                                 $visited[$i] = true;
6407                                 $i += $ng;
6408                                 $i %= $nv;
6409                         } while (!$visited[$i]);
6410                         $this->Polygon($p, $style, $line_style, $fill_color);
6411                 }
6412
6413                 /**
6414                 * Draws a rounded rectangle.
6415                 * @param float $x Abscissa of upper-left corner.
6416                 * @param float $y Ordinate of upper-left corner.
6417                 * @param float $w Width.
6418                 * @param float $h Height.
6419                 * @param float $r Radius of the rounded corners.
6420                 * @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").
6421                 * @param string $style Style of rendering. Possible values are:
6422                 * <ul>
6423                 *        <li>D or empty string: Draw (default).</li>
6424                 *        <li>F: Fill.</li>
6425                 *        <li>DF or FD: Draw and fill.</li>
6426                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6427                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6428                 * </ul>
6429                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6430                 * @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).
6431                 * @access public
6432                 * @since 2.1.000 (2008-01-08)
6433                 */
6434                 function RoundedRect($x, $y, $w, $h, $r, $round_corner="1111", $style="", $border_style=array(), $fill_color=array()) {
6435                         if ("0000" == $round_corner) { // Not rounded
6436                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
6437                         } else { // Rounded
6438                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6439                                         $this->SetFillColorArray($fill_color);
6440                                 }
6441                                 switch ($style) {
6442                                         case "F": {
6443                                                 $border_style = array();
6444                                                 $op = "f";
6445                                                 break;
6446                                         }
6447                                         case "FD":
6448                                         case "DF": {
6449                                                 $op = "B";
6450                                                 break;
6451                                         }
6452                                         case "CNZ": {
6453                                                 $op = "W n";
6454                                                 break;
6455                                         }
6456                                         case "CEO": {
6457                                                 $op = "W* n";
6458                                                 break;
6459                                         }
6460                                         default: {
6461                                                 $op = "S";
6462                                                 break;
6463                                         }
6464                                 }
6465                                 if ($border_style) {
6466                                         $this->SetLineStyle($border_style);
6467                                 }
6468                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
6469                                 $this->_outPoint($x + $r, $y);
6470                                 $xc = $x + $w - $r;
6471                                 $yc = $y + $r;
6472                                 $this->_outLine($xc, $y);
6473                                 if ($round_corner[0]) {
6474                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
6475                                 } else {
6476                                         $this->_outLine($x + $w, $y);
6477                                 }
6478                                 $xc = $x + $w - $r;
6479                                 $yc = $y + $h - $r;
6480                                 $this->_outLine($x + $w, $yc);
6481                                 if ($round_corner[1]) {
6482                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
6483                                 } else {
6484                                         $this->_outLine($x + $w, $y + $h);
6485                                 }
6486                                 $xc = $x + $r;
6487                                 $yc = $y + $h - $r;
6488                                 $this->_outLine($xc, $y + $h);
6489                                 if ($round_corner[2]) {
6490                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
6491                                 } else {
6492                                         $this->_outLine($x, $y + $h);
6493                                 }
6494                                 $xc = $x + $r;
6495                                 $yc = $y + $r;
6496                                 $this->_outLine($x, $yc);
6497                                 if ($round_corner[3]) {
6498                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
6499                                 } else {
6500                                         $this->_outLine($x, $y);
6501                                         $this->_outLine($x + $r, $y);
6502                                 }
6503                                 $this->_out($op);
6504                         }
6505                 }
6506
6507                 // END GRAPHIC FUNCTIONS SECTION -----------------------
6508
6509                 // BIDIRECTIONAL TEXT SECTION --------------------------
6510                 /**
6511                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
6512                  * @param string $str string to manipulate.
6513                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
6514                  * @return string
6515                  * @author Nicola Asuni
6516                  * @since 2.1.000 (2008-01-08)
6517                 */
6518                 function utf8StrRev($str, $setbom=false, $forcertl=false) {
6519                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $forcertl), $setbom);
6520                 }
6521
6522                 /**
6523                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
6524                  * @param array $ta array of characters composing the string.
6525                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
6526                  * @return string
6527                  * @author Nicola Asuni
6528                  * @since 2.4.000 (2008-03-06)
6529                 */
6530                 function utf8Bidi($ta, $forcertl=false) {
6531                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
6532
6533                         // paragraph embedding level
6534                         $pel = 0;
6535                         // max level
6536                         $maxlevel = 0;
6537                         // create string from array
6538                         $str = $this->UTF8ArrSubString($ta);
6539                         // check if string contains arabic text
6540                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
6541                                 $arabic = true;
6542                         } else {
6543                                 $arabic = false;
6544                         }
6545                         // check if string contains RTL text
6546                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
6547                                 return $ta;
6548                         }
6549                         // get number of chars
6550                         $numchars = count($ta);
6551                         if ($forcertl == 'R') {
6552                                         $pel = 1;
6553                         } elseif ($forcertl == 'L') {
6554                                         $pel = 0;
6555                         } else {
6556                                 // P2. In each paragraph, find the first character of type L, AL, or R.
6557                                 // 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.
6558                                 for ($i=0; $i < $numchars; $i++) {
6559                                         $type = $unicode[$ta[$i]];
6560                                         if ($type == 'L') {
6561                                                 $pel = 0;
6562                                                 break;
6563                                         } elseif (($type == 'AL') OR ($type == 'R')) {
6564                                                 $pel = 1;
6565                                                 break;
6566                                         }
6567                                 }
6568                         }
6569                         // Current Embedding Level
6570                         $cel = $pel;
6571                         // directional override status
6572                         $dos = 'N';
6573                         $remember = array();
6574                         // start-of-level-run
6575                         $sor = $pel % 2 ? 'R' : 'L';
6576                         $eor = $sor;
6577
6578                         //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array()));
6579                         //$current_level = &$levels[count( $levels )-1];
6580
6581                         // Array of characters data
6582                         $chardata = Array();
6583
6584                         // 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.
6585                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
6586                         for ($i=0; $i < $numchars; $i++) {
6587                                 if ($ta[$i] == K_RLE) {
6588                                         // X2. With each RLE, compute the least greater odd embedding level.
6589                                         //      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.
6590                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6591                                         $next_level = $cel + ($cel % 2) + 1;
6592                                         if ($next_level < 62) {
6593                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
6594                                                 $cel = $next_level;
6595                                                 $dos = 'N';
6596                                                 $sor = $eor;
6597                                                 $eor = $cel % 2 ? 'R' : 'L';
6598                                         }
6599                                 } elseif ($ta[$i] == K_LRE) {
6600                                         // X3. With each LRE, compute the least greater even embedding level.
6601                                         //      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.
6602                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6603                                         $next_level = $cel + 2 - ($cel % 2);
6604                                         if ( $next_level < 62 ) {
6605                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
6606                                                 $cel = $next_level;
6607                                                 $dos = 'N';
6608                                                 $sor = $eor;
6609                                                 $eor = $cel % 2 ? 'R' : 'L';
6610                                         }
6611                                 } elseif ($ta[$i] == K_RLO) {
6612                                         // X4. With each RLO, compute the least greater odd embedding level.
6613                                         //      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.
6614                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6615                                         $next_level = $cel + ($cel % 2) + 1;
6616                                         if ($next_level < 62) {
6617                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
6618                                                 $cel = $next_level;
6619                                                 $dos = 'R';
6620                                                 $sor = $eor;
6621                                                 $eor = $cel % 2 ? 'R' : 'L';
6622                                         }
6623                                 } elseif ($ta[$i] == K_LRO) {
6624                                         // X5. With each LRO, compute the least greater even embedding level.
6625                                         //      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.
6626                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
6627                                         $next_level = $cel + 2 - ($cel % 2);
6628                                         if ( $next_level < 62 ) {
6629                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
6630                                                 $cel = $next_level;
6631                                                 $dos = 'L';
6632                                                 $sor = $eor;
6633                                                 $eor = $cel % 2 ? 'R' : 'L';
6634                                         }
6635                                 } elseif ($ta[$i] == K_PDF) {
6636                                         // 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.
6637                                         if (count($remember)) {
6638                                                 $last = count($remember ) - 1;
6639                                                 if (($remember[$last]['num'] == K_RLE) OR
6640                                                           ($remember[$last]['num'] == K_LRE) OR
6641                                                           ($remember[$last]['num'] == K_RLO) OR
6642                                                           ($remember[$last]['num'] == K_LRO)) {
6643                                                         $match = array_pop($remember);
6644                                                         $cel = $match['cel'];
6645                                                         $dos = $match['dos'];
6646                                                         $sor = $eor;
6647                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
6648                                                 }
6649                                         }
6650                                 } elseif (($ta[$i] != K_RLE) AND
6651                                                                  ($ta[$i] != K_LRE) AND
6652                                                                  ($ta[$i] != K_RLO) AND
6653                                                                  ($ta[$i] != K_LRO) AND
6654                                                                  ($ta[$i] != K_PDF)) {
6655                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
6656                                         //      a. Set the level of the current character to the current embedding level.
6657                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
6658                                         if ($dos != 'N') {
6659                                                 $chardir = $dos;
6660                                         } else {
6661                                                 $chardir = $unicode[$ta[$i]];
6662                                         }
6663                                         // stores string characters and other information
6664                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
6665                                 }
6666                         } // end for each char
6667
6668                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
6669                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
6670                         // 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 \93other\94 run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
6671
6672                         // 3.3.3 Resolving Weak Types
6673                         // 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.
6674                         // Nonspacing marks are now resolved based on the previous characters.
6675                         $numchars = count($chardata);
6676
6677                         // 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.
6678                         $prevlevel = -1; // track level changes
6679                         $levcount = 0; // counts consecutive chars at the same level
6680                         for ($i=0; $i < $numchars; $i++) {
6681                                 if ($chardata[$i]['type'] == 'NSM') {
6682                                         if ($levcount) {
6683                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
6684                                         } elseif ($i > 0) {
6685                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
6686                                         }
6687                                 }
6688                                 if ($chardata[$i]['level'] != $prevlevel) {
6689                                         $levcount = 0;
6690                                 } else {
6691                                         $levcount++;
6692                                 }
6693                                 $prevlevel = $chardata[$i]['level'];
6694                         }
6695
6696                         // 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.
6697                         $prevlevel = -1;
6698                         $levcount = 0;
6699                         for ($i=0; $i < $numchars; $i++) {
6700                                 if ($chardata[$i]['char'] == 'EN') {
6701                                         for ($j=$levcount; $j >= 0; $j--) {
6702                                                 if ($chardata[$j]['type'] == 'AL') {
6703                                                         $chardata[$i]['type'] = 'AN';
6704                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
6705                                                         break;
6706                                                 }
6707                                         }
6708                                 }
6709                                 if ($chardata[$i]['level'] != $prevlevel) {
6710                                         $levcount = 0;
6711                                 } else {
6712                                         $levcount++;
6713                                 }
6714                                 $prevlevel = $chardata[$i]['level'];
6715                         }
6716
6717                         // W3. Change all ALs to R.
6718                         for ($i=0; $i < $numchars; $i++) {
6719                                 if ($chardata[$i]['type'] == 'AL') {
6720                                         $chardata[$i]['type'] = 'R';
6721                                 }
6722                         }
6723
6724                         // 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.
6725                         $prevlevel = -1;
6726                         $levcount = 0;
6727                         for ($i=0; $i < $numchars; $i++) {
6728                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
6729                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
6730                                                 $chardata[$i]['type'] = 'EN';
6731                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
6732                                                 $chardata[$i]['type'] = 'EN';
6733                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
6734                                                 $chardata[$i]['type'] = 'AN';
6735                                         }
6736                                 }
6737                                 if ($chardata[$i]['level'] != $prevlevel) {
6738                                         $levcount = 0;
6739                                 } else {
6740                                         $levcount++;
6741                                 }
6742                                 $prevlevel = $chardata[$i]['level'];
6743                         }
6744
6745                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
6746                         $prevlevel = -1;
6747                         $levcount = 0;
6748                         for ($i=0; $i < $numchars; $i++) {
6749                                 if ($chardata[$i]['type'] == 'ET') {
6750                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
6751                                                 $chardata[$i]['type'] = 'EN';
6752                                         } else {
6753                                                 $j = $i+1;
6754                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
6755                                                         if ($chardata[$j]['type'] == 'EN') {
6756                                                                 $chardata[$i]['type'] = 'EN';
6757                                                                 break;
6758                                                         } elseif ($chardata[$j]['type'] != 'ET') {
6759                                                                 break;
6760                                                         }
6761                                                         $j++;
6762                                                 }
6763                                         }
6764                                 }
6765                                 if ($chardata[$i]['level'] != $prevlevel) {
6766                                         $levcount = 0;
6767                                 } else {
6768                                         $levcount++;
6769                                 }
6770                                 $prevlevel = $chardata[$i]['level'];
6771                         }
6772
6773                         // W6. Otherwise, separators and terminators change to Other Neutral.
6774                         $prevlevel = -1;
6775                         $levcount = 0;
6776                         for ($i=0; $i < $numchars; $i++) {
6777                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
6778                                         $chardata[$i]['type'] = 'ON';
6779                                 }
6780                                 if ($chardata[$i]['level'] != $prevlevel) {
6781                                         $levcount = 0;
6782                                 } else {
6783                                         $levcount++;
6784                                 }
6785                                 $prevlevel = $chardata[$i]['level'];
6786                         }
6787
6788                         //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.
6789                         $prevlevel = -1;
6790                         $levcount = 0;
6791                         for ($i=0; $i < $numchars; $i++) {
6792                                 if ($chardata[$i]['char'] == 'EN') {
6793                                         for ($j=$levcount; $j >= 0; $j--) {
6794                                                 if ($chardata[$j]['type'] == 'L') {
6795                                                         $chardata[$i]['type'] = 'L';
6796                                                 } elseif ($chardata[$j]['type'] == 'R') {
6797                                                         break;
6798                                                 }
6799                                         }
6800                                 }
6801                                 if ($chardata[$i]['level'] != $prevlevel) {
6802                                         $levcount = 0;
6803                                 } else {
6804                                         $levcount++;
6805                                 }
6806                                 $prevlevel = $chardata[$i]['level'];
6807                         }
6808
6809                         // 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.
6810                         $prevlevel = -1;
6811                         $levcount = 0;
6812                         for ($i=0; $i < $numchars; $i++) {
6813                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
6814                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
6815                                                 $chardata[$i]['type'] = 'L';
6816                                         } elseif (($chardata[$i]['type'] == 'N') AND
6817                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
6818                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
6819                                                 $chardata[$i]['type'] = 'R';
6820                                         } elseif ($chardata[$i]['type'] == 'N') {
6821                                                 // N2. Any remaining neutrals take the embedding direction
6822                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
6823                                         }
6824                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
6825                                         // first char
6826                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
6827                                                 $chardata[$i]['type'] = 'L';
6828                                         } elseif (($chardata[$i]['type'] == 'N') AND
6829                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
6830                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
6831                                                 $chardata[$i]['type'] = 'R';
6832                                         } elseif ($chardata[$i]['type'] == 'N') {
6833                                                 // N2. Any remaining neutrals take the embedding direction
6834                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
6835                                         }
6836                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
6837                                         //last char
6838                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
6839                                                 $chardata[$i]['type'] = 'L';
6840                                         } elseif (($chardata[$i]['type'] == 'N') AND
6841                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
6842                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
6843                                                 $chardata[$i]['type'] = 'R';
6844                                         } elseif ($chardata[$i]['type'] == 'N') {
6845                                                 // N2. Any remaining neutrals take the embedding direction
6846                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
6847                                         }
6848                                 } elseif ($chardata[$i]['type'] == 'N') {
6849                                         // N2. Any remaining neutrals take the embedding direction
6850                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
6851                                 }
6852                                 if ($chardata[$i]['level'] != $prevlevel) {
6853                                         $levcount = 0;
6854                                 } else {
6855                                         $levcount++;
6856                                 }
6857                                 $prevlevel = $chardata[$i]['level'];
6858                         }
6859
6860                         // 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.
6861                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
6862                         for ($i=0; $i < $numchars; $i++) {
6863                                 $odd = $chardata[$i]['level'] % 2;
6864                                 if ($odd) {
6865                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
6866                                                 $chardata[$i]['level'] += 1;
6867                                         }
6868                                 } else {
6869                                         if ($chardata[$i]['type'] == 'R') {
6870                                                 $chardata[$i]['level'] += 1;
6871                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
6872                                                 $chardata[$i]['level'] += 2;
6873                                         }
6874                                 }
6875                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
6876                         }
6877
6878                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
6879                         //      1. Segment separators,
6880                         //      2. Paragraph separators,
6881                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
6882                         //      4. Any sequence of white space characters at the end of the line.
6883                         for ($i=0; $i < $numchars; $i++) {
6884                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
6885                                         $chardata[$i]['level'] = $pel;
6886                                 } elseif ($chardata[$i]['type'] == 'WS') {
6887                                         $j = $i+1;
6888                                         while ($j < $numchars) {
6889                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
6890                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
6891                                                         $chardata[$i]['level'] = $pel;
6892                                                         break;
6893                                                 } elseif ($chardata[$j]['type'] != 'WS') {
6894                                                         break;
6895                                                 }
6896                                                 $j++;
6897                                         }
6898                                 }
6899                         }
6900
6901                         // Arabic Shaping
6902                         // 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.
6903                         if ($arabic) {
6904                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
6905                                 $alfletter = array(1570,1571,1573,1575);
6906                                 $chardata2 = $chardata;
6907                                 $laaletter = false;
6908                                 $charAL = array();
6909                                 $x = 0;
6910                                 for ($i=0; $i < $numchars; $i++) {
6911                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
6912                                                 $charAL[$x] = $chardata[$i];
6913                                                 $charAL[$x]['i'] = $i;
6914                                                 $chardata[$i]['x'] = $x;
6915                                                 $x++;
6916                                         }
6917                                 }
6918                                 $numAL = $x;
6919                                 for ($i=0; $i < $numchars; $i++) {
6920                                         $thischar = $chardata[$i];
6921                                         if ($i > 0) {
6922                                                 $prevchar = $chardata[($i-1)];
6923                                         } else {
6924                                                 $prevchar = false;
6925                                         }
6926                                         if (($i+1) < $numchars) {
6927                                                 $nextchar = $chardata[($i+1)];
6928                                         } else {
6929                                                 $nextchar = false;
6930                                         }
6931                                         if ($unicode[$thischar['char']] == 'AL') {
6932                                                 $x = $thischar['x'];
6933                                                 if ($x > 0) {
6934                                                         $prevchar = $charAL[($x-1)];
6935                                                 } else {
6936                                                         $prevchar = false;
6937                                                 }
6938                                                 if (($x+1) < $numAL) {
6939                                                         $nextchar = $charAL[($x+1)];
6940                                                 } else {
6941                                                         $nextchar = false;
6942                                                 }
6943                                                 // if laa letter
6944                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
6945                                                         $arabicarr = $laa_array;
6946                                                         $laaletter = true;
6947                                                         if ($x > 1) {
6948                                                                 $prevchar = $charAL[($x-2)];
6949                                                         } else {
6950                                                                 $prevchar = false;
6951                                                         }
6952                                                 } else {
6953                                                         $arabicarr = $unicode_arlet;
6954                                                         $laaletter = false;
6955                                                 }
6956                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
6957                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
6958                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
6959                                                         ($prevchar['type'] == $thischar['type']) AND
6960                                                         ($nextchar['type'] == $thischar['type']) AND
6961                                                         ($nextchar['char'] != 1567)) {
6962                                                         if (in_array($prevchar['char'], $endedletter)) {
6963                                                                 if (isset($arabicarr[$thischar['char']][2])) {
6964                                                                         // initial
6965                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
6966                                                                 }
6967                                                         } else {
6968                                                                 if (isset($arabicarr[$thischar['char']][3])) {
6969                                                                         // medial
6970                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
6971                                                                 }
6972                                                         }
6973                                                 } elseif (($nextchar !== false) AND
6974                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
6975                                                         ($nextchar['type'] == $thischar['type']) AND
6976                                                         ($nextchar['char'] != 1567)) {
6977                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
6978                                                                 // initial
6979                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
6980                                                         }
6981                                                 } elseif ((($prevchar !== false) AND
6982                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
6983                                                         ($prevchar['type'] == $thischar['type'])) OR
6984                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
6985                                                         // final
6986                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
6987                                                                 ($chardata[$i-1]['char'] == 1604) AND
6988                                                                 ($chardata[$i-2]['char'] == 1604)) {
6989                                                                 //Allah Word
6990                                                                 // mark characters to delete with false
6991                                                                 $chardata2[$i-2]['char'] = false;
6992                                                                 $chardata2[$i-1]['char'] = false;
6993                                                                 $chardata2[$i]['char'] = 65010;
6994                                                         } else {
6995                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
6996                                                                         if (isset($arabicarr[$thischar['char']][0])) {
6997                                                                                 // isolated
6998                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
6999                                                                         }
7000                                                                 } else {
7001                                                                         if (isset($arabicarr[$thischar['char']][1])) {
7002                                                                                 // final
7003                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
7004                                                                         }
7005                                                                 }
7006                                                         }
7007                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
7008                                                         // isolated
7009                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7010                                                 }
7011                                                 // if laa letter
7012                                                 if ($laaletter) {
7013                                                         // mark characters to delete with false
7014                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
7015                                                 }
7016                                         } // end if AL (Arabic Letter)
7017                                 } // end for each char
7018                                 /*
7019                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594.
7020                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
7021                                  */
7022                                 $cw = &$this->CurrentFont['cw'];
7023                                 for ($i=0; $i < ($numchars-1); $i++) {
7024                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
7025                                                 // check if the subtitution font is defined on current font
7026                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
7027                                                         $chardata2[$i]['char'] = false;
7028                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
7029                                                 }
7030                                         }
7031                                 }
7032                                 // remove marked characters
7033                                 foreach($chardata2 as $key => $value) {
7034                                         if ($value['char'] === false) {
7035                                                 unset($chardata2[$key]);
7036                                         }
7037                                 }
7038                                 $chardata = array_values($chardata2);
7039                                 $numchars = count($chardata);
7040                                 unset($chardata2);
7041                                 unset($arabicarr);
7042                                 unset($laaletter);
7043                                 unset($charAL);
7044                         }
7045
7046                         // 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.
7047                         for ($j=$maxlevel; $j > 0; $j--) {
7048                                 $ordarray = Array();
7049                                 $revarr = Array();
7050                                 $onlevel = false;
7051                                 for ($i=0; $i < $numchars; $i++) {
7052                                         if ($chardata[$i]['level'] >= $j) {
7053                                                 $onlevel = true;
7054                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
7055                                                         // 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.
7056                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
7057                                                 }
7058                                                 $revarr[] = $chardata[$i];
7059                                         } else {
7060                                                 if ($onlevel) {
7061                                                         $revarr = array_reverse($revarr);
7062                                                         $ordarray = array_merge($ordarray, $revarr);
7063                                                         $revarr = Array();
7064                                                         $onlevel = false;
7065                                                 }
7066                                                 $ordarray[] = $chardata[$i];
7067                                         }
7068                                 }
7069                                 if ($onlevel) {
7070                                         $revarr = array_reverse($revarr);
7071                                         $ordarray = array_merge($ordarray, $revarr);
7072                                 }
7073                                 $chardata = $ordarray;
7074                         }
7075
7076                         $ordarray = array();
7077                         for ($i=0; $i < $numchars; $i++) {
7078                                 $ordarray[] = $chardata[$i]['char'];
7079                         }
7080
7081                         return $ordarray;
7082                 }
7083
7084                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
7085
7086                 /*
7087                 * Adds a bookmark.
7088                 * @param string $txt bookmark description.
7089                 * @param int $level bookmark level.
7090                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
7091                 * @access public
7092                 * @author Olivier Plathey, Nicola Asuni
7093                 * @since 2.1.002 (2008-02-12)
7094                 */
7095                 function Bookmark($txt, $level=0, $y=-1) {
7096                         if ($y == -1) {
7097                                 $y = $this->GetY();
7098                         }
7099                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->PageNo());
7100                 }
7101
7102                 /*
7103                 * Create a bookmark PDF string.
7104                 * @access protected
7105                 * @author Olivier Plathey, Nicola Asuni
7106                 * @since 2.1.002 (2008-02-12)
7107                 */
7108                 function _putbookmarks() {
7109                         $nb = count($this->outlines);
7110                         if ($nb == 0) {
7111                                 return;
7112                         }
7113                         $lru = array();
7114                         $level = 0;
7115                         foreach($this->outlines as $i => $o) {
7116                                 if ($o['l'] > 0) {
7117                                         $parent = $lru[($o['l'] - 1)];
7118                                         //Set parent and last pointers
7119                                         $this->outlines[$i]['parent'] = $parent;
7120                                         $this->outlines[$parent]['last'] = $i;
7121                                         if ($o['l'] > $level) {
7122                                                 //Level increasing: set first pointer
7123                                                 $this->outlines[$parent]['first'] = $i;
7124                                         }
7125                                 } else {
7126                                         $this->outlines[$i]['parent'] = $nb;
7127                                 }
7128                                 if (($o['l'] <= $level) AND ($i > 0)) {
7129                                         //Set prev and next pointers
7130                                         $prev = $lru[$o['l']];
7131                                         $this->outlines[$prev]['next'] = $i;
7132                                         $this->outlines[$i]['prev'] = $prev;
7133                                 }
7134                                 $lru[$o['l']] = $i;
7135                                 $level = $o['l'];
7136                         }
7137                         //Outline items
7138                         $n = $this->n + 1;
7139                         foreach($this->outlines as $i => $o) {
7140                                 $this->_newobj();
7141                                 $this->_out('<</Title '.$this->_textstring($o['t']));
7142                                 $this->_out('/Parent '.($n+$o['parent']).' 0 R');
7143                                 if (isset($o['prev']))
7144                                 $this->_out('/Prev '.($n+$o['prev']).' 0 R');
7145                                 if (isset($o['next']))
7146                                 $this->_out('/Next '.($n+$o['next']).' 0 R');
7147                                 if (isset($o['first']))
7148                                 $this->_out('/First '.($n+$o['first']).' 0 R');
7149                                 if (isset($o['last']))
7150                                 $this->_out('/Last '.($n+$o['last']).' 0 R');
7151                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]', 1+2*$o['p'], ($this->h-$o['y'])*$this->k));
7152                                 $this->_out('/Count 0>>');
7153                                 $this->_out('endobj');
7154                         }
7155                         //Outline root
7156                         $this->_newobj();
7157                         $this->OutlineRoot=$this->n;
7158                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
7159                         $this->_out('/Last '.($n+$lru[0]).' 0 R>>');
7160                         $this->_out('endobj');
7161                 }
7162
7163
7164                 // --- JAVASCRIPT - FORMS ------------------------------
7165
7166                 /*
7167                 * Adds a javascript
7168                 * @access public
7169                 * @author Johannes Güntert, Nicola Asuni
7170                 * @since 2.1.002 (2008-02-12)
7171                 */
7172                 function IncludeJS($script) {
7173                         $this->javascript .= $script;
7174                 }
7175
7176                 /*
7177                 * Create a javascript PDF string.
7178                 * @access protected
7179                 * @author Johannes Güntert, Nicola Asuni
7180                 * @since 2.1.002 (2008-02-12)
7181                 */
7182                 function _putjavascript() {
7183                         if (empty($this->javascript)) {
7184                                 return;
7185                         }
7186                         $this->_newobj();
7187                         $this->n_js = $this->n;
7188                         $this->_out('<<');
7189                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
7190                         $this->_out('>>');
7191                         $this->_out('endobj');
7192                         $this->_newobj();
7193                         $this->_out('<<');
7194                         $this->_out('/S /JavaScript');
7195                         $this->_out('/JS '.$this->_textstring($this->javascript));
7196                         $this->_out('>>');
7197                         $this->_out('endobj');
7198                 }
7199
7200                 /*
7201                 * Convert color to javascript color.
7202                 * @param string $color color name or #RRGGBB
7203                 * @access protected
7204                 * @author Denis Van Nuffelen, Nicola Asuni
7205                 * @since 2.1.002 (2008-02-12)
7206                 */
7207                 function _JScolor($color) {
7208                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
7209                         if (substr($color,0,1) == '#') {
7210                                 return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
7211                         }
7212                         if (!in_array($color,$aColors)) {
7213                                 $this->Error('Invalid color: '.$color);
7214                         }
7215                         return 'color.'.$color;
7216                 }
7217
7218                 /*
7219                 * Adds a javascript form field.
7220                 * @param string $type field type
7221                 * @param string $name field name
7222                 * @param int $x horizontal position
7223                 * @param int $y vertical position
7224                 * @param int $w width
7225                 * @param int $h height
7226                 * @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>
7227                 * @access protected
7228                 * @author Denis Van Nuffelen, Nicola Asuni
7229                 * @since 2.1.002 (2008-02-12)
7230                 */
7231                 function _addfield($type, $name, $x, $y, $w, $h, $prop) {
7232                         $k = $this->k;
7233                         $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";
7234                         $this->javascript .= "f".$name.".textSize=".$this->FontSizePt.";\n";
7235                         while (list($key, $val) = each($prop)) {
7236                                 if (strcmp(substr($key,-5),"Color") == 0) {
7237                                         $val = $this->_JScolor($val);
7238                                 } else {
7239                                         $val = "'".$val."'";
7240                                 }
7241                                 $this->javascript .= "f".$name.".".$key."=".$val.";\n";
7242                         }
7243                         $this->x += $w;
7244                 }
7245
7246                 /*
7247                 * Creates a text field
7248                 * @param string $name field name
7249                 * @param int $w width
7250                 * @param int $h height
7251                 * @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>
7252                 * @access public
7253                 * @author Denis Van Nuffelen, Nicola Asuni
7254                 * @since 2.1.002 (2008-02-12)
7255                 */
7256                 function TextField($name, $w, $h, $prop=array()) {
7257                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
7258                 }
7259
7260                 /*
7261                 * Creates a RadioButton field
7262                 * @param string $name field name
7263                 * @param int $w width
7264                 * @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>
7265                 * @access public
7266                 * @author Nicola Asuni
7267                 * @since 2.2.003 (2008-03-03)
7268                 */
7269                 function RadioButton($name, $w, $prop=array()) {
7270                         if (!isset($prop['strokeColor'])) {
7271                                 $prop['strokeColor']='black';
7272                         }
7273                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
7274                 }
7275
7276                 /*
7277                 * Creates a List-box field
7278                 * @param string $name field name
7279                 * @param int $w width
7280                 * @param int $h height
7281                 * @param array $values array containing the list of values.
7282                 * @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>
7283                 * @access public
7284                 * @author Nicola Asuni
7285                 * @since 2.2.003 (2008-03-03)
7286                 */
7287                 function ListBox($name, $w, $h, $values, $prop=array()) {
7288                         if (!isset($prop['strokeColor'])) {
7289                                 $prop['strokeColor'] = 'ltGray';
7290                         }
7291                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
7292                         $s = '';
7293                         foreach($values as $value) {
7294                                 $s .= "'".addslashes($value)."',";
7295                         }
7296                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7297                 }
7298
7299                 /*
7300                 * Creates a Combo-box field
7301                 * @param string $name field name
7302                 * @param int $w width
7303                 * @param int $h height
7304                 * @param array $values array containing the list of values.
7305                 * @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>
7306                 * @access public
7307                 * @author Denis Van Nuffelen, Nicola Asuni
7308                 * @since 2.1.002 (2008-02-12)
7309                 */
7310                 function ComboBox($name, $w, $h, $values, $prop=array()) {
7311                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
7312                         $s = '';
7313                         foreach($values as $value) {
7314                                 $s .= "'".addslashes($value)."',";
7315                         }
7316                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7317                 }
7318
7319                 /*
7320                 * Creates a CheckBox field
7321                 * @param string $name field name
7322                 * @param int $w width
7323                 * @param boolean $checked define the initial state (default = false).
7324                 * @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>
7325                 * @access public
7326                 * @author Denis Van Nuffelen, Nicola Asuni
7327                 * @since 2.1.002 (2008-02-12)
7328                 */
7329                 function CheckBox($name, $w, $checked=false, $prop=array()) {
7330                         $prop['value'] = ($checked ? 'Yes' : 'Off');
7331                         if (!isset($prop['strokeColor'])) {
7332                                 $prop['strokeColor'] = 'black';
7333                         }
7334                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
7335                 }
7336
7337                 /*
7338                 * Creates a button field
7339                 * @param string $name field name
7340                 * @param int $w width
7341                 * @param int $h height
7342                 * @param string $caption caption.
7343                 * @param string $action action triggered by the button (JavaScript code).
7344                 * @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>
7345                 * @access public
7346                 * @author Denis Van Nuffelen, Nicola Asuni
7347                 * @since 2.1.002 (2008-02-12)
7348                 */
7349                 function Button($name, $w, $h, $caption, $action, $prop=array()) {
7350                         if (!isset($prop['strokeColor'])) {
7351                                 $prop['strokeColor'] = 'black';
7352                         }
7353                         if (!isset($prop['borderStyle'])) {
7354                                 $prop['borderStyle'] = 'beveled';
7355                         }
7356                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
7357                         $this->javascript .= "f".$name.".buttonSetCaption('".addslashes($caption)."');\n";
7358                         $this->javascript .= "f".$name.".setAction('MouseUp','".addslashes($action)."');\n";
7359                         $this->javascript .= "f".$name.".highlight='push';\n";
7360                         $this->javascript .= "f".$name.".print=false;\n";
7361                 }
7362
7363                 // END JAVASCRIPT - FORMS ------------------------------
7364
7365                 /*
7366                 * Enable Write permissions for PDF Reader.
7367                 * @access protected
7368                 * @author Nicola Asuni
7369                 * @since 2.9.000 (2008-03-26)
7370                 */
7371                 function _putuserrights() {
7372                         if (!$this->ur) {
7373                                 return;
7374                         }
7375                         $this->_out('/Perms');
7376                         $this->_out('<<');
7377                         $this->_out('/UR3');
7378                         $this->_out('<<');
7379                         //$this->_out('/SubFilter/adbe.pkcs7.detached/Filter/Adobe.PPKLite/Contents');
7380                         //$this->_out('<0>');
7381                         //$this->_out('/ByteRange[0 3]');
7382                         $this->_out('/M '.$this->_datestring('D:'.date('YmdHis')));
7383                         $this->_out('/Name(TCPDF)');
7384                         $this->_out('/Reference[');
7385                         $this->_out('<<');
7386                         $this->_out('/TransformParams');
7387                         $this->_out('<<');
7388                         $this->_out('/Type/TransformParams');
7389                         $this->_out('/V/2.2');
7390                         if (!empty($this->ur_document)) {
7391                                 $this->_out('/Document['.$this->ur_document.']');
7392                         }
7393                         if (!empty($this->ur_annots)) {
7394                                 $this->_out('/Annots['.$this->ur_annots.']');
7395                         }
7396                         if (!empty($this->ur_form)) {
7397                                 $this->_out('/Form['.$this->ur_form.']');
7398                         }
7399                         if (!empty($this->ur_signature)) {
7400                                 $this->_out('/Signature['.$this->ur_signature.']');
7401                         }
7402                         $this->_out('>>');
7403                         $this->_out('/TransformMethod/UR3');
7404                         $this->_out('/Type/SigRef');
7405                         $this->_out('>>');
7406                         $this->_out(']');
7407                         $this->_out('/Type/Sig');
7408                         $this->_out('>>');
7409                         $this->_out('>>');
7410                 }
7411
7412                 /*
7413                 * Set User's Rights for PDF Reader
7414                 * Check the PDF Reference 8.7.1 Transform Methods,
7415                 * Table 8.105 Entries in the UR transform parameters dictionary
7416                 * @param boolean $enable if true enable user's rights on PDF reader
7417                 * @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.
7418                 * @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.
7419                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
7420                 * @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.
7421                 * @access public
7422                 * @author Nicola Asuni
7423                 * @since 2.9.000 (2008-03-26)
7424                 */
7425                 function setUserRights(
7426                                 $enable=true,
7427                                 $document="/FullSave",
7428                                 $annots="/Create/Delete/Modify/Copy/Import/Export",
7429                                 $form="/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate",
7430                                 $signature="/Modify") {
7431                         $this->ur = $enable;
7432                         $this->ur_document = $document;
7433                         $this->ur_annots = $annots;
7434                         $this->ur_form = $form;
7435                         $this->ur_signature = $signature;
7436                 }
7437
7438                 /*
7439                 * Create a new page group.
7440                 * NOTE: call this function before calling AddPage()
7441                 * @access public
7442                 * @since 3.0.000 (2008-03-27)
7443                 */
7444                 function startPageGroup() {
7445                         $this->newpagegroup = true;
7446                 }
7447
7448                 /*
7449                 * Return the current page in the group.
7450                 * @return current page in the group
7451                 * @access public
7452                 * @since 3.0.000 (2008-03-27)
7453                 */
7454                 function getGroupPageNo() {
7455                         return $this->pagegroups[$this->currpagegroup];
7456                 }
7457
7458                 /*
7459                 * Return the alias of the current page group
7460                 * (will be replaced by the total number of pages in this group).
7461                 * @return alias of the current page group
7462                 * @access public
7463                 * @since 3.0.000 (2008-03-27)
7464                 */
7465                 function getPageGroupAlias() {
7466                         return $this->currpagegroup;
7467                 }
7468
7469                 /*
7470                 * Put visibility settings.
7471                 * @access protected
7472                 * @since 3.0.000 (2008-03-27)
7473                 */
7474                 function _putocg() {
7475                         $this->_newobj();
7476                         $this->n_ocg_print = $this->n;
7477                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
7478                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
7479                         $this->_out('endobj');
7480                         $this->_newobj();
7481                         $this->n_ocg_view=$this->n;
7482                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
7483                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
7484                         $this->_out('endobj');
7485                 }
7486
7487                 /*
7488                 * Set the visibility of the successive elements.
7489                 * This can be useful, for instance, to put a background
7490                 * image or color that will show on screen but won't print.
7491                 * @param string $v visibility mode. Legal values are: all, print, screen.
7492                 * @access public
7493                 * @since 3.0.000 (2008-03-27)
7494                 */
7495                 function setVisibility($v) {
7496                         if ($this->openMarkedContent) {
7497                                 // close existing open marked-content
7498                                 $this->_out('EMC');
7499                                 $this->openMarkedContent = false;
7500                         }
7501                         switch($v) {
7502                                 case "print": {
7503                                         $this->_out('/OC /OC1 BDC');
7504                                         $this->openMarkedContent = true;
7505                                         break;
7506                                 }
7507                                 case "screen": {
7508                                         $this->_out('/OC /OC2 BDC');
7509                                         $this->openMarkedContent = true;
7510                                         break;
7511                                 }
7512                                 case "all": {
7513                                         $this->_out('');
7514                                         break;
7515                                 }
7516                                 default: {
7517                                         $this->Error('Incorrect visibility: '.$v);
7518                                         break;
7519                                 }
7520                         }
7521                         $this->visibility = $v;
7522                 }
7523
7524                 /*
7525                 * Add transparency parameters to the current extgstate
7526                 * @param array $params parameters
7527                 * @return the number of extgstates
7528                 * @access protected
7529                 * @since 3.0.000 (2008-03-27)
7530                 */
7531                 function addExtGState($parms) {
7532                         $n = count($this->extgstates) + 1;
7533                         $this->extgstates[$n]['parms'] = $parms;
7534                         return $n;
7535                 }
7536
7537                 /*
7538                 * Add an extgstate
7539                 * @param array $gs extgstate
7540                 * @access protected
7541                 * @since 3.0.000 (2008-03-27)
7542                 */
7543                 function setExtGState($gs) {
7544                         $this->_out(sprintf('/GS%d gs', $gs));
7545                 }
7546
7547                 /*
7548                 * Put extgstates for object transparency
7549                 * @param array $gs extgstate
7550                 * @access protected
7551                 * @since 3.0.000 (2008-03-27)
7552                 */
7553                 function _putextgstates() {
7554                         $ne = count($this->extgstates);
7555                         for ($i = 1; $i <= $ne; $i++) {
7556                                 $this->_newobj();
7557                                 $this->extgstates[$i]['n'] = $this->n;
7558                                 $this->_out('<</Type /ExtGState');
7559                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
7560                                         $this->_out('/'.$k.' '.$v);
7561                                 }
7562                                 $this->_out('>>');
7563                                 $this->_out('endobj');
7564                         }
7565                 }
7566
7567                 /*
7568                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
7569                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
7570                 * @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
7571                 * @access public
7572                 * @since 3.0.000 (2008-03-27)
7573                 */
7574                 function setAlpha($alpha, $bm='Normal') {
7575                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
7576                         $this->setExtGState($gs);
7577                 }
7578
7579                 /*
7580                 * Set the default JPEG compression quality (1-100)
7581                 * @param int $quality JPEG quality, integer between 1 and 100
7582                 * @access public
7583                 * @since 3.0.000 (2008-03-27)
7584                 */
7585                 function setJPEGQuality($quality) {
7586                         if (($quality < 1) OR ($quality > 100)) {
7587                                 $quality = 75;
7588                         }
7589                         $this->jpeg_quality = intval($quality);
7590                 }
7591
7592                 /*
7593                 * Set the default number of columns in a row for HTML tables.
7594                 * @param int $cols number of columns
7595                 * @access public
7596                 * @since 3.0.014 (2008-06-04)
7597                 */
7598                 function setDefaultTableColumns($cols=4) {
7599                         $this->default_table_columns = intval($cols);
7600                 }
7601
7602                 /*
7603                 * Set the height of cell repect font height.
7604                 * @param int $h cell proportion respect font height (typical value = 1.25).
7605                 * @access public
7606                 * @since 3.0.014 (2008-06-04)
7607                 */
7608                 function setCellHeightRatio($h) {
7609                         $this->cell_height_ratio = $h;
7610                 }
7611
7612                 /*
7613                 * return the height of cell repect font height.
7614                 * @access public
7615                 * @since 4.0.012 (2008-07-24)
7616                 */
7617                 function getCellHeightRatio() {
7618                         return $this->cell_height_ratio;
7619                 }
7620
7621                 /*
7622                 * Set the PDF version (check PDF reference for valid values).
7623                 * Default value is 1.t
7624                 * @access public
7625                 * @since 3.1.000 (2008-06-09)
7626                 */
7627                 function setPDFVersion($version="1.7") {
7628                         $this->PDFVersion = $version;
7629                 }
7630
7631                 /*
7632                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
7633                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
7634                 * <ul>
7635                 * <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>
7636                 * <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>
7637                 * <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>
7638                 * <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>
7639                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
7640                 * <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>
7641                 * <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>
7642                 * <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>
7643                 * <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>
7644                 * <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>
7645                 * <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>
7646                 * <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>
7647                 * <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>
7648                 * <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>
7649                 * <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>
7650                 * <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>
7651                 * </ul>
7652                 * @param array $preferences array of options.
7653                 * @author Nicola Asuni
7654                 * @access public
7655                 * @since 3.1.000 (2008-06-09)
7656                 */
7657                 function setViewerPreferences($preferences) {
7658                         $this->viewer_preferences = $preferences;
7659                 }
7660
7661                 /**
7662                 * Paints a linear colour gradient.
7663                 * @param float $x abscissa of the top left corner of the rectangle.
7664                 * @param float $y ordinate of the top left corner of the rectangle.
7665                 * @param float $w width of the rectangle.
7666                 * @param float $h height of the rectangle.
7667                 * @param array $col1 first color (RGB components).
7668                 * @param array $col2 second color (RGB components).
7669                 * @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).
7670                 * @author Andreas Würmser, Nicola Asuni
7671                 * @since 3.1.000 (2008-06-09)
7672                 * @access public
7673                 */
7674                 function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
7675                         $this->Clip($x, $y, $w, $h);
7676                         $this->Gradient(2, $col1, $col2, $coords);
7677                 }
7678
7679                 /**
7680                 * Paints a radial colour gradient.
7681                 * @param float $x abscissa of the top left corner of the rectangle.
7682                 * @param float $y ordinate of the top left corner of the rectangle.
7683                 * @param float $w width of the rectangle.
7684                 * @param float $h height of the rectangle.
7685                 * @param array $col1 first color (RGB components).
7686                 * @param array $col2 second color (RGB components).
7687                 * @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.
7688                 * @author Andreas Würmser, Nicola Asuni
7689                 * @since 3.1.000 (2008-06-09)
7690                 * @access public
7691                 */
7692                 function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
7693                         $this->Clip($x, $y, $w, $h);
7694                         $this->Gradient(3, $col1, $col2, $coords);
7695                 }
7696
7697                 /**
7698                 * Paints a coons patch mesh.
7699                 * @param float $x abscissa of the top left corner of the rectangle.
7700                 * @param float $y ordinate of the top left corner of the rectangle.
7701                 * @param float $w width of the rectangle.
7702                 * @param float $h height of the rectangle.
7703                 * @param array $col1 first color (lower left corner) (RGB components).
7704                 * @param array $col2 second color (lower right corner) (RGB components).
7705                 * @param array $col3 third color (upper right corner) (RGB components).
7706                 * @param array $col4 fourth color (upper left corner) (RGB components).
7707                 * @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 Bézier 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 Bézier 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>
7708                 * @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
7709                 * @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
7710                 * @author Andreas Würmser, Nicola Asuni
7711                 * @since 3.1.000 (2008-06-09)
7712                 * @access public
7713                 */
7714                 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) {
7715                         $this->Clip($x, $y, $w, $h);
7716                         $n = count($this->gradients) + 1;
7717                         $this->gradients[$n]['type'] = 6; //coons patch mesh
7718                         //check the coords array if it is the simple array or the multi patch array
7719                         if (!isset($coords[0]['f'])){
7720                                 //simple array -> convert to multi patch array
7721                                 if (!isset($col1[1])) {
7722                                         $col1[1] = $col1[2] = $col1[0];
7723                                 }
7724                                 if (!isset($col2[1])) {
7725                                         $col2[1] = $col2[2] = $col2[0];
7726                                 }
7727                                 if (!isset($col3[1])) {
7728                                         $col3[1] = $col3[2] = $col3[0];
7729                                 }
7730                                 if (!isset($col4[1])) {
7731                                         $col4[1] = $col4[2] = $col4[0];
7732                                 }
7733                                 $patch_array[0]['f'] = 0;
7734                                 $patch_array[0]['points'] = $coords;
7735                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
7736                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
7737                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
7738                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
7739                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
7740                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
7741                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
7742                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
7743                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
7744                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
7745                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
7746                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
7747                         } else {
7748                                 //multi patch array
7749                                 $patch_array = $coords;
7750                         }
7751                         $bpcd = 65535; //16 BitsPerCoordinate
7752                         //build the data stream
7753                         $this->gradients[$n]['stream'] = "";
7754                         for($i=0; $i < count($patch_array); $i++) {
7755                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
7756                                 for($j=0; $j < count($patch_array[$i]['points']); $j++) {
7757                                         //each point as 16 bit
7758                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j]-$coords_min)/($coords_max-$coords_min))*$bpcd;
7759                                         if ($patch_array[$i]['points'][$j] < 0) {
7760                                                 $patch_array[$i]['points'][$j] = 0;
7761                                         }
7762                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
7763                                                 $patch_array[$i]['points'][$j] = $bpcd;
7764                                         }
7765                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]/256));
7766                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]%256));
7767                                 }
7768                                 for($j=0; $j < count($patch_array[$i]['colors']); $j++) {
7769                                         //each color component as 8 bit
7770                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
7771                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
7772                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
7773                                 }
7774                         }
7775                         //paint the gradient
7776                         $this->_out('/Sh'.$n.' sh');
7777                         //restore previous Graphic State
7778                         $this->_out('Q');
7779                 }
7780
7781                 /**
7782                 * Set a rectangular clipping area.
7783                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
7784                 * @param float $y ordinate of the top left corner of the rectangle.
7785                 * @param float $w width of the rectangle.
7786                 * @param float $h height of the rectangle.
7787                 * @author Andreas Würmser, Nicola Asuni
7788                 * @since 3.1.000 (2008-06-09)
7789                 * @access protected
7790                 */
7791                 function Clip($x, $y, $w, $h){
7792                         if ($this->rtl) {
7793                                 $x = $this->w - $x - $w;
7794                         }
7795                         //save current Graphic State
7796                         $s = 'q';
7797                         //set clipping area
7798                         $s .= sprintf(' %.2f %.2f %.2f %.2f re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
7799                         //set up transformation matrix for gradient
7800                         $s .= sprintf(' %.3f 0 0 %.3f %.3f %.3f cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
7801                         $this->_out($s);
7802                 }
7803
7804                 /**
7805                 * Output gradient.
7806                 * @param int $type type of gradient.
7807                 * @param array $col1 first color (RGB components).
7808                 * @param array $col2 second color (RGB components).
7809                 * @param array $coords array of coordinates.
7810                 * @author Andreas Würmser, Nicola Asuni
7811                 * @since 3.1.000 (2008-06-09)
7812                 * @access protected
7813                 */
7814                 function Gradient($type, $col1, $col2, $coords){
7815                         $n = count($this->gradients) + 1;
7816                         $this->gradients[$n]['type'] = $type;
7817                         if (!isset($col1[1])) {
7818                                 $col1[1]=$col1[2]=$col1[0];
7819                         }
7820                         $this->gradients[$n]['col1'] = sprintf('%.3f %.3f %.3f', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
7821                         if (!isset($col2[1])) {
7822                                 $col2[1] = $col2[2] = $col2[0];
7823                         }
7824                         $this->gradients[$n]['col2'] = sprintf('%.3f %.3f %.3f', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
7825                         $this->gradients[$n]['coords'] = $coords;
7826                         //paint the gradient
7827                         $this->_out('/Sh'.$n.' sh');
7828                         //restore previous Graphic State
7829                         $this->_out('Q');
7830                 }
7831
7832                 /**
7833                 * Output shaders.
7834                 * @author Andreas Würmser, Nicola Asuni
7835                 * @since 3.1.000 (2008-06-09)
7836                 * @access protected
7837                 */
7838                 function _putshaders() {
7839                         foreach($this->gradients as $id => $grad) {
7840                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
7841                                         $this->_newobj();
7842                                         $this->_out('<<');
7843                                         $this->_out('/FunctionType 2');
7844                                         $this->_out('/Domain [0.0 1.0]');
7845                                         $this->_out('/C0 ['.$grad['col1'].']');
7846                                         $this->_out('/C1 ['.$grad['col2'].']');
7847                                         $this->_out('/N 1');
7848                                         $this->_out('>>');
7849                                         $this->_out('endobj');
7850                                         $f1 = $this->n;
7851                                 }
7852                                 $this->_newobj();
7853                                 $this->_out('<<');
7854                                 $this->_out('/ShadingType '.$grad['type']);
7855                                 $this->_out('/ColorSpace /DeviceRGB');
7856                                 if ($grad['type'] == 2) {
7857                                         $this->_out(sprintf('/Coords [%.3f %.3f %.3f %.3f]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
7858                                         $this->_out('/Function '.$f1.' 0 R');
7859                                         $this->_out('/Extend [true true] ');
7860                                         $this->_out('>>');
7861                                 } elseif ($grad['type'] == 3) {
7862                                         //x0, y0, r0, x1, y1, r1
7863                                         //at this this time radius of inner circle is 0
7864                                         $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]));
7865                                         $this->_out('/Function '.$f1.' 0 R');
7866                                         $this->_out('/Extend [true true] ');
7867                                         $this->_out('>>');
7868                                 } elseif ($grad['type'] == 6) {
7869                                         $this->_out('/BitsPerCoordinate 16');
7870                                         $this->_out('/BitsPerComponent 8');
7871                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
7872                                         $this->_out('/BitsPerFlag 8');
7873                                         $this->_out('/Length '.strlen($grad['stream']));
7874                                         $this->_out('>>');
7875                                         $this->_putstream($grad['stream']);
7876                                 }
7877                                 $this->_out('endobj');
7878                                 $this->gradients[$id]['id'] = $this->n;
7879                         }
7880                 }
7881
7882                 /**
7883                 * Output an arc
7884                 * @author Maxime Delorme, Nicola Asuni
7885                 * @since 3.1.000 (2008-06-09)
7886                 * @access protected
7887                 */
7888                 function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
7889                         $h = $this->h;
7890                         $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));
7891                 }
7892
7893                 /**
7894                 * Draw the sector of a circle.
7895                 * It can be used for instance to render pie charts.
7896                 * @param float $xc abscissa of the center.
7897                 * @param float $yc ordinate of the center.
7898                 * @param float $r radius.
7899                 * @param float $a start angle (in degrees).
7900                 * @param float $b end angle (in degrees).
7901                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
7902                 * @param float $cw: indicates whether to go clockwise (default: true).
7903                 * @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.
7904                 * @author Maxime Delorme, Nicola Asuni
7905                 * @since 3.1.000 (2008-06-09)
7906                 * @access public
7907                 */
7908                 function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
7909                         if ($this->rtl) {
7910                                 $xc = $this->w - $xc - $w;
7911                         }
7912                         if ($cw) {
7913                                 $d = $b;
7914                                 $b = $o - $a;
7915                                 $a = $o - $d;
7916                         } else {
7917                                 $b += $o;
7918                                 $a += $o;
7919                         }
7920                         $a = ($a % 360) + 360;
7921                         $b = ($b % 360) + 360;
7922                         if ($a > $b) {
7923                                 $b +=360;
7924                         }
7925                         $b = $b / 360 * 2 * M_PI;
7926                         $a = $a / 360 * 2 * M_PI;
7927                         $d = $b - $a;
7928                         if ($d == 0 ) {
7929                                 $d = 2 * M_PI;
7930                         }
7931                         $k = $this->k;
7932                         $hp = $this->h;
7933                         if ($style=='F') {
7934                                 $op = 'f';
7935                         } elseif ($style=='FD' or $style=='DF') {
7936                                 $op = 'b';
7937                         } else {
7938                                 $op = 's';
7939                         }
7940                         if (sin($d/2)) {
7941                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
7942                         }
7943                         //first put the center
7944                         $this->_out(sprintf('%.2f %.2f m', ($xc)*$k, ($hp-$yc)*$k));
7945                         //put the first point
7946                         $this->_out(sprintf('%.2f %.2f l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
7947                         //draw the arc
7948                         if ($d < (M_PI/2)) {
7949                                 $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));
7950                         } else {
7951                                 $b = $a + $d/4;
7952                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
7953                                 $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));
7954                                 $a = $b;
7955                                 $b = $a + $d/4;
7956                                 $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));
7957                                 $a = $b;
7958                                 $b = $a + $d/4;
7959                                 $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) );
7960                                 $a = $b;
7961                                 $b = $a + $d/4;
7962                                 $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));
7963                         }
7964                         //terminate drawing
7965                         $this->_out($op);
7966                 }
7967
7968                 /**
7969                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
7970                 * Only vector drawing is supported, not text or bitmap.
7971                 * 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).
7972                 * @param string $file Name of the file containing the image.
7973                 * @param float $x Abscissa of the upper-left corner.
7974                 * @param float $y Ordinate of the upper-left corner.
7975                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7976                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7977                 * @param mixed $link URL or identifier returned by AddLink().
7978                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
7979                 * @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>
7980                 * @author Valentin Schmidt, Nicola Asuni
7981                 * @since 3.1.000 (2008-06-09)
7982                 * @access public
7983                 */
7984                 function ImageEps($file, $x, $y, $w=0, $h=0, $link='', $useBoundingBox=true, $align='') {
7985                         if ($this->rtl) {
7986                                 $x = ($this->w - $x - $w);
7987                         }
7988                         $data = file_get_contents($file);
7989                         if ($data === false) {
7990                                 $this->Error('EPS file not found: '.$file);
7991                         }
7992                         $regs = array();
7993                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
7994                         preg_match ('/%%Creator:([^\r\n]+)/', $data, $regs); # find Creator
7995                         if (count($regs) > 1) {
7996                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
7997                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
7998                                         $versexp = explode(' ', $version_str);
7999                                         $version = (float)array_pop($versexp);
8000                                         if ($version >= 9) {
8001                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
8002                                         }
8003                                 }
8004                         }
8005                         // strip binary bytes in front of PS-header
8006                         $start = strpos($data, '%!PS-Adobe');
8007                         if ($start > 0) {
8008                                 $data = substr($data, $start);
8009                         }
8010                         // find BoundingBox params
8011                         preg_match ("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
8012                         if (count($regs) > 1) {
8013                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
8014                         } else {
8015                                 $this->Error('No BoundingBox found in EPS file: '.$file);
8016                         }
8017                         $start = strpos($data, '%%EndSetup');
8018                         if ($start === false) {
8019                                 $start = strpos($data, '%%EndProlog');
8020                         }
8021                         if ($start === false) {
8022                                 $start = strpos($data, '%%BoundingBox');
8023                         }
8024                         $data = substr($data, $start);
8025                         $end = strpos($data, '%%PageTrailer');
8026                         if ($end===false) {
8027                                 $end = strpos($data, 'showpage');
8028                         }
8029                         if ($end) {
8030                                 $data = substr($data, 0, $end);
8031                         }
8032                         // save the current graphic state
8033                         $this->_out('q');
8034                         $k = $this->k;
8035                         if ($useBoundingBox){
8036                                 $dx = $x * $k - $x1;
8037                                 $dy = $y * $k - $y1;
8038                         } else {
8039                                 $dx = $x * $k;
8040                                 $dy = $y * $k;
8041                         }
8042                         // translate
8043                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy+($this->hPt - 2*$y*$k - ($y2-$y1))));
8044                         if ($w > 0) {
8045                                 $scale_x = $w/(($x2-$x1)/$k);
8046                                 if ($h > 0) {
8047                                         $scale_y = $h/(($y2-$y1)/$k);
8048                                 } else {
8049                                         $scale_y = $scale_x;
8050                                         $h = ($y2-$y1)/$k * $scale_y;
8051                                 }
8052                         } else {
8053                                 if ($h > 0) {
8054                                         $scale_y = $h/(($y2-$y1)/$k);
8055                                         $scale_x = $scale_y;
8056                                         $w = ($x2-$x1)/$k * $scale_x;
8057                                 } else {
8058                                         $w = ($x2 - $x1) / $k;
8059                                         $h = ($y2 - $y1) / $k;
8060                                 }
8061                         }
8062                         // scale
8063                         if (isset($scale_x)) {
8064                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1*(1-$scale_x), $y2*(1-$scale_y)));
8065                         }
8066                         // handle pc/unix/mac line endings
8067                         $lines = split ("\r\n|[\r\n]", $data);
8068                         $u=0;
8069                         $cnt = count($lines);
8070                         for ($i=0; $i < $cnt; $i++) {
8071                                 $line = $lines[$i];
8072                                 if (($line == '') OR ($line{0} == '%')) {
8073                                         continue;
8074                                 }
8075                                 $len = strlen($line);
8076                                 $chunks = explode(' ', $line);
8077                                 $cmd = array_pop($chunks);
8078                                 // RGB
8079                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
8080                                         $b = array_pop($chunks);
8081                                         $g = array_pop($chunks);
8082                                         $r = array_pop($chunks);
8083                                         $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!
8084                                         continue;
8085                                 }
8086                                 switch ($cmd) {
8087                                         case 'm':
8088                                         case 'l':
8089                                         case 'v':
8090                                         case 'y':
8091                                         case 'c':
8092                                         case 'k':
8093                                         case 'K':
8094                                         case 'g':
8095                                         case 'G':
8096                                         case 's':
8097                                         case 'S':
8098                                         case 'J':
8099                                         case 'j':
8100                                         case 'w':
8101                                         case 'M':
8102                                         case 'd':
8103                                         case 'n':
8104                                         case 'v': {
8105                                                 $this->_out($line);
8106                                                 break;
8107                                         }
8108                                         case 'x': {// custom fill color
8109                                                 list($c,$m,$y,$k) = $chunks;
8110                                                 $this->_out("$c $m $y $k k");
8111                                                 break;
8112                                         }
8113                                         case 'X': { // custom stroke color
8114                                                 list($c,$m,$y,$k) = $chunks;
8115                                                 $this->_out("$c $m $y $k K");
8116                                                 break;
8117                                         }
8118                                         case 'Y':
8119                                         case 'N':
8120                                         case 'V':
8121                                         case 'L':
8122                                         case 'C': {
8123                                                 $line{$len-1} = strtolower($cmd);
8124                                                 $this->_out($line);
8125                                                 break;
8126                                         }
8127                                         case 'b':
8128                                         case 'B': {
8129                                                 $this->_out($cmd . '*');
8130                                                 break;
8131                                         }
8132                                         case 'f':
8133                                         case 'F': {
8134                                                 if ($u > 0) {
8135                                                         $isU = false;
8136                                                         $max = min($i+5, $cnt);
8137                                                         for ($j=$i+1; $j < $max; $j++)
8138                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
8139                                                         if ($isU) {
8140                                                                 $this->_out("f*");
8141                                                         }
8142                                                 } else {
8143                                                         $this->_out("f*");
8144                                                 }
8145                                                 break;
8146                                         }
8147                                         case '*u': {
8148                                                 $u++;
8149                                                 break;
8150                                         }
8151                                         case '*U': {
8152                                                 $u--;
8153                                                 break;
8154                                         }
8155                                 }
8156                         }
8157                         // restore previous graphic state
8158                         $this->_out('Q');
8159                         if ($link) {
8160                                 $this->Link($x, $y, $w, $h, $link);
8161                         }
8162                         // set bottomcoordinates
8163                         $this->img_rb_y = $y + $h;
8164                         if ($this->rtl) {
8165                                 // set left side coordinate
8166                                 $this->img_rb_x = ($this->w - $x - $w);
8167                         } else {
8168                                 // set right side coordinate
8169                                 $this->img_rb_x = $x + $w;
8170                         }
8171                         // set pointer to align the successive text/objects
8172                         switch($align) {
8173                                 case 'T':{
8174                                         $this->y = $y;
8175                                         $this->x = $this->img_rb_x;
8176                                         break;
8177                                 }
8178                                 case 'M':{
8179                                         $this->y = $y + round($h/2);
8180                                         $this->x = $this->img_rb_x;
8181                                         break;
8182                                 }
8183                                 case 'B':{
8184                                         $this->y = $this->img_rb_y;
8185                                         $this->x = $this->img_rb_x;
8186                                         break;
8187                                 }
8188                                 case 'N':{
8189                                         $this->SetY($this->img_rb_y);
8190                                         break;
8191                                 }
8192                                 default:{
8193                                         break;
8194                                 }
8195                         }
8196                 }
8197
8198                 /**
8199                  * Set document barcode.
8200                  * @param string $bc barcode
8201                  */
8202                 function setBarcode($bc="") {
8203                         $this->barcode = $bc;
8204                 }
8205
8206                 /**
8207                  * Get current barcode.
8208                  * @return string
8209                  * @since 4.0.012 (2008-07-24)
8210                  */
8211                 function getBarcode() {
8212                         return $this->barcode;
8213                 }
8214
8215                 /**
8216                  * Print Barcode.
8217                  * @param string $code code to print
8218                  * @param string $type type of barcode.
8219                  * @param int $x x position in user units
8220                  * @param int $y y position in user units
8221                  * @param int $w width in user units
8222                  * @param int $h height position in user units
8223                  * @param float $xres width of the smallest bar in user units
8224                  * @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>
8225                  * @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>
8226                  * @author Nicola Asuni
8227                  * @since 3.1.000 (2008-06-09)
8228                  * @access public
8229                  */
8230                 function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
8231                         if (empty($code)) {
8232                                 return;
8233                         }
8234                         $barcodeobj = new TCPDFbarcode($code, $type);
8235                         $arrcode = $barcodeobj->getBarcodeArray();
8236                         if ($arrcode === false) {
8237                                 $this->Error('Error in barcode string');
8238                         }
8239                         // set default values
8240                         if (!isset($style["position"])) {
8241                                 if ($this->rtl) {
8242                                         $style["position"] = "R";
8243                                 } else {
8244                                         $style["position"] = "L";
8245                                 }
8246                         }
8247                         if (!isset($style["padding"])) {
8248                                 $style["padding"] = 0;
8249                         }
8250                         if (!isset($style["fgcolor"])) {
8251                                 $style["fgcolor"] = array(0,0,0); // default black
8252                         }
8253                         if (!isset($style["bgcolor"])) {
8254                                 $style["bgcolor"] = false; // default transparent
8255                         }
8256                         if (!isset($style["border"])) {
8257                                 $style["border"] = false;
8258                         }
8259                         if (!isset($style["text"])) {
8260                                 $style["text"] = false;
8261                                 $fontsize = 0;
8262                         }
8263                         if ($style["text"] AND isset($style["font"])) {
8264                                 $prevFontFamily = $this->FontFamily;
8265                                 $prevFontStyle = $this->FontStyle;
8266                                 $prevFontSizePt = $this->FontSizePt;
8267                                 if (isset($style["fontsize"])) {
8268                                         $fontsize = $style["fontsize"];
8269                                 } else {
8270                                         $fontsize = 0;
8271                                 }
8272                                 $this->SetFont($style["font"], '', $fontsize);
8273                         }
8274                         if (!isset($style["stretchtext"])) {
8275                                 $style["stretchtext"] = 4;
8276                         }
8277                         // set foreground color
8278                         $prevDrawColor = $this->DrawColor;
8279                         $prevTextColor = $this->TextColor;
8280                         $this->SetDrawColorArray($style["fgcolor"]);
8281                         $this->SetTextColorArray($style["fgcolor"]);
8282                         if (empty($w) OR ($w <= 0)) {
8283                                 if ($this->rtl) {
8284                                         $w = $this->x - $this->lMargin;
8285                                 } else {
8286                                         $w = $this->w - $this->rMargin - $this->x;
8287                                 }
8288                         }
8289                         if (empty($x)) {
8290                                 $x = $this->GetX();
8291                         }
8292                         if ($this->rtl) {
8293                                 $x = $this->w - $x;
8294                         }
8295                         if (empty($y)) {
8296                                 $y = $this->GetY();
8297                         }
8298                         if (empty($xres)) {
8299                                 $xres = 0.4;
8300                         }
8301                         $fbw = ($arrcode["maxw"] * $xres) + (2 * $style["padding"]);
8302                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style["padding"]);
8303                         if (empty($h)) {
8304                                 $h = 10 + $extraspace;
8305                         }
8306                         if ((($y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
8307                                 //Automatic page break
8308                                 $x = $this->x;
8309                                 $ws = $this->ws;
8310                                 if ($ws > 0) {
8311                                         $this->ws = 0;
8312                                         $this->_out('0 Tw');
8313                                 }
8314                                 $this->AddPage($this->CurOrientation);
8315                                 if ($ws > 0) {
8316                                         $this->ws = $ws;
8317                                         $this->_out(sprintf('%.3f Tw',$ws * $k));
8318                                 }
8319                                 $this->x = $x;
8320                                 $y = $this->y;
8321                         }
8322                         // maximum bar heigth
8323                         $barh = $h - $extraspace;
8324                         switch ($style["position"]) {
8325                                 case "L": { // left
8326                                         if ($this->rtl) {
8327                                                 $xpos = $x - $w;
8328                                         } else {
8329                                                 $xpos = $x;
8330                                         }
8331                                         break;
8332                                 }
8333                                 case "C": { // center
8334                                         $xdiff = (($w - $fbw) / 2);
8335                                         if ($this->rtl) {
8336                                                 $xpos = $x - $w + $xdiff;
8337                                         } else {
8338                                                 $xpos = $x + $xdiff;
8339                                         }
8340                                         break;
8341                                 }
8342                                 case "R": { // right
8343                                         if ($this->rtl) {
8344                                                 $xpos = $x - $fbw;
8345                                         } else {
8346                                                 $xpos = $x + $w - $fbw;
8347                                         }
8348                                         break;
8349                                 }
8350                                 case "S": { // stretch
8351                                         $fbw = $w;
8352                                         $xres = ($w - (2 * $style["padding"])) / $arrcode["maxw"];
8353                                         if ($this->rtl) {
8354                                                 $xpos = $x - $w;
8355                                         } else {
8356                                                 $xpos = $x;
8357                                         }
8358                                         break;
8359                                 }
8360                         }
8361                         $xpos_rect = $xpos;
8362                         $xpos = $xpos_rect + $style["padding"];
8363                         $xpos_text = $xpos;
8364                         // barcode is always printed in LTR direction
8365                         $tempRTL = $this->rtl;
8366                         $this->rtl = false;
8367                         // print background color
8368                         if ($style["bgcolor"]) {
8369                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style["bgcolor"]);
8370                         } elseif ($style["border"]) {
8371                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
8372                         }
8373                         // print bars
8374                         if ($arrcode !== false) {
8375                                 foreach ($arrcode["bcode"] as $k => $v) {
8376                                         $bw = ($v["w"] * $xres);
8377                                         if ($v["t"]) {
8378                                                 // braw a vertical bar
8379                                                 $ypos = $y + $style["padding"] + ($v["p"] * $barh / $arrcode["maxh"]);
8380                                                 $this->Rect($xpos, $ypos, $bw, ($v["h"] * $barh  / $arrcode["maxh"]), 'DF', array("L"=>0,"T"=>0,"R"=>0,"B"=>0), $style["fgcolor"]);
8381                                         }
8382                                         $xpos += $bw;
8383                                 }
8384                         }
8385                         // print text
8386                         if ($style["text"]) {
8387                                 // print text
8388                                 $this->x = $xpos_text;
8389                                 $this->y = $y + $style["padding"] + $barh;
8390                                 $this->Cell(($arrcode["maxw"] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style["stretchtext"]);
8391                         }
8392                         // restore original direction
8393                         $this->rtl = $tempRTL;
8394                         // restore previous font
8395                         if ($style["text"] AND isset($style["font"])) {
8396                                 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
8397                         }
8398                         // restore colors
8399                         $this->DrawColor = $prevDrawColor;
8400                         $this->TextColor = $prevTextColor;
8401                         // set bottomcoordinates
8402                         $this->img_rb_y = $y + $h;
8403                         if ($this->rtl) {
8404                                 // set left side coordinate
8405                                 $this->img_rb_x = ($this->w - $x - $w);
8406                         } else {
8407                                 // set right side coordinate
8408                                 $this->img_rb_x = $x + $w;
8409                         }
8410                         // set pointer to align the successive text/objects
8411                         switch($align) {
8412                                 case 'T':{
8413                                         $this->y = $y;
8414                                         $this->x = $this->img_rb_x;
8415                                         break;
8416                                 }
8417                                 case 'M':{
8418                                         $this->y = $y + round($h/2);
8419                                         $this->x = $this->img_rb_x;
8420                                         break;
8421                                 }
8422                                 case 'B':{
8423                                         $this->y = $this->img_rb_y;
8424                                         $this->x = $this->img_rb_x;
8425                                         break;
8426                                 }
8427                                 case 'N':{
8428                                         $this->SetY($this->img_rb_y);
8429                                         break;
8430                                 }
8431                                 default:{
8432                                         break;
8433                                 }
8434                         }
8435                 }
8436
8437                 /**
8438                  * This function is DEPRECATED, please use the new write1DBarcode() function.
8439                  * @param int $x x position in user units
8440                  * @param int $y y position in user units
8441                  * @param int $w width in user units
8442                  * @param int $h height position in user units
8443                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
8444                  * @param string $style barcode style
8445                  * @param string $font font for text
8446                  * @param int $xres x resolution
8447                  * @param string $code code to print
8448                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
8449                  * @see write1DBarcode()
8450                  */
8451                 function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
8452                         // convert old settings for the new write1DBarcode() function.
8453                         $xres = 1 / $xres;
8454                         $newstyle = array(
8455                                 "position" => "L",
8456                                 "border" => false,
8457                                 "padding" => 0,
8458                                 "fgcolor" => array(0,0,0),
8459                                 "bgcolor" => false,
8460                                 "text" => true,
8461                                 "font" => $font,
8462                                 "fontsize" => 8,
8463                                 "stretchtext" => 4
8464                         );
8465                         if ($style & 1) {
8466                                 $newstyle["border"] = true;
8467                         }
8468                         if ($style & 2) {
8469                                 $newstyle["bgcolor"] = false;
8470                         }
8471                         if ($style & 4) {
8472                                 $newstyle["position"] = "C";
8473                         } elseif ($style & 8) {
8474                                 $newstyle["position"] = "L";
8475                         } elseif ($style & 16) {
8476                                 $newstyle["position"] = "R";
8477                         }
8478                         if ($style & 128) {
8479                                 $newstyle["text"] = true;
8480                         }
8481                         if ($style & 256) {
8482                                 $newstyle["stretchtext"] = 4;
8483                         }
8484                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
8485                 }
8486
8487                 /**
8488                  * Returns an array containing current margins:
8489                  * <ul>
8490                                 <li>$ret['left'] = left  margin</li>
8491                                 <li>$ret['right'] = right margin</li>
8492                                 <li>$ret['top'] = top margin</li>
8493                                 <li>$ret['bottom'] = bottom margin</li>
8494                                 <li>$ret['header'] = header margin</li>
8495                                 <li>$ret['footer'] = footer margin</li>
8496                                 <li>$ret['cell'] = cell margin</li>
8497                  * </ul>
8498                  * @return array containing all margins measures
8499                  * @since 3.2.000 (2008-06-23)
8500                  */
8501                 function getMargins() {
8502                         $ret = array(
8503                                 'left' => $this->lMargin,
8504                                 'right' => $this->rMargin,
8505                                 'top' => $this->tMargin,
8506                                 'bottom' => $this->bMargin,
8507                                 'header' => $this->header_margin,
8508                                 'footer' => $this->footer_margin,
8509                                 'cell' => $this->cMargin,
8510                         );
8511                         return $ret;
8512                 }
8513
8514                 /**
8515                  * Returns an array containing original margins:
8516                  * <ul>
8517                                 <li>$ret['left'] = left  margin</li>
8518                                 <li>$ret['right'] = right margin</li>
8519                  * </ul>
8520                  * @return array containing all margins measures
8521                  * @since 4.0.012 (2008-07-24)
8522                  */
8523                 function getOriginalMargins() {
8524                         $ret = array(
8525                                 'left' => $this->original_lMargin,
8526                                 'right' => $this->original_rMargin
8527                         );
8528                         return $ret;
8529                 }
8530
8531                 /**
8532                  * Returns the current font size.
8533                  * @return current font size
8534                  * @since 3.2.000 (2008-06-23)
8535                  */
8536                 function getFontSize() {
8537                         return $this->FontSize;
8538                 }
8539
8540                 /**
8541                  * Returns the current font size in points unit.
8542                  * @return current font size in points unit
8543                  * @since 3.2.000 (2008-06-23)
8544                  */
8545                 function getFontSizePt() {
8546                         return $this->FontSizePt;
8547                 }
8548
8549                 /**
8550                  * Prints a cell (rectangular area) with optional borders, background color and html text string.
8551                  * 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 />
8552                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
8553                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
8554                  * @param float $h Cell minimum height. The cell extends automatically if needed.
8555                  * @param float $x upper-left corner X coordinate
8556                  * @param float $y upper-left corner Y coordinate
8557                  * @param string $html html text to print. Default value: empty string.
8558                  * @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>
8559                  * @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>
8560         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
8561                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
8562                  * @param boolean $reseth if true reset the last cell height (default true).
8563                  * @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>
8564                  * @uses MultiCell()
8565                  * @see Multicell(), writeHTML()
8566                  */
8567                 function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='') {
8568                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true);
8569                 }
8570
8571                 /**
8572                  * Returns the HTML DOM array.
8573                  * <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>
8574                  * @param string $html html code
8575                  * @return array
8576                  * @since 3.2.000 (2008-06-20)
8577                  */
8578                 function getHtmlDomArray($html) {
8579                         // remove all unsupported tags (the line below lists all supported tags)
8580                         $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>");
8581                         //replace carriage returns, newlines and tabs
8582                         $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ", "\\" => "\\\\");
8583                         $html = strtr($html, $repTable);
8584                         // remove extra spaces from tables
8585                         $html = preg_replace('/[\s]*<\/table>[\s]*/', '</table>', $html);
8586                         $html = preg_replace('/[\s]*<\/tr>[\s]*/', '</tr>', $html);
8587                         $html = preg_replace('/[\s]*<tr/', '<tr', $html);
8588                         $html = preg_replace('/[\s]*<\/th>[\s]*/', '</th>', $html);
8589                         $html = preg_replace('/[\s]*<th/', '<th', $html);
8590                         $html = preg_replace('/[\s]*<\/td>[\s]*/', '</td>', $html);
8591                         $html = preg_replace('/[\s]*<td/', '<td', $html);
8592                         // pattern for generic tag
8593                         $tagpattern = '/(<[^>]+>)/Uu';
8594                         // explodes the string
8595                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
8596                         // count elements
8597                         $maxel = count($a);
8598                         $key = 0;
8599                         // create an array of elements
8600                         $dom = array();
8601                         $dom[$key] = array();
8602                         // set first void element
8603                         $dom[$key]['tag'] = false;
8604                         $dom[$key]['value'] = "";
8605                         $dom[$key]['parent'] = 0;
8606                         $dom[$key]['fontname'] = $this->FontFamily;
8607                         $dom[$key]['fontstyle'] = $this->FontStyle;
8608                         $dom[$key]['fontsize'] = $this->FontSizePt;
8609                         $dom[$key]['bgcolor'] = false;
8610                         $dom[$key]['fgcolor'] = $this->fgcolor;
8611                         $dom[$key]['align'] = '';
8612                         $key++;
8613                         $level = array();
8614                         array_push($level, 0); // root
8615                         while ($key <= $maxel) {
8616                                 if ($key > 0) {
8617                                         $dom[$key] = array();
8618                                 }
8619                                 $element = $a[($key-1)];
8620                                 if (preg_match($tagpattern, $element)) {
8621                                         // html tag
8622                                         $dom[$key]['tag'] = true;
8623                                         $element = substr($element, 1, -1);
8624                                         // get tag name
8625                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
8626                                         $dom[$key]['value'] = strtolower($tag[1]);
8627                                         if ($element{0} == '/') {
8628                                                 // closing html tag
8629                                                 $dom[$key]['opening'] = false;
8630                                                 $dom[$key]['parent'] = end($level);
8631                                                 array_pop($level);
8632                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
8633                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
8634                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
8635                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
8636                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
8637                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
8638                                                 // set the number of columns in table tag
8639                                                 if (($dom[$key]['value'] == "tr") AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
8640                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
8641                                                 }
8642                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
8643                                                         $dom[($dom[$key]['parent'])]['content'] = "";
8644                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; $i++) {
8645                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[($i-1)];
8646                                                         }
8647                                                         $key = $i;
8648                                                 }
8649                                         } else {
8650                                                 // opening html tag
8651                                                 $dom[$key]['opening'] = true;
8652                                                 $dom[$key]['parent'] = end($level);
8653                                                 if (substr($element, -1, 1) != '/') {
8654                                                         // not self-closing tag
8655                                                         array_push($level, $key);
8656                                                         $dom[$key]['self'] = false;
8657                                                 } else {
8658                                                         $dom[$key]['self'] = true;
8659                                                 }
8660                                                 // copy some values from parent
8661                                                 if ($key > 0) {
8662                                                         $dom[$key]['fontname'] = $dom[($dom[$key]['parent'])]['fontname'];
8663                                                         $dom[$key]['fontstyle'] = $dom[($dom[$key]['parent'])]['fontstyle'];
8664                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'];
8665                                                         $dom[$key]['bgcolor'] = $dom[($dom[$key]['parent'])]['bgcolor'];
8666                                                         $dom[$key]['fgcolor'] = $dom[($dom[$key]['parent'])]['fgcolor'];
8667                                                         $dom[$key]['align'] = $dom[($dom[$key]['parent'])]['align'];
8668                                                 }
8669                                                 // get attributes
8670                                                 preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER);
8671                                                 $dom[$key]['attribute'] = array(); // reset attribute array
8672                                                 while (list($id, $name) = each($attr_array[1])) {
8673                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
8674                                                 }
8675                                                 // split style attributes
8676                                                 if (isset($dom[$key]['attribute']['style'])) {
8677                                                         // get style attributes
8678                                                         preg_match_all('/([^:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
8679                                                         $dom[$key]['style'] = array(); // reset style attribute array
8680                                                         while (list($id, $name) = each($style_array[1])) {
8681                                                                 $dom[$key]['style'][strtolower($name)] = $style_array[2][$id];
8682                                                         }
8683                                                         // --- get some style attributes ---
8684                                                         if (isset($dom[$key]['style']['font-family'])) {
8685                                                                 // font family
8686                                                                 if (isset($dom[$key]['style']['font-family'])) {
8687                                                                         $fontslist = split(",", strtolower($dom[$key]['style']['font-family']));
8688                                                                         foreach($fontslist as $font) {
8689                                                                                 $font = trim(strtolower($font));
8690                                                                                 if (in_array($font, $this->fontlist)){
8691                                                                                         $dom[$key]['fontname'] = $font;
8692                                                                                         break;
8693                                                                                 }
8694                                                                         }
8695                                                                 }
8696                                                         }
8697                                                         // font size
8698                                                         if (isset($dom[$key]['style']['font-size'])) {
8699                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['style']['font-size']);
8700                                                         }
8701                                                         // font style
8702                                                         $dom[$key]['fontstyle'] = "";
8703                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == "b")) {
8704                                                                 $dom[$key]['fontstyle'] .= "B";
8705                                                         }
8706                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == "i")) {
8707                                                                 $dom[$key]['fontstyle'] .= "I";
8708                                                         }
8709                                                         // check for width attribute
8710                                                         if (isset($dom[$key]['style']['width'])) {
8711                                                                 $dom[$key]['width'] = intval($dom[$key]['style']['width']);
8712                                                         }
8713                                                         // check for height attribute
8714                                                         if (isset($dom[$key]['style']['height'])) {
8715                                                                 $dom[$key]['height'] = intval($dom[$key]['style']['height']);
8716                                                         }
8717                                                         // check for text alignment
8718                                                         if (isset($dom[$key]['style']['text-align'])) {
8719                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
8720                                                         }
8721                                                 }
8722                                                 // check for font tag
8723                                                 if ($dom[$key]['value'] == "font") {
8724                                                         // font family
8725                                                         if (isset($dom[$key]['attribute']['face'])) {
8726                                                                 $fontslist = split(",", strtolower($dom[$key]['attribute']['face']));
8727                                                                 foreach($fontslist as $font) {
8728                                                                         $font = trim(strtolower($font));
8729                                                                         if (in_array($font, $this->fontlist)){
8730                                                                                 $dom[$key]['fontname'] = $font;
8731                                                                                 break;
8732                                                                         }
8733                                                                 }
8734                                                         }
8735                                                         // font size
8736                                                         if (isset($dom[$key]['attribute']['size'])) {
8737                                                                 if ($key > 0) {
8738                                                                         if ($dom[$key]['attribute']['size']{0} == "+") {
8739                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
8740                                                                         } elseif ($dom[$key]['attribute']['size']{0} == "-") {
8741                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
8742                                                                         } else {
8743                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
8744                                                                         }
8745                                                                 } else {
8746                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
8747                                                                 }
8748                                                         }
8749                                                 }
8750                                                 if (($dom[$key]['value'] == "ul") OR ($dom[$key]['value'] == "ol") OR ($dom[$key]['value'] == "dl")) {
8751                                                         // force natural alignment for lists
8752                                                         if ($this->rtl) {
8753                                                                 $dom[$key]['align'] = "R";
8754                                                         } else {
8755                                                                 $dom[$key]['align'] = "L";
8756                                                         }
8757                                                 }
8758                                                 if (($dom[$key]['value'] == "small") OR ($dom[$key]['value'] == "sup") OR ($dom[$key]['value'] == "sub")) {
8759                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
8760                                                 }
8761                                                 if (($dom[$key]['value'] == "strong") OR ($dom[$key]['value'] == "b")) {
8762                                                         $dom[$key]['fontstyle'] .= "B";
8763                                                 }
8764                                                 if (($dom[$key]['value'] == "em") OR ($dom[$key]['value'] == "i")) {
8765                                                         $dom[$key]['fontstyle'] .= "I";
8766                                                 }
8767                                                 if (($dom[$key]['value']{0} == "h") AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
8768                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
8769                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
8770                                                         $dom[$key]['fontstyle'] .= "B";
8771                                                 }
8772                                                 if (($dom[$key]['value'] == "table")) {
8773                                                         $dom[$key]['rows'] = 0; // number of rows
8774                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
8775                                                 }
8776                                                 if (($dom[$key]['value'] == "tr")) {
8777                                                         $dom[$key]['cols'] = 0;
8778                                                         // store the number of rows on table element
8779                                                         $dom[($dom[$key]['parent'])]['rows']++;
8780                                                         // store the TR elements IDs on table element
8781                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
8782                                                 }
8783                                                 if (($dom[$key]['value'] == "th") OR ($dom[$key]['value'] == "td")) {
8784                                                         if (isset($dom[$key]['attribute']['colspan'])) {
8785                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
8786                                                         } else {
8787                                                                 $colspan = 1;
8788                                                         }
8789                                                         $dom[$key]['attribute']['colspan'] = $colspan;
8790                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
8791                                                 }
8792                                                 // set foreground color attribute
8793                                                 if (isset($dom[$key]['attribute']['color']) AND (!empty($dom[$key]['attribute']['color']))) {
8794                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
8795                                                 }
8796                                                 // set background color attribute
8797                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!empty($dom[$key]['attribute']['bgcolor']))) {
8798                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
8799                                                 }
8800                                                 // check for width attribute
8801                                                 if (isset($dom[$key]['attribute']['width'])) {
8802                                                         $dom[$key]['width'] = intval($dom[$key]['attribute']['width']);
8803                                                 }
8804                                                 // check for height attribute
8805                                                 if (isset($dom[$key]['attribute']['height'])) {
8806                                                         $dom[$key]['height'] = intval($dom[$key]['attribute']['height']);
8807                                                 }
8808                                                 // check for text alignment
8809                                                 if (isset($dom[$key]['attribute']['align']) AND (!empty($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
8810                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
8811                                                 }
8812                                         } // end opening tag
8813                                 } else {
8814                                         // text
8815                                         $dom[$key]['tag'] = false;
8816                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
8817                                         $dom[$key]['parent'] = end($level);
8818                                         // calculate text width
8819                                         //$dom[$key]['width'] = $this->GetStringWidth($dom[$key]['value'], $dom[($dom[$key]['parent'])]['fontname'], $dom[($dom[$key]['parent'])]['fontstyle'], $dom[($dom[$key]['parent'])]['fontsize']);
8820                                 }
8821                                 $key++;
8822                         }
8823                         return $dom;
8824                 }
8825
8826                 /**
8827                  * Allows to preserve some HTML formatting (limited support).<br />
8828                  * 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,
8829                  * @param string $html text to display
8830                  * @param boolean $ln if true add a new line after text (default = true)
8831                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
8832                  * @param boolean $reseth if true reset the last cell height (default false).
8833                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
8834                  * @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>
8835                  */
8836                 function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
8837                         // store current values
8838                         $prevlMargin = $this->lMargin;
8839                         $prevrMargin = $this->rMargin;
8840                         $prevcMargin = $this->cMargin;
8841                         $prevFontFamily = $this->FontFamily;
8842                         $prevFontStyle = $this->FontStyle;
8843                         $prevFontSizePt = $this->FontSizePt;
8844                         $curfontname = $prevFontFamily;
8845                         $curfontstyle = $prevFontStyle;
8846                         $curfontsize = $prevFontSizePt;
8847                         $prevbgcolor = $this->bgcolor;
8848                         $prevfgcolor = $this->fgcolor;
8849                         $this->newline = true;
8850                         $startlinepage = $this->page;
8851                         if (isset($this->footerpos[$this->page])) {
8852                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
8853                                 $startlinepos = $this->footerpos[$this->page];
8854                         } else {
8855                                 $startlinepos = strlen($this->pages[$this->page]);
8856                         }
8857                         $lalign = $align;
8858                         $plalign = $align;
8859                         if ($this->rtl) {
8860                                 $w = $this->x - $this->lMargin;
8861                         } else {
8862                                 $w = $this->w - $this->rMargin - $this->x;
8863                         }
8864                         $w -= (2 * $this->cMargin);
8865                         if ($cell) {
8866                                 if ($this->rtl) {
8867                                         $this->x -= $this->cMargin;
8868                                 } else {
8869                                         $this->x += $this->cMargin;
8870                                 }
8871                         }
8872                         $this->listindent = $this->GetStringWidth("0000");
8873                         $this->listnum = 0;
8874                         if ((empty($this->lasth))OR ($reseth)) {
8875                                 //set row height
8876                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
8877                         }
8878                         $dom = $this->getHtmlDomArray($html);
8879                         $maxel = count($dom);
8880                         $key = 0;
8881                         while ($key < $maxel) {
8882                                 if ($dom[$key]['tag'] OR ($key == 0)) {
8883                                         if (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
8884                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : '';
8885                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : '';
8886                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : '';
8887                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
8888                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
8889                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
8890                                                         $curfontname = $fontname;
8891                                                         $curfontstyle = $fontstyle;
8892                                                         $curfontsize = $fontsize;
8893                                                 }
8894                                         }
8895                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
8896                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
8897                                                 $wfill = true;
8898                                         } else {
8899                                                 $wfill = $fill | false;
8900                                         }
8901                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
8902                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
8903                                         }
8904                                         if (isset($dom[$key]['align'])) {
8905                                                 $lalign = $dom[$key]['align'];
8906                                         }
8907                                         if (empty($lalign)) {
8908                                                 $lalign = $align;
8909                                         }
8910                                 }
8911                                 // align lines
8912                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
8913                                         // we are at the beginning of a new line
8914                                         if (isset($startlinex)) {
8915                                                 if (isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) {
8916                                                         // the last line must be shifted to be aligned as requested
8917                                                         $linew = abs($this->endlinex - $startlinex);
8918                                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
8919                                                         if (isset($opentagpos) AND isset($this->footerpos[$startlinepage])) {
8920                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
8921                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
8922                                                         } elseif (isset($opentagpos)) {
8923                                                                 $midpos = $opentagpos;
8924                                                         } elseif (isset($this->footerpos[$startlinepage])) {
8925                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
8926                                                                 $midpos = $this->footerpos[$startlinepage];
8927                                                         } else {
8928                                                                 $midpos = 0;
8929                                                         }
8930                                                         if ($midpos > 0) {
8931                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
8932                                                                 $pend = substr($this->pages[$startlinepage], $midpos);
8933                                                         } else {
8934                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
8935                                                                 $pend = "";
8936                                                         }
8937                                                         // calculate shifting amount
8938                                                         $mdiff = abs($w - $linew);
8939                                                         if ($plalign == "C") {
8940                                                                 if ($this->rtl) {
8941                                                                         $t_x = -($mdiff / 2);
8942                                                                 } else {
8943                                                                         $t_x = ($mdiff / 2);
8944                                                                 }
8945                                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
8946                                                                 // right alignment on LTR document
8947                                                                 $t_x = $mdiff;
8948                                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
8949                                                                 // left alignment on RTL document
8950                                                                 $t_x = -$mdiff;
8951                                                         }
8952                                                         // shift the line
8953                                                         $trx = sprintf('1 0 0 1 %.3f 0 cm', ($t_x * $this->k));
8954                                                         $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
8955                                                         $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
8956                                                 }
8957                                         }
8958                                         $this->checkPageBreak($this->lasth);
8959                                         $this->SetFont($fontname, $fontstyle, $fontsize);
8960                                         if ($wfill) {
8961                                                 $this->SetFillColorArray($this->bgcolor);
8962                                         }
8963                                         $startlinex = $this->x;
8964                                         $startlinepage = $this->page;
8965                                         if (isset($endlinepos)) {
8966                                                 $startlinepos = $endlinepos;
8967                                                 unset($endlinepos);
8968                                         } else {
8969                                                 if (isset($this->footerpos[$this->page])) {
8970                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
8971                                                         $startlinepos = $this->footerpos[$this->page];
8972                                                 } else {
8973                                                         $startlinepos = strlen($this->pages[$this->page]);
8974                                                 }
8975                                         }
8976                                         $plalign = $lalign;
8977                                         $this->newline = false;
8978                                 }
8979                                 if (isset($opentagpos)) {
8980                                         unset($opentagpos);
8981                                 }
8982                                 if ($dom[$key]['tag']) {
8983                                         if ($dom[$key]['opening']) {
8984                                                 // table content is handled in a special way
8985                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
8986                                                         $trid = $dom[$key]['parent'];
8987                                                         $table_el = $dom[$trid]['parent'];
8988                                                         if (!isset($dom[$table_el]['cols'])) {
8989                                                                 $dom[$table_el]['cols'] = $trid['cols'];
8990                                                         }
8991                                                         // calculate cell width
8992                                                         if (isset($dom[($dom[$key]['parent'])]['width'])) {
8993                                                                 $table_width = $this->pixelsToUnits($dom[($dom[$key]['parent'])]['width']);
8994                                                         } else {
8995                                                                 $table_width = $w;
8996                                                         }
8997                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
8998                                                                 $currentcmargin = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding']);
8999                                                                 $this->cMargin = $currentcmargin;
9000                                                         } else {
9001                                                                 $currentcmargin = 0;
9002                                                         }
9003                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
9004                                                                 $cellspacing = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing']);
9005                                                         } else {
9006                                                                 $cellspacing = 0;
9007                                                         }
9008                                                         if ($this->rtl) {
9009                                                                 $cellspacingx = -$cellspacing;
9010                                                         } else {
9011                                                                 $cellspacingx = $cellspacing;
9012                                                         }
9013                                                         $colspan = $dom[$key]['attribute']['colspan'];
9014                                                         if (isset($dom[$key]['width'])) {
9015                                                                 $cellw = $this->pixelsToUnits($dom[$key]['width']);
9016                                                         } else {
9017                                                                 $cellw = ($colspan * ($table_width / $dom[$table_el]['cols']));
9018                                                         }
9019                                                         $cellw -= $cellspacing;
9020                                                         $cell_content = $dom[$key]['content'];
9021                                                         $tagtype = $dom[$key]['value'];
9022                                                         $parentid = $key;
9023                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
9024                                                                 // move $key index forward
9025                                                                 $key++;
9026                                                         }
9027                                                         if (!isset($dom[$trid]['startpage'])) {
9028                                                                 $dom[$trid]['startpage'] = $this->page;
9029                                                         } else {
9030                                                                 $this->setPage($dom[$trid]['startpage']);
9031                                                         }
9032                                                         if (!isset($dom[$trid]['starty'])) {
9033                                                                 $dom[$trid]['starty'] = $this->y;
9034                                                         } else {
9035                                                                 $this->y = $dom[$trid]['starty'];
9036                                                         }
9037                                                         if (!isset($dom[$trid]['startx'])) {
9038                                                                 $dom[$trid]['startx'] = $this->x;
9039                                                         }
9040                                                         $this->x += ($cellspacingx / 2);
9041                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
9042                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
9043                                                         }       else {
9044                                                                 $rowspan = 1;
9045                                                         }
9046                                                         // skip row-spanned cells started on the previous rows
9047                                                         if (isset($dom[$table_el]['rowspans'])) {
9048                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9049                                                                         if  (($trwsp['startx'] == $this->x) AND (($trwsp['starty'] < $this->y) OR ($trwsp['startpage'] < $this->page)) AND ($trwsp['rowspan'] > 0)) {
9050                                                                                 $this->x = $trwsp['endx'] + $cellspacingx;
9051                                                                         }
9052                                                                 }
9053                                                         }
9054                                                         // add rowspan information to table element
9055                                                         if ($rowspan > 1) {
9056                                                                 if (isset($this->footerpos[$this->page])) {
9057                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9058                                                                         $trintmrkpos = $this->footerpos[$this->page];
9059                                                                 } else {
9060                                                                         $trintmrkpos = strlen($this->pages[$this->page]);
9061                                                                 }
9062                                                                 $trsid = array_push($dom[$table_el]['rowspans'], array('rowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
9063                                                         }
9064                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
9065                                                         if ($rowspan > 1) {
9066                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
9067                                                         }
9068                                                         // push background colors
9069                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
9070                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
9071                                                         }
9072
9073                                                         // write the cell content
9074                                                         $this->MultiCell($cellw, 0, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
9075
9076                                                         $this->cMargin = $currentcmargin;
9077                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
9078                                                         // update the end of row position
9079                                                         if (isset($dom[$trid]['endy'])) {
9080                                                                 if ($this->page == $dom[$trid]['endpage']) {
9081                                                                         $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
9082                                                                 } elseif ($this->page > $dom[$trid]['endpage']) {
9083                                                                         $dom[$trid]['endy'] = $this->y;
9084                                                                 }
9085                                                         } else {
9086                                                                 $dom[$trid]['endy'] = $this->y;
9087                                                         }
9088                                                         if (isset($dom[$trid]['endpage'])) {
9089                                                                 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
9090                                                         } else {
9091                                                                 $dom[$trid]['endpage'] = $this->page;
9092                                                         }
9093                                                         // account for row-spanned cells
9094                                                         if ($rowspan > 1) {
9095                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
9096                                                         }
9097                                                         if (isset($dom[$table_el]['rowspans'])) {
9098                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9099                                                                         if ($trwsp['rowspan'] > 0) {
9100                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
9101                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
9102                                                                         }
9103                                                                 }
9104                                                         }
9105                                                         $this->x += ($cellspacingx / 2);
9106                                                 } else {
9107                                                         // opening tag (or self-closing tag)
9108                                                         if (!isset($opentagpos)) {
9109                                                                 if (isset($this->footerpos[$this->page])) {
9110                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9111                                                                         $opentagpos = $this->footerpos[$this->page];
9112                                                                 } else {
9113                                                                         $opentagpos = strlen($this->pages[$this->page]);
9114                                                                 }
9115                                                         }
9116                                                         $this->openHTMLTagHandler($dom, $key, $cell);
9117                                                 }
9118                                         } else {
9119                                                 // closing tag
9120                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
9121                                         }
9122                                 } elseif (strlen($dom[$key]['value']) > 0) {
9123                                         // text
9124                                         if ($this->HREF) {
9125                                                 // HTML <a> Link
9126                                                 $strrest = $this->addHtmlLink($this->HREF, $dom[$key]['value'], $wfill, true);
9127                                         } else {
9128                                                 $ctmpmargin = $this->cMargin;
9129                                                 $this->cMargin = 0;
9130                                                 // write only the first line and get the rest
9131                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, "", false, 0, true);
9132                                                 $this->cMargin = $ctmpmargin;
9133                                         }
9134                                         if (strlen($strrest) > 0) {
9135                                                 // store the remaining string on the previous $key position
9136                                                 $this->newline = true;
9137                                                 if ($cell) {
9138                                                         if ($this->rtl) {
9139                                                                 $this->x -= $this->cMargin;
9140                                                         } else {
9141                                                                 $this->x += $this->cMargin;
9142                                                         }
9143                                                 }
9144                                                 $dom[$key]['value'] = ltrim($strrest);
9145                                                 $key--;
9146                                         }
9147                                 }
9148                                 $key++;
9149                         } // end for each $key
9150                         // align the last line
9151                         if (isset($startlinex)) {
9152                                 if (isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) {
9153                                         // the last line must be shifted to be aligned as requested
9154                                         $linew = abs($this->endlinex - $startlinex);
9155                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9156                                         if (isset($opentagpos) AND isset($this->footerpos[$startlinepage])) {
9157                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9158                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9159                                         } elseif (isset($opentagpos)) {
9160                                                 $midpos = $opentagpos;
9161                                         } elseif (isset($this->footerpos[$startlinepage])) {
9162                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9163                                                 $midpos = $this->footerpos[$startlinepage];
9164                                         } else {
9165                                                 $midpos = 0;
9166                                         }
9167                                         if ($midpos > 0) {
9168                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9169                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9170                                         } else {
9171                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9172                                                 $pend = "";
9173                                         }
9174                                         // calculate shifting amount
9175                                         $mdiff = abs($w - $linew);
9176                                         if ($plalign == "C") {
9177                                                 if ($this->rtl) {
9178                                                         $t_x = -($mdiff / 2);
9179                                                 } else {
9180                                                         $t_x = ($mdiff / 2);
9181                                                 }
9182                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9183                                                 // right alignment on LTR document
9184                                                 $t_x = $mdiff;
9185                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9186                                                 // left alignment on RTL document
9187                                                 $t_x = -$mdiff;
9188                                         }
9189                                         // shift the line
9190                                         $trx = sprintf('1 0 0 1 %.3f 0 cm', ($t_x * $this->k));
9191                                         $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9192                                 }
9193                         }
9194                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == "table")))) {
9195                                 $this->Ln($this->lasth);
9196                         }
9197                         // restore previous values
9198                         $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
9199                         $this->SetFillColorArray($prevbgcolor);
9200                         $this->SetTextColorArray($prevfgcolor);
9201                         $this->lMargin = $prevlMargin;
9202                         $this->rMargin = $prevrMargin;
9203                         $this->cMargin = $prevcMargin;
9204                         unset($dom);
9205                 }
9206
9207                 /**
9208                  * Process opening tags.
9209                  * @param array $dom html dom array
9210                  * @param int $key current element id
9211                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
9212                  * @access protected
9213                  */
9214                 function openHTMLTagHandler(&$dom, $key, $cell=false) {
9215                         $tag = $dom[$key];
9216                         $parent = $dom[($dom[$key]['parent'])];
9217                         // check for text direction attribute
9218                         if (isset($tag['attribute']['dir'])) {
9219                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
9220                         } else {
9221                                 $this->tmprtl = false;
9222                         }
9223                         //Opening tag
9224                         switch($tag['value']) {
9225                                 case 'table': {
9226                                         $dom[$key]['rowspans'] = array();
9227                                         if (isset($tag['attribute']['cellpadding'])) {
9228                                                 $this->oldcMargin = $this->cMargin;
9229                                                 $this->cMargin = $this->pixelsToUnits($tag['attribute']['cellpadding']);
9230                                         }
9231                                         break;
9232                                 }
9233                                 case 'tr': {
9234                                         // array of columns positions
9235                                         $dom[$key]['cellpos'] = array();
9236                                         break;
9237                                 }
9238                                 case 'td':
9239                                 case 'th': {
9240                                         break;
9241                                 }
9242                                 case 'hr': {
9243                                         $this->Ln('', $cell);
9244                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
9245                                                 $hrWidth = $this->pixelsToUnits($tag['attribute']['width']);
9246                                         } else {
9247                                                 $hrWidth = $this->w - $this->lMargin - $this->rMargin;
9248                                         }
9249                                         $x = $this->GetX();
9250                                         $y = $this->GetY();
9251                                         $prevlinewidth = $this->GetLineWidth();
9252                                         $this->Line($x, $y, $x + $hrWidth, $y);
9253                                         $this->SetLineWidth($prevlinewidth);
9254                                         $this->Ln('', $cell);
9255                                         break;
9256                                 }
9257                                 case 'u': {
9258                                         $this->setStyle('u', true);
9259                                         break;
9260                                 }
9261                                 case 'del': {
9262                                         $this->setStyle('d', true);
9263                                         break;
9264                                 }
9265                                 case 'a': {
9266                                         $this->HREF = $tag['attribute']['href'];
9267                                         break;
9268                                 }
9269                                 case 'img': {
9270                                         if (isset($tag['attribute']['src'])) {
9271                                                 // replace relative path with real server path
9272                                                 if ($tag['attribute']['src'][0] == '/') {
9273                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
9274                                                 }
9275                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
9276                                                 if (!isset($tag['attribute']['width'])) {
9277                                                         $tag['attribute']['width'] = 0;
9278                                                 }
9279                                                 if (!isset($tag['attribute']['height'])) {
9280                                                         $tag['attribute']['height'] = 0;
9281                                                 }
9282                                                 if (!isset($tag['attribute']['align'])) {
9283                                                         $align = 'N';
9284                                                 } else {
9285                                                         switch($tag['attribute']['align']) {
9286                                                                 case 'top':{
9287                                                                         $align = 'T';
9288                                                                         break;
9289                                                                 }
9290                                                                 case 'middle':{
9291                                                                         $align = 'M';
9292                                                                         break;
9293                                                                 }
9294                                                                 case 'bottom':{
9295                                                                         $align = 'B';
9296                                                                         break;
9297                                                                 }
9298                                                                 default:{
9299                                                                         $align = 'N';
9300                                                                         break;
9301                                                                 }
9302                                                         }
9303                                                 }
9304                                                 $fileinfo = pathinfo($tag['attribute']['src']);
9305                                                 if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
9306                                                         $type = strtolower($fileinfo['extension']);
9307                                                 }
9308                                                 if (($type == "eps") OR ($type == "ai")) {
9309                                                         $this->ImageEps($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', true, $align);
9310                                                 } else {
9311                                                         $this->Image($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', '', $align);
9312                                                 }
9313                                         }
9314                                         break;
9315                                 }
9316                                 case 'dl': {
9317                                         $this->listnum++;
9318                                         break;
9319                                 }
9320                                 case 'dt': {
9321                                         $this->Ln('', $cell);
9322                                         break;
9323                                 }
9324                                 case 'dd': {
9325                                         if ($this->rtl) {
9326                                                 $this->rMargin += $this->listindent;
9327                                         } else {
9328                                                 $this->lMargin += $this->listindent;
9329                                         }
9330                                         $this->Ln('', $cell);
9331                                         break;
9332                                 }
9333                                 case 'ul':
9334                                 case 'ol': {
9335                                         $this->listnum++;
9336                                         if ($tag['value'] == "ol") {
9337                                                 $this->listordered[$this->listnum] = true;
9338                                         } else {
9339                                                 $this->listordered[$this->listnum] = false;
9340                                         }
9341                                         $this->listcount[$this->listnum] = 0;
9342                                         if ($this->rtl) {
9343                                                 $this->rMargin += $this->listindent;
9344                                         } else {
9345                                                 $this->lMargin += $this->listindent;
9346                                         }
9347                                         break;
9348                                 }
9349                                 case 'li': {
9350                                         $this->Ln('', $cell);
9351                                         if ($tag['value'] == 'li') {
9352                                                 if ($this->listordered[$this->listnum]) {
9353                                                         if (isset($tag['attribute']['value'])) {
9354                                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
9355                                                         }
9356                                                         $this->listcount[$this->listnum]++;
9357                                                         if ($this->rtl) {
9358                                                                 $this->lispacer = ".".($this->listcount[$this->listnum]);
9359                                                         } else {
9360                                                                 $this->lispacer = ($this->listcount[$this->listnum]).".";
9361                                                         }
9362                                                 } else {
9363                                                         //unordered list symbol
9364                                                         $this->lispacer = "-";
9365                                                 }
9366                                         } else {
9367                                                 $this->lispacer = "";
9368                                         }
9369                                         $tmpx = $this->x;
9370                                         $lspace = $this->GetStringWidth($this->lispacer."  ");
9371                                         if ($this->rtl) {
9372                                                 $this->x += $lspace;
9373                                         } else {
9374                                                 $this->x -= $lspace;
9375                                         }
9376                                         $this->Write($this->lasth, $this->lispacer, '', false, '', false, 0, false);
9377                                         $this->x = $tmpx;
9378                                         break;
9379                                 }
9380                                 case 'blockquote':
9381                                 case 'br': {
9382                                         $this->Ln('', $cell);
9383                                         break;
9384                                 }
9385                                 case 'p': {
9386                                         $this->Ln('', $cell);
9387                                         $this->Ln('', $cell);
9388                                         break;
9389                                 }
9390                                 case 'sup': {
9391                                         $this->SetXY($this->GetX(), $this->GetY() - (($parent['fontsize'] - $this->FontSizePt) / $this->k));
9392                                         break;
9393                                 }
9394                                 case 'sub': {
9395                                         $this->SetXY($this->GetX(), $this->GetY() + (($parent['fontsize'] - (0.5 * $this->FontSizePt)) / $this->k));
9396                                         break;
9397                                 }
9398                                 case 'small': {
9399                                         $this->SetXY($this->GetX(), $this->GetY() + (($parent['fontsize'] - $this->FontSizePt)/$this->k));
9400                                         break;
9401                                 }
9402                                 case 'h1':
9403                                 case 'h2':
9404                                 case 'h3':
9405                                 case 'h4':
9406                                 case 'h5':
9407                                 case 'h6': {
9408                                         $this->Ln(($tag['fontsize'] * 1.5) / $this->k, $cell);
9409                                         break;
9410                                 }
9411                                 default: {
9412                                         break;
9413                                 }
9414                         }
9415                 }
9416
9417                 /**
9418                  * Process closing tags.
9419                  * @param array $dom html dom array
9420                  * @param int $key current element id
9421                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
9422                  * @access protected
9423                  */
9424                 function closeHTMLTagHandler(&$dom, $key, $cell=false) {
9425                         $tag = $dom[$key];
9426                         $parent = $dom[($dom[$key]['parent'])];
9427                         //Closing tag
9428                         switch($tag['value']) {
9429                                 case 'td':
9430                                 case 'th': {
9431                                         break;
9432                                 }
9433                                 case 'tr': {
9434                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
9435                                         $this->setPage($parent['endpage']);
9436                                         $this->y = $parent['endy'];
9437                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
9438                                                 $cellspacing = $this->pixelsToUnits($dom[$table_el]['attribute']['cellspacing']);
9439                                                 $this->y += $cellspacing;
9440                                         }
9441                                         $this->Ln(0, $cell);
9442                                         $this->x = $parent['startx'];
9443                                         // update row-spanned cells
9444                                         if (isset($dom[$table_el]['rowspans'])) {
9445                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9446                                                                 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
9447                                                 }
9448                                         }
9449                                         break;
9450                                 }
9451                                 case 'table': {
9452                                         // draw borders
9453                                         $table_el = $parent;
9454                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
9455                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
9456                                                         $border = 1;
9457                                         } else {
9458                                                 $border = 0;
9459                                         }
9460                                         // for each row
9461                                         foreach ($table_el['trids'] as $j => $trkey) {
9462                                                 $parent = $dom[$trkey];
9463                                                 $this->setPage($parent['startpage']);
9464                                                 $this->y = $parent['starty'];
9465                                                 $restspace = $this->getPageHeight() - $this->y - $this->getBreakMargin();
9466                                                 $startpage = $parent['startpage'];
9467                                                 $endpage = $parent['endpage'];
9468                                                 // for each cell on the row
9469                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
9470                                                         if (isset($cellpos['rowspanid'])) {
9471                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
9472                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
9473                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
9474                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
9475                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
9476                                                         } else {
9477                                                                 $endy = $parent['endy'];
9478                                                         }
9479                                                         if ($endpage > $startpage) {
9480                                                                 // design borders around HTML cells.
9481                                                                 for ($page=$startpage; $page <= $endpage; $page++) {
9482                                                                         $this->setPage($page);
9483                                                                         if ($page == $startpage) {
9484                                                                                 $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin();
9485                                                                                 $ch = $restspace;
9486                                                                         } elseif ($page == $endpage) {
9487                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
9488                                                                                 $ch = $endy - $this->tMargin;
9489                                                                         } else {
9490                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
9491                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
9492                                                                         }
9493
9494                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
9495                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
9496                                                                                 $fill = true;
9497                                                                         } else {
9498                                                                                 $fill = false;
9499                                                                         }
9500                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
9501                                                                         $this->x = $cellpos['startx'];
9502                                                                         // design a cell around the text
9503                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
9504                                                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
9505                                                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
9506                                                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
9507                                                                         $this->intmrk[$this->page] += strlen($ccode."\n");
9508                                                                 }
9509                                                         } else {
9510                                                                 $ch = $endy - $parent['starty'];
9511                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
9512                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
9513                                                                         $fill = true;
9514                                                                 } else {
9515                                                                         $fill = false;
9516                                                                 }
9517                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
9518                                                                 $this->x = $cellpos['startx'];
9519                                                                 $this->y = $parent['starty'];
9520                                                                 // design a cell around the text
9521                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
9522                                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
9523                                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
9524                                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
9525                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
9526                                                         }
9527                                                 }
9528                                                 if (isset($table_el['attribute']['cellspacing'])) {
9529                                                         $cellspacing = $this->pixelsToUnits($table_el['attribute']['cellspacing']);
9530                                                         $this->y += $cellspacing;
9531                                                 }
9532                                                 $this->Ln(0, $cell);
9533                                                 $this->x = $parent['startx'];
9534                                         }
9535                                         if (isset($parent['cellpadding'])) {
9536                                                 $this->cMargin = $this->oldcMargin;
9537                                         }
9538                                         //set row height
9539                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
9540                                         break;
9541                                 }
9542                                 case 'u': {
9543                                         $this->setStyle('u', false);
9544                                         break;
9545                                 }
9546                                 case 'del': {
9547                                         $this->setStyle('d', false);
9548                                         break;
9549                                 }
9550                                 case 'a': {
9551                                         $this->HREF = '';
9552                                         break;
9553                                 }
9554                                 case 'sup': {
9555                                         $this->SetXY($this->GetX(), $this->GetY() + (($this->FontSizePt - $parent['fontsize'])/$this->k));
9556                                         break;
9557                                 }
9558                                 case 'sub': {
9559                                         $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSizePt - (0.5 * $parent['fontsize']))/$this->k));
9560                                         break;
9561                                 }
9562                                 case 'small': {
9563                                         $this->SetXY($this->GetX(), $this->GetY() - (($this->FontSizePt - $parent['fontsize'])/$this->k));
9564                                         break;
9565                                 }
9566                                 case 'p': {
9567                                         $this->Ln('', $cell);
9568                                         $this->Ln('', $cell);
9569                                         break;
9570                                 }
9571                                 case 'dl': {
9572                                         $this->listnum--;
9573                                         if ($this->listnum <= 0) {
9574                                                 $this->listnum = 0;
9575                                                 $this->Ln('', $cell);
9576                                                 $this->Ln('', $cell);
9577                                         }
9578                                         break;
9579                                 }
9580                                 case 'dt': {
9581                                         $this->lispacer = "";
9582                                         break;
9583                                 }
9584                                 case 'dd': {
9585                                         $this->lispacer = "";
9586                                         if ($this->rtl) {
9587                                                 $this->rMargin -= $this->listindent;
9588                                         } else {
9589                                                 $this->lMargin -= $this->listindent;
9590                                         }
9591                                         break;
9592                                 }
9593                                 case 'ul':
9594                                 case 'ol': {
9595                                         $this->listnum--;
9596                                         $this->lispacer = "";
9597                                         if ($this->rtl) {
9598                                                 $this->rMargin -= $this->listindent;
9599                                         } else {
9600                                                 $this->lMargin -= $this->listindent;
9601                                         }
9602                                         if ($this->listnum <= 0) {
9603                                                 $this->listnum = 0;
9604                                                 $this->Ln('', $cell);
9605                                                 $this->Ln('', $cell);
9606                                         }
9607                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
9608                                         break;
9609                                 }
9610                                 case 'li': {
9611                                         $this->lispacer = "";
9612                                         break;
9613                                 }
9614                                 case 'h1':
9615                                 case 'h2':
9616                                 case 'h3':
9617                                 case 'h4':
9618                                 case 'h5':
9619                                 case 'h6': {
9620                                         $this->Ln(($parent['fontsize'] * 1.5) / $this->k, $cell);
9621                                         break;
9622                                 }
9623                                 default : {
9624                                         break;
9625                                 }
9626                         }
9627                         $this->tmprtl = false;
9628                 }
9629         } // END OF TCPDF CLASS
9630 }
9631 //============================================================+
9632 // END OF FILE
9633 //============================================================+
9634 ?>