Some security fixes backported from unstable code.
[fa-stable.git] / reporting / includes / tcpdf.php
1 <?php
2 //============================================================+
3 // File name   : tcpdf.php
4 // Begin       : 2002-08-03
5 // Last Update : 2008-09-19
6 // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
7 // Version     : 4.0.027_PHP4
8 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
9 //      ----------------------------------------------------------------------------
10 //  Copyright (C) 2002-2008  Nicola Asuni - Tecnick.com S.r.l.
11 //
12 //      This program is free software: you can redistribute it and/or modify
13 //      it under the terms of the GNU Lesser General Public License as published by
14 //      the Free Software Foundation, either version 2.1 of the License, or
15 //      (at your option) any later version.
16 //
17 //      This program is distributed in the hope that it will be useful,
18 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 //      GNU Lesser General Public License for more details.
21 //
22 //      You should have received a copy of the GNU Lesser General Public License
23 //      along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 //
25 //      See LICENSE.TXT file for more information.
26 //  ----------------------------------------------------------------------------
27 //
28 // Description : This is a PHP class for generating PDF documents without
29 //               requiring external extensions.
30 //
31 // NOTE:
32 // This class was originally derived in 2002 from the Public
33 // Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
34 // but now is almost entirely rewritten.
35 //
36 // Main features:
37 //  * no external libraries are required for the basic functions;
38 //      * supports all ISO page formats;
39 //      * supports UTF-8 Unicode and Right-To-Left languages;
40 //      * supports document encryption;
41 //      * includes methods to publish some XHTML code;
42 //      * includes graphic (geometric) and transformation methods;
43 //      * includes bookmarks;
44 //      * includes Javascript and forms support;
45 //      * includes a method to print various barcode formats;
46 //      * supports TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
47 //      * supports custom page formats, margins and units of measure;
48 //      * includes methods for page header and footer management;
49 //      * supports automatic page break;
50 //      * supports automatic page numbering and page groups;
51 //      * supports automatic line break and text justification;
52 //      * supports JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
53 //      * supports stroke and clipping mode for text;
54 //      * supports clipping masks;
55 //      * supports Grayscale, RGB, CMYK, Spot colors and transparency;
56 //      * supports links and annotations;
57 //      * supports page compression (requires zlib extension);
58 //      * supports PDF user's rights.
59 //
60 // -----------------------------------------------------------
61 // THANKS TO:
62 //
63 // Olivier Plathey (http://www.fpdf.org) for original FPDF.
64 // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
65 // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
66 // Warren Sherliker (wsherliker@gmail.com) for better image handling.
67 // dullus for text Justification.
68 // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
69 // Patrick Benny for text stretch suggestion on Cell().
70 // Johannes Güntert for JavaScript support.
71 // Denis Van Nuffelen for Dynamic Form.
72 // Jacek Czekaj for multibyte justification
73 // Anthony Ferrara for the reintroduction of legacy image methods.
74 // Sourceforge user 1707880 (hucste) for line-trough mode.
75 // Larry Stanbery for page groups.
76 // Martin Hall-May for transparency.
77 // Aaron C. Spike for Polycurve method.
78 // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
79 // Moritz Wagner and Andreas Wurmser for graphic functions.
80 // Andrew Whitehead for core fonts support.
81 // Esteban Joël Marín for OpenType font conversion.
82 // Teus Hagen for several suggestions and fixes.
83 // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
84 // Kosmas Papachristos for some CSS improvements.
85 // Anyone that has reported a bug or sent a suggestion.
86 //============================================================+
87
88 /**
89  * This is a PHP class for generating PDF documents without requiring external extensions.<br>
90  * TCPDF project (http://www.tcpdf.org) was originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
91  * <h3>TCPDF main features are:</h3>
92  * <ul>
93  * <li>no external libraries are required for the basic functions;</li>
94  * <li>supports all ISO page formats;</li>
95  * <li>supports UTF-8 Unicode and Right-To-Left languages;</li>
96  * <li>supports document encryption;</li>
97  * <li>includes methods to publish some XHTML code;</li>
98  * <li>includes graphic (geometric) and transformation methods;</li>
99  * <li>includes bookmarks;</li>
100  * <li>includes Javascript and forms support;</li>
101  * <li>includes a method to print various barcode formats;</li>
102  * <li>supports TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
103  * <li>supports custom page formats, margins and units of measure;</li>
104  * <li>includes methods for page header and footer management;</li>
105  * <li>supports automatic page break;</li>
106  * <li>supports automatic page numbering and page groups;</li>
107  * <li>supports automatic line break and text justification;
108  * <li>supports JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;</li>
109  * <li>supports stroke and clipping mode for text;</li>
110  * <li>supports clipping masks;</li>
111  * <li>supports Grayscale, RGB and CMYK colors and transparency;</li>
112  * <li>supports links and annotations;</li>
113  * <li>supports page compression (requires zlib extension);</li>
114  * <li>supports PDF user's rights.</li>
115  * </ul>
116  * Tools to encode your unicode fonts are on fonts/utils directory.</p>
117  * @package com.tecnick.tcpdf
118  * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
119  * @author Nicola Asuni
120  * @copyright 2004-2008 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
121  * @link http://www.tcpdf.org
122  * @license http://www.gnu.org/copyleft/lesser.html LGPL
123  * @version 4.0.027_PHP4
124  */
125
126 /**
127  * main configuration file
128  */
129 /** -------------------------------FrontAccounting 2.0  ---------------------------
130   * following changes are done for FrontAccounting 2.0 - Joe Hunt 06.08.2008
131   * 1. /config/tcpdf_config.php is not included, commented out
132   * 2. Following 3 defines instead:
133   *    if (!defined("K_PATH_FONTS"))
134   *        define ("K_PATH_FONTS", '../reporting/fonts/');
135   *    define ("K_PATH_CACHE", '../reporting/fonts/');
136   *    define("K_CELL_HEIGHT_RATIO", 1.25);
137   * 3. ./unicode_data2.php only included if unicode is set. (in class constructor)
138   *    We only use a reduced variant of unicode_data.php (unicode_data.php).af wrap the
139   *    following defines
140   *    if (!defined("K_RE_PATTERN_RTL"))
141   *    and
142   *    if (!defined("K_RE_PATTERN_ARABIC"))
143   * 4. Parameter $unicode in constructor renamed to $uni.
144   * 4. Header function renamed to Header1 (due to conflict with FrontReport Header)
145   * -------------------------------------------------------------------------------
146   */
147 if (!defined("K_PATH_FONTS"))
148         define ("K_PATH_FONTS", '../reporting/fonts/');
149 define ("K_PATH_CACHE", '../reporting/fonts/');
150 define("K_CELL_HEIGHT_RATIO", 1.25);
151
152 //require_once(dirname(__FILE__).'/config/tcpdf_config.php');
153
154 // includes some support files
155
156 /**
157  * unicode data
158  */
159 // only included if unicode
160 //include_once(dirname(__FILE__)."/unicode_data2.php");
161
162 /**
163  * html colors table
164  */
165 require_once(dirname(__FILE__).'/htmlcolors.php');
166
167 /**
168  * barcode class
169  */
170 require_once(dirname(__FILE__)."/barcodes.php");
171
172 /**
173  * HTML entity decode functions
174  */
175 require_once(dirname(__FILE__)."/html_entity_decode_php4.php");
176
177 if (!class_exists('TCPDF')) {
178         /**
179          * define default PDF document producer
180          */
181         define('PDF_PRODUCER','TCPDF 4.0.027_PHP4 (http://www.tcpdf.org)');
182
183         /**
184         * This is a PHP class for generating PDF documents without requiring external extensions.<br>
185         * TCPDF project (http://www.tcpdf.org) has been originally derived in 2002 from the Public Domain FPDF class by Olivier Plathey (http://www.fpdf.org), but now is almost entirely rewritten.<br>
186         * @name TCPDF
187         * @package com.tecnick.tcpdf
188         * @version 4.0.027_PHP4
189         * @author Nicola Asuni - info@tecnick.com
190         * @link http://www.tcpdf.org
191         * @license http://www.gnu.org/copyleft/lesser.html LGPL
192         */
193         class TCPDF {
194                 
195                 // protected or Protected properties
196
197                 /**
198                 * @var current page number
199                 * @access protected
200                 */
201                 var $page;
202
203                 /**
204                 * @var current object number
205                 * @access protected
206                 */
207                 var $n;
208
209                 /**
210                 * @var array of object offsets
211                 * @access protected
212                 */
213                 var $offsets;
214
215                 /**
216                 * @var buffer holding in-memory PDF
217                 * @access protected
218                 */
219                 var $buffer;
220
221                 /**
222                 * @var array containing pages
223                 * @access protected
224                 */
225                 var $pages = array();
226
227                 /**
228                 * @var current document state
229                 * @access protected
230                 */
231                 var $state;
232
233                 /**
234                 * @var compression flag
235                 * @access protected
236                 */
237                 var $compress;
238
239                 /**
240                 * @var current page orientation (P = Portrait, L = Landscape)
241                 * @access protected
242                 */
243                 var $CurOrientation;
244
245                 /**
246                 * @var array that stores page dimensions.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height</li><li>$this->pagedim[$this->page]['tm'] => top_margin</li><li>$this->pagedim[$this->page]['bm'] => bottom_margin</li><li>$this->pagedim[$this->page]['lm'] => left_margin</li><li>$this->pagedim[$this->page]['rm'] => right_margin</li><li>$this->pagedim[$this->page]['pb'] => auto_page_break</li><li>$this->pagedim[$this->page]['or'] => page_orientation</li></ul>
247                 * @access protected
248                 */
249                 var $pagedim = array();
250
251                 /**
252                 * @var scale factor (number of points in user unit)
253                 * @access protected
254                 */
255                 var $k;
256
257                 /**
258                 * @var width of page format in points
259                 * @access protected
260                 */
261                 var $fwPt;
262
263                 /**
264                 * @var height of page format in points
265                 * @access protected
266                 */
267                 var $fhPt;
268
269                 /**
270                 * @var current width of page in points
271                 * @access protected
272                 */
273                 var $wPt;
274
275                 /**
276                 * @var current height of page in points
277                 * @access protected
278                 */
279                 var $hPt;
280
281                 /**
282                 * @var current width of page in user unit
283                 * @access protected
284                 */
285                 var $w;
286
287                 /**
288                 * @var current height of page in user unit
289                 * @access protected
290                 */
291                 var $h;
292
293                 /**
294                 * @var left margin
295                 * @access protected
296                 */
297                 var $lMargin;
298
299                 /**
300                 * @var top margin
301                 * @access protected
302                 */
303                 var $tMargin;
304
305                 /**
306                 * @var right margin
307                 * @access protected
308                 */
309                 var $rMargin;
310
311                 /**
312                 * @var page break margin
313                 * @access protected
314                 */
315                 var $bMargin;
316
317                 /**
318                 * @var cell internal padding
319                 * @access protected
320                 */
321                 var $cMargin;
322
323                 /**
324                 * @var cell internal padding (previous value)
325                 * @access protected
326                 */
327                 var $oldcMargin;
328
329                 /**
330                 * @var current horizontal position in user unit for cell positioning
331                 * @access protected
332                 */
333                 var $x;
334
335                 /**
336                 * @var current vertical position in user unit for cell positioning
337                 * @access protected
338                 */
339                 var $y;
340
341                 /**
342                 * @var height of last cell printed
343                 * @access protected
344                 */
345                 var $lasth;
346
347                 /**
348                 * @var line width in user unit
349                 * @access protected
350                 */
351                 var $LineWidth;
352
353                 /**
354                 * @var array of standard font names
355                 * @access protected
356                 */
357                 var $CoreFonts;
358
359                 /**
360                 * @var array of used fonts
361                 * @access protected
362                 */
363                 var $fonts = array();
364
365                 /**
366                 * @var array of font files
367                 * @access protected
368                 */
369                 var $FontFiles = array();
370
371                 /**
372                 * @var array of encoding differences
373                 * @access protected
374                 */
375                 var $diffs = array();
376
377                 /**
378                 * @var array of used images
379                 * @access protected
380                 */
381                 var $images = array();
382
383                 /**
384                 * @var array of Annotations in pages
385                 * @access protected
386                 */
387                 var $PageAnnots = array();
388
389                 /**
390                 * @var array of internal links
391                 * @access protected
392                 */
393                 var $links = array();
394
395                 /**
396                 * @var current font family
397                 * @access protected
398                 */
399                 var $FontFamily;
400
401                 /**
402                 * @var current font style
403                 * @access protected
404                 */
405                 var $FontStyle;
406
407                 /**
408                 * @var current font ascent (distance between font top and baseline)
409                 * @access protected
410                 * @since 2.8.000 (2007-03-29)
411                 */
412                 var $FontAscent;
413
414                 /**
415                 * @var current font descent (distance between font bottom and baseline)
416                 * @access protected
417                 * @since 2.8.000 (2007-03-29)
418                 */
419                 var $FontDescent;
420
421                 /**
422                 * @var underlining flag
423                 * @access protected
424                 */
425                 var $underline;
426
427                 /**
428                 * @var current font info
429                 * @access protected
430                 */
431                 var $CurrentFont;
432
433                 /**
434                 * @var current font size in points
435                 * @access protected
436                 */
437                 var $FontSizePt;
438
439                 /**
440                 * @var current font size in user unit
441                 * @access protected
442                 */
443                 var $FontSize;
444
445                 /**
446                 * @var commands for drawing color
447                 * @access protected
448                 */
449                 var $DrawColor;
450
451                 /**
452                 * @var commands for filling color
453                 * @access protected
454                 */
455                 var $FillColor;
456
457                 /**
458                 * @var commands for text color
459                 * @access protected
460                 */
461                 var $TextColor;
462
463                 /**
464                 * @var indicates whether fill and text colors are different
465                 * @access protected
466                 */
467                 var $ColorFlag;
468
469                 /**
470                 * @var word spacing
471                 * @access protected
472                 */
473                 var $ws;
474
475                 /**
476                 * @var automatic page breaking
477                 * @access protected
478                 */
479                 var $AutoPageBreak;
480
481                 /**
482                 * @var threshold used to trigger page breaks
483                 * @access protected
484                 */
485                 var $PageBreakTrigger;
486
487                 /**
488                 * @var flag set when processing footer
489                 * @access protected
490                 */
491                 var $InFooter;
492
493                 /**
494                 * @var zoom display mode
495                 * @access protected
496                 */
497                 var $ZoomMode;
498
499                 /**
500                 * @var layout display mode
501                 * @access protected
502                 */
503                 var $LayoutMode;
504
505                 /**
506                 * @var title
507                 * @access protected
508                 */
509                 var $title;
510
511                 /**
512                 * @var subject
513                 * @access protected
514                 */
515                 var $subject;
516
517                 /**
518                 * @var author
519                 * @access protected
520                 */
521                 var $author;
522
523                 /**
524                 * @var keywords
525                 * @access protected
526                 */
527                 var $keywords;
528
529                 /**
530                 * @var creator
531                 * @access protected
532                 */
533                 var $creator;
534
535                 /**
536                 * @var alias for total number of pages
537                 * @access protected
538                 */
539                 var $AliasNbPages;
540
541                 /**
542                 * @var right-bottom corner X coordinate of inserted image
543                 * @since 2002-07-31
544                 * @author Nicola Asuni
545                 * @access protected
546                 */
547                 var $img_rb_x;
548
549                 /**
550                 * @var right-bottom corner Y coordinate of inserted image
551                 * @since 2002-07-31
552                 * @author Nicola Asuni
553                 * @access protected
554                 */
555                 var $img_rb_y;
556
557                 /**
558                 * @var image scale factor
559                 * @since 2004-06-14
560                 * @author Nicola Asuni
561                 * @access protected
562                 */
563                 var $imgscale = 1;
564
565                 /**
566                 * @var boolean set to true when the input text is unicode (require unicode fonts)
567                 * @since 2005-01-02
568                 * @author Nicola Asuni
569                 * @access protected
570                 */
571                 var $isunicode = false;
572
573                 /**
574                 * @var PDF version
575                 * @since 1.5.3
576                 * @access protected
577                 */
578                 var $PDFVersion = "1.7";
579
580
581                 // ----------------------
582
583                 /**
584                  * @var Minimum distance between header and top page margin.
585                  * @access protected
586                  */
587                 var $header_margin;
588
589                 /**
590                  * @var Minimum distance between footer and bottom page margin.
591                  * @access protected
592                  */
593                 var $footer_margin;
594
595                 /**
596                  * @var original left margin value
597                  * @access protected
598                  * @since 1.53.0.TC013
599                  */
600                 var $original_lMargin;
601
602                 /**
603                  * @var original right margin value
604                  * @access protected
605                  * @since 1.53.0.TC013
606                  */
607                 var $original_rMargin;
608
609                 /**
610                  * @var Header font.
611                  * @access protected
612                  */
613                 var $header_font;
614
615                 /**
616                  * @var Footer font.
617                  * @access protected
618                  */
619                 var $footer_font;
620
621                 /**
622                  * @var Language templates.
623                  * @access protected
624                  */
625                 var $l;
626
627                 /**
628                  * @var Barcode to print on page footer (only if set).
629                  * @access protected
630                  */
631                 var $barcode = false;
632
633                 /**
634                  * @var If true prints header
635                  * @access protected
636                  */
637                 var $print_header = true;
638
639                 /**
640                  * @var If true prints footer.
641                  * @access protected
642                  */
643                 var $print_footer = true;
644
645                 /**
646                  * @var Header image logo.
647                  * @access protected
648                  */
649                 var $header_logo = "";
650
651                 /**
652                  * @var Header image logo width in mm.
653                  * @access protected
654                  */
655                 var $header_logo_width = 30;
656
657                 /**
658                  * @var String to print as title on document header.
659                  * @access protected
660                  */
661                 var $header_title = "";
662
663                 /**
664                  * @var String to print on document header.
665                  * @access protected
666                  */
667                 var $header_string = "";
668
669                 /**
670                  * @var Default number of columns for html table.
671                  * @access protected
672                  */
673                 var $default_table_columns = 4;
674
675
676                 // variables for html parser
677
678                 /**
679                  * @var HTML PARSER: store current link.
680                  * @access protected
681                  */
682                 var $HREF;
683
684                 /**
685                  * @var store available fonts list.
686                  * @access protected
687                  */
688                 var $fontlist = array();
689
690                 /**
691                  * @var current foreground color
692                  * @access protected
693                  */
694                 var $fgcolor;
695
696                 /**
697                  * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
698                  * @access protected
699                  */
700                 var $listordered = array();
701
702                 /**
703                  * @var HTML PARSER: array count list items on nested lists.
704                  * @access protected
705                  */
706                 var $listcount = array();
707
708                 /**
709                  * @var HTML PARSER: current list nesting level.
710                  * @access protected
711                  */
712                 var $listnum = 0;
713
714                 /**
715                  * @var HTML PARSER: indent amount for lists.
716                  * @access protected
717                  */
718                 var $listindent;
719
720                 /**
721                  * @var current background color
722                  * @access protected
723                  */
724                 var $bgcolor;
725
726                 /**
727                  * @var Store temporary font size in points.
728                  * @access protected
729                  */
730                 var $tempfontsize = 10;
731
732                 /**
733                  * @var Bold font style status.
734                  * @access protected
735                  */
736                 var $b;
737
738                 /**
739                  * @var Underlined font style status.
740                  * @access protected
741                  */
742                 var $u;
743
744                 /**
745                  * @var Italic font style status.
746                  * @access protected
747                  */
748                 var $i;
749
750                 /**
751                  * @var Line through font style status.
752                  * @access protected
753                  * @since 2.8.000 (2008-03-19)
754                  */
755                 var $d;
756
757                 /**
758                  * @var spacer for LI tags.
759                  * @access protected
760                  */
761                 var $lispacer = "";
762
763                 /**
764                  * @var default encoding
765                  * @access protected
766                  * @since 1.53.0.TC010
767                  */
768                 var $encoding = "UTF-8";
769
770                 /**
771                  * @var PHP internal encoding
772                  * @access protected
773                  * @since 1.53.0.TC016
774                  */
775                 var $internal_encoding;
776
777                 /**
778                  * @var indicates if the document language is Right-To-Left
779                  * @access protected
780                  * @since 2.0.000
781                  */
782                 var $rtl = false;
783
784                 /**
785                  * @var used to force RTL or LTR string inversion
786                  * @access protected
787                  * @since 2.0.000
788                  */
789                 var $tmprtl = false;
790
791                 // --- Variables used for document encryption:
792
793                 /**
794                  * Indicates whether document is protected
795                  * @access protected
796                  * @since 2.0.000 (2008-01-02)
797                  */
798                 var $encrypted;
799
800                 /**
801                  * U entry in pdf document
802                  * @access protected
803                  * @since 2.0.000 (2008-01-02)
804                  */
805                 var $Uvalue;
806
807                 /**
808                  * O entry in pdf document
809                  * @access protected
810                  * @since 2.0.000 (2008-01-02)
811                  */
812                 var $Ovalue;
813
814                 /**
815                  * P entry in pdf document
816                  * @access protected
817                  * @since 2.0.000 (2008-01-02)
818                  */
819                 var $Pvalue;
820
821                 /**
822                  * encryption object id
823                  * @access protected
824                  * @since 2.0.000 (2008-01-02)
825                  */
826                 var $enc_obj_id;
827
828                 /**
829                  * last RC4 key encrypted (cached for optimisation)
830                  * @access protected
831                  * @since 2.0.000 (2008-01-02)
832                  */
833                 var $last_rc4_key;
834
835                 /**
836                  * last RC4 computed key
837                  * @access protected
838                  * @since 2.0.000 (2008-01-02)
839                  */
840                 var $last_rc4_key_c;
841
842                 // --- bookmark ---
843
844                 /**
845                  * Outlines for bookmark
846                  * @access protected
847                  * @since 2.1.002 (2008-02-12)
848                  */
849                 var $outlines = array();
850
851                 /**
852                  * Outline root for bookmark
853                  * @access protected
854                  * @since 2.1.002 (2008-02-12)
855                  */
856                 var $OutlineRoot;
857
858
859                 // --- javascript and form ---
860
861                 /**
862                  * javascript code
863                  * @access protected
864                  * @since 2.1.002 (2008-02-12)
865                  */
866                 var $javascript = "";
867
868                 /**
869                  * javascript counter
870                  * @access protected
871                  * @since 2.1.002 (2008-02-12)
872                  */
873                 var $n_js;
874
875                 /**
876                  * line trough state
877                  * @access protected
878                  * @since 2.8.000 (2008-03-19)
879                  */
880                 var $linethrough;
881
882                 // --- Variables used for User's Rights ---
883                 // See PDF reference chapter 8.7 Digital Signatures
884
885                 /**
886                  * If true enables user's rights on PDF reader
887                  * @access protected
888                  * @since 2.9.000 (2008-03-26)
889                  */
890                 var $ur;
891
892                 /**
893                  * Names specifying additional document-wide usage rights for the document.
894                  * @access protected
895                  * @since 2.9.000 (2008-03-26)
896                  */
897                 var $ur_document;
898
899                 /**
900                  * Names specifying additional annotation-related usage rights for the document.
901                  * @access protected
902                  * @since 2.9.000 (2008-03-26)
903                  */
904                 var $ur_annots;
905
906                 /**
907                  * Names specifying additional form-field-related usage rights for the document.
908                  * @access protected
909                  * @since 2.9.000 (2008-03-26)
910                  */
911                 var $ur_form;
912
913                 /**
914                  * Names specifying additional signature-related usage rights for the document.
915                  * @access protected
916                  * @since 2.9.000 (2008-03-26)
917                  */
918                 var $ur_signature;
919
920                 /**
921                  * Dot Per Inch Document Resolution (do not change)
922                  * @access protected
923                  * @since 3.0.000 (2008-03-27)
924                  */
925                 var $dpi = 72;
926
927                 /**
928                  * Indicates whether a new page group was requested
929                  * @access protected
930                  * @since 3.0.000 (2008-03-27)
931                  */
932                 var $newpagegroup;
933
934                 /**
935                  * Contains the number of pages of the groups
936                  * @access protected
937                  * @since 3.0.000 (2008-03-27)
938                  */
939                 var $pagegroups;
940
941                 /**
942                  * Contains the alias of the current page group
943                  * @access protected
944                  * @since 3.0.000 (2008-03-27)
945                  */
946                 var $currpagegroup;
947
948                 /**
949                  * Restrict the rendering of some elements to screen or printout.
950                  * @access protected
951                  * @since 3.0.000 (2008-03-27)
952                  */
953                 var $visibility="all";
954
955                 /**
956                  * Print visibility.
957                  * @access protected
958                  * @since 3.0.000 (2008-03-27)
959                  */
960                 var $n_ocg_print;
961
962                 /**
963                  * View visibility.
964                  * @access protected
965                  * @since 3.0.000 (2008-03-27)
966                  */
967                 var $n_ocg_view;
968
969                 /**
970                  * Array of transparency objects and parameters.
971                  * @access protected
972                  * @since 3.0.000 (2008-03-27)
973                  */
974                 var $extgstates;
975
976                 /**
977                  * Set the default JPEG compression quality (1-100)
978                  * @access protected
979                  * @since 3.0.000 (2008-03-27)
980                  */
981                 var $jpeg_quality;
982
983                 /**
984                  * Default cell height ratio.
985                  * @access protected
986                  * @since 3.0.014 (2008-05-23)
987                  */
988                 var $cell_height_ratio = K_CELL_HEIGHT_RATIO;
989
990                 /**
991                  * PDF viewer preferences.
992                  * @access protected
993                  * @since 3.1.000 (2008-06-09)
994                  */
995                 var $viewer_preferences;
996
997                 /**
998                  * A name object specifying how the document should be displayed when opened.
999                  * @access protected
1000                  * @since 3.1.000 (2008-06-09)
1001                  */
1002                 var $PageMode;
1003
1004                 /**
1005                  * Array for storing gradient information.
1006                  * @access protected
1007                  * @since 3.1.000 (2008-06-09)
1008                  */
1009                 var $gradients = array();
1010
1011                 /**
1012                  * Array used to store positions inside the pages buffer.
1013                  * keys are the page numbers
1014                  * @access protected
1015                  * @since 3.2.000 (2008-06-26)
1016                  */
1017                 var $intmrk = array();
1018
1019                 /**
1020                  * Array used to store footer positions of each page.
1021                  * @access protected
1022                  * @since 3.2.000 (2008-07-01)
1023                  */
1024                 var $footerpos = array();
1025
1026
1027                 /**
1028                  * Array used to store footer lenght of each page.
1029                  * @access protected
1030                  * @since 4.0.014 (2008-07-29)
1031                  */
1032                 var $footerlen = array();
1033
1034                 /**
1035                  * True if a newline is created.
1036                  * @access protected
1037                  * @since 3.2.000 (2008-07-01)
1038                  */
1039                 var $newline = true;
1040
1041                 /**
1042                  * End position of the latest inserted line
1043                  * @access protected
1044                  * @since 3.2.000 (2008-07-01)
1045                  */
1046                 var $endlinex = 0;
1047
1048                 /**
1049                  * PDF string for last line width
1050                  * @access protected
1051                  * @since 4.0.006 (2008-07-16)
1052                  */
1053                 var $linestyleWidth = "";
1054
1055                 /**
1056                  * PDF string for last line width
1057                  * @access protected
1058                  * @since 4.0.006 (2008-07-16)
1059                  */
1060                 var $linestyleCap = "0 J";
1061
1062                 /**
1063                  * PDF string for last line width
1064                  * @access protected
1065                  * @since 4.0.006 (2008-07-16)
1066                  */
1067                 var $linestyleJoin = "0 j";
1068
1069                 /**
1070                  * PDF string for last line width
1071                  * @access protected
1072                  * @since 4.0.006 (2008-07-16)
1073                  */
1074                 var $linestyleDash = "[] 0 d";
1075
1076                 /**
1077                  * True if marked-content sequence is open
1078                  * @access protected
1079                  * @since 4.0.013 (2008-07-28)
1080                  */
1081                 var $openMarkedContent = false;
1082
1083                 /**
1084                  * Count the latest inserted vertical spaces on HTML.
1085                  * @access protected
1086                  * @since 4.0.021 (2008-08-24)
1087                  */
1088                 var $htmlvspace = 0;
1089                 
1090                 /**
1091                  * Array of Spot colors
1092                  * @access protected
1093                  * @since 4.0.024 (2008-09-12)
1094                  */
1095                 var $spot_colors = array();
1096
1097                 //------------------------------------------------------------
1098                 // METHODS
1099                 //------------------------------------------------------------
1100
1101                 /**
1102                  * This is the class constructor.
1103                  * It allows to set up the page format, the orientation and
1104                  * the measure unit used in all the methods (except for the font sizes).
1105                  * @since 1.0
1106                  * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1107                  * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1108                  * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1109                  * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1110                  * @param String $encoding charset encoding; default is UTF-8
1111                  */
1112                 function TCPDF($orientation='P', $unit='mm', $format='A4', $uni=true, $encoding="UTF-8") {
1113                         if ($uni) // Fix for FrontAccounting
1114                         {
1115                                 global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
1116                                 include_once(dirname(__FILE__)."/unicode_data2.php");
1117                         }
1118                         /* Set internal character encoding to ASCII */
1119                         if (function_exists("mb_internal_encoding") AND mb_internal_encoding()) {
1120                                 $this->internal_encoding = mb_internal_encoding();
1121                                 mb_internal_encoding("ASCII");
1122                         }
1123                         // set language direction
1124                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
1125                         $this->tmprtl = false;
1126                         //Some checks
1127                         $this->_dochecks();
1128                         //Initialization of properties
1129                         $this->isunicode = $uni;
1130                         $this->page = 0;
1131                         $this->pagedim = array();
1132                         $this->n = 2;
1133                         $this->buffer = '';
1134                         $this->pages = array();
1135                         $this->state = 0;
1136                         $this->fonts = array();
1137                         $this->FontFiles = array();
1138                         $this->diffs = array();
1139                         $this->images = array();
1140                         $this->links = array();
1141                         $this->gradients = array();
1142                         $this->InFooter = false;
1143                         $this->lasth = 0;
1144                         $this->FontFamily = 'helvetica';
1145                         $this->FontStyle = '';
1146                         $this->FontSizePt = 12;
1147                         $this->underline = false;
1148                         $this->linethrough = false;
1149                         $this->DrawColor = '0 G';
1150                         $this->FillColor = '0 g';
1151                         $this->TextColor = '0 g';
1152                         $this->ColorFlag = false;
1153                         $this->ws = 0;
1154                         // encryption values
1155                         $this->encrypted = false;
1156                         $this->last_rc4_key = '';
1157                         $this->padding = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A";
1158                         //Standard Unicode fonts
1159                         $this->CoreFonts = array(
1160                                 'courier'=>'Courier',
1161                                 'courierB'=>'Courier-Bold',
1162                                 'courierI'=>'Courier-Oblique',
1163                                 'courierBI'=>'Courier-BoldOblique',
1164                                 'helvetica'=>'Helvetica',
1165                                 'helveticaB'=>'Helvetica-Bold',
1166                                 'helveticaI'=>'Helvetica-Oblique',
1167                                 'helveticaBI'=>'Helvetica-BoldOblique',
1168                                 'times'=>'Times-Roman',
1169                                 'timesB'=>'Times-Bold',
1170                                 'timesI'=>'Times-Italic',
1171                                 'timesBI'=>'Times-BoldItalic',
1172                                 'symbol'=>'Symbol',
1173                                 'zapfdingbats'=>'ZapfDingbats'
1174                         );
1175                         //Set scale factor
1176                         $this->setPageUnit($unit);
1177                         // set page format and orientation
1178                         $this->setPageFormat($format, $orientation);
1179                         //Page margins (1 cm)
1180                         $margin = 28.35 / $this->k;
1181                         $this->SetMargins($margin,$margin);
1182                         //Interior cell margin (1 mm)
1183                         $this->cMargin = $margin / 10;
1184                         //Line width (0.2 mm)
1185                         $this->LineWidth = 0.57 / $this->k;
1186                         $this->linestyleWidth = sprintf('%.2f w', ($this->LineWidth * $this->k));
1187                         $this->linestyleCap = "0 J";
1188                         $this->linestyleJoin = "0 j";
1189                         $this->linestyleDash = "[] 0 d";
1190                         //Automatic page break
1191                         $this->SetAutoPageBreak(true, 2*$margin);
1192                         //Full width display mode
1193                         $this->SetDisplayMode('fullwidth');
1194                         //Compression
1195                         $this->SetCompression(true);
1196                         //Set default PDF version number
1197                         $this->PDFVersion = "1.7";
1198                         $this->encoding = $encoding;
1199                         $this->HREF = '';
1200                         $this->getFontsList();
1201                         $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1202                         $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1203                         $this->extgstates = array();
1204                         // user's rights
1205                         $this->ur = false;
1206                         $this->ur_document = "/FullSave";
1207                         $this->ur_annots = "/Create/Delete/Modify/Copy/Import/Export";
1208                         $this->ur_form = "/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate";
1209                         $this->ur_signature = "/Modify";
1210                         // set default JPEG quality
1211                         $this->jpeg_quality = 75;
1212                         // initialize some settings
1213                         $this->utf8Bidi(array(""));
1214                 }
1215
1216                 /**
1217                  * Default destructor.
1218                  * @since 1.53.0.TC016
1219                  */
1220                 function TCPDFDestruct() {
1221                         // restore internal encoding
1222                         if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1223                                 mb_internal_encoding($this->internal_encoding);
1224                         }
1225                 }
1226
1227                 /**
1228                 * Set the units of measure for the document.
1229                 * @param string $unit User measure unit. Possible values are:<ul><li>pt: point</li><li>mm: millimeter (default)</li><li>cm: centimeter</li><li>in: inch</li></ul><br />A point equals 1/72 of inch, that is to say about 0.35 mm (an inch being 2.54 cm). This is a very common unit in typography; font sizes are expressed in that unit.
1230                 * @since 3.0.015 (2008-06-06)
1231                 */
1232                 function setPageUnit($unit) {
1233                 //Set scale factor
1234                         switch (strtolower($unit)) {
1235                                 // points
1236                                 case 'pt': {
1237                                         $this->k = 1;
1238                                         break;
1239                                 }
1240                                 // millimeters
1241                                 case 'mm': {
1242                                         $this->k = $this->dpi / 25.4;
1243                                         break;
1244                                 }
1245                                 // centimeters
1246                                 case 'cm': {
1247                                         $this->k = $this->dpi / 2.54;
1248                                         break;
1249                                 }
1250                                 // inches
1251                                 case 'in': {
1252                                         $this->k = $this->dpi;
1253                                         break;
1254                                 }
1255                                 // unsupported unit
1256                                 default : {
1257                                         $this->Error('Incorrect unit: '.$unit);
1258                                         break;
1259                                 }
1260                         }
1261                         if (isset($this->CurOrientation)) {
1262                                         $this->setPageOrientation($this->CurOrientation);
1263                         }
1264                 }
1265
1266                 /**
1267                 * Set the page format
1268                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1269                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1270                 * @since 3.0.015 (2008-06-06)
1271                 */
1272                 function setPageFormat($format, $orientation="P") {
1273                         //Page format
1274                         if (is_string($format)) {
1275                                 // Page formats (45 standard ISO paper formats and 4 american common formats).
1276                                 // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1277                                 switch (strtoupper($format)){
1278                                         case '4A0': {$format = array(4767.87,6740.79); break;}
1279                                         case '2A0': {$format = array(3370.39,4767.87); break;}
1280                                         case 'A0': {$format = array(2383.94,3370.39); break;}
1281                                         case 'A1': {$format = array(1683.78,2383.94); break;}
1282                                         case 'A2': {$format = array(1190.55,1683.78); break;}
1283                                         case 'A3': {$format = array(841.89,1190.55); break;}
1284                                         case 'A4': default: {$format = array(595.28,841.89); break;}
1285                                         case 'A5': {$format = array(419.53,595.28); break;}
1286                                         case 'A6': {$format = array(297.64,419.53); break;}
1287                                         case 'A7': {$format = array(209.76,297.64); break;}
1288                                         case 'A8': {$format = array(147.40,209.76); break;}
1289                                         case 'A9': {$format = array(104.88,147.40); break;}
1290                                         case 'A10': {$format = array(73.70,104.88); break;}
1291                                         case 'B0': {$format = array(2834.65,4008.19); break;}
1292                                         case 'B1': {$format = array(2004.09,2834.65); break;}
1293                                         case 'B2': {$format = array(1417.32,2004.09); break;}
1294                                         case 'B3': {$format = array(1000.63,1417.32); break;}
1295                                         case 'B4': {$format = array(708.66,1000.63); break;}
1296                                         case 'B5': {$format = array(498.90,708.66); break;}
1297                                         case 'B6': {$format = array(354.33,498.90); break;}
1298                                         case 'B7': {$format = array(249.45,354.33); break;}
1299                                         case 'B8': {$format = array(175.75,249.45); break;}
1300                                         case 'B9': {$format = array(124.72,175.75); break;}
1301                                         case 'B10': {$format = array(87.87,124.72); break;}
1302                                         case 'C0': {$format = array(2599.37,3676.54); break;}
1303                                         case 'C1': {$format = array(1836.85,2599.37); break;}
1304                                         case 'C2': {$format = array(1298.27,1836.85); break;}
1305                                         case 'C3': {$format = array(918.43,1298.27); break;}
1306                                         case 'C4': {$format = array(649.13,918.43); break;}
1307                                         case 'C5': {$format = array(459.21,649.13); break;}
1308                                         case 'C6': {$format = array(323.15,459.21); break;}
1309                                         case 'C7': {$format = array(229.61,323.15); break;}
1310                                         case 'C8': {$format = array(161.57,229.61); break;}
1311                                         case 'C9': {$format = array(113.39,161.57); break;}
1312                                         case 'C10': {$format = array(79.37,113.39); break;}
1313                                         case 'RA0': {$format = array(2437.80,3458.27); break;}
1314                                         case 'RA1': {$format = array(1729.13,2437.80); break;}
1315                                         case 'RA2': {$format = array(1218.90,1729.13); break;}
1316                                         case 'RA3': {$format = array(864.57,1218.90); break;}
1317                                         case 'RA4': {$format = array(609.45,864.57); break;}
1318                                         case 'SRA0': {$format = array(2551.18,3628.35); break;}
1319                                         case 'SRA1': {$format = array(1814.17,2551.18); break;}
1320                                         case 'SRA2': {$format = array(1275.59,1814.17); break;}
1321                                         case 'SRA3': {$format = array(907.09,1275.59); break;}
1322                                         case 'SRA4': {$format = array(637.80,907.09); break;}
1323                                         case 'LETTER': {$format = array(612.00,792.00); break;}
1324                                         case 'LEGAL': {$format = array(612.00,1008.00); break;}
1325                                         case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1326                                         case 'FOLIO': {$format = array(612.00,936.00); break;}
1327                                 }
1328                                 $this->fwPt = $format[0];
1329                                 $this->fhPt = $format[1];
1330                         }
1331                         else {
1332                                 $this->fwPt = $format[0] * $this->k;
1333                                 $this->fhPt = $format[1] * $this->k;
1334                         }
1335                         $this->setPageOrientation($orientation);
1336                 }
1337
1338
1339                 /**
1340                 * Set page orientation.
1341                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1342                 * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1343                 * @param float $bottommargin bottom margin of the page.
1344                 * @since 3.0.015 (2008-06-06)
1345                 */
1346                 function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1347                         $orientation = strtoupper($orientation);
1348                         if (($orientation == 'P') OR ($orientation == 'PORTRAIT')) {
1349                                 $this->CurOrientation = 'P';
1350                                 $this->wPt = $this->fwPt;
1351                                 $this->hPt = $this->fhPt;
1352                         } elseif (($orientation == 'L') OR ($orientation == 'LANDSCAPE')) {
1353                                 $this->CurOrientation = 'L';
1354                                 $this->wPt = $this->fhPt;
1355                                 $this->hPt = $this->fwPt;
1356                         }
1357                         else {
1358                                 $this->Error('Incorrect orientation: '.$orientation);
1359                         }
1360                         $this->w = $this->wPt / $this->k;
1361                         $this->h = $this->hPt / $this->k;
1362                         if (empty($autopagebreak)) {
1363                                 if (isset($this->AutoPageBreak)) {
1364                                         $autopagebreak = $this->AutoPageBreak;
1365                                 } else {
1366                                         $autopagebreak = true;
1367                                 }
1368                         }
1369                         if (empty($bottommargin)) {
1370                                 if (isset($this->bMargin)) {
1371                                         $bottommargin = $this->bMargin;
1372                                 } else {
1373                                         // default value = 2 cm
1374                                         $bottommargin = 2 * 28.35 / $this->k;
1375                                 }
1376                         }
1377                         $this->SetAutoPageBreak($autopagebreak, $bottommargin);
1378                         // store page dimensions
1379                         $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation);
1380                 }
1381
1382                 /**
1383                  * Enable or disable Right-To-Left language mode
1384                  * @param Boolean $enable if true enable Right-To-Left language mode.
1385                  * @since 2.0.000 (2008-01-03)
1386                  */
1387                 function setRTL($enable) {
1388                         $this->rtl = $enable ? true : false;
1389                         $this->tmprtl = false;
1390                 }
1391
1392                 /**
1393                  * Return the RTL status
1394                  * @return boolean
1395                  * @since 4.0.012 (2008-07-24)
1396                  */
1397                 function getRTL() {
1398                         return $this->rtl;
1399                 }
1400
1401                 /**
1402                 * Force temporary RTL language direction
1403                 * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
1404                 * @since 2.1.000 (2008-01-09)
1405                 */
1406                 function setTempRTL($mode) {
1407                         switch ($mode) {
1408                                 case false:
1409                                 case 'L':
1410                                 case 'R': {
1411                                         $this->tmprtl = $mode;
1412                                 }
1413                         }
1414                 }
1415
1416                 /**
1417                 * Set the last cell height.
1418                 * @param float $h cell height.
1419                 * @author Nicola Asuni
1420                 * @since 1.53.0.TC034
1421                 */
1422                 function setLastH($h) {
1423                         $this->lasth = $h;
1424                 }
1425
1426                 /**
1427                 * Get the last cell height.
1428                 * @return last cell height
1429                 * @since 4.0.017 (2008-08-05)
1430                 */
1431                 function getLastH() {
1432                         return $this->lasth;
1433                 }
1434
1435                 /**
1436                 * Set the image scale.
1437                 * @param float $scale image scale.
1438                 * @author Nicola Asuni
1439                 * @since 1.5.2
1440                 */
1441                 function setImageScale($scale) {
1442                         $this->imgscale = $scale;
1443                 }
1444
1445                 /**
1446                 * Returns the image scale.
1447                 * @return float image scale.
1448                 * @author Nicola Asuni
1449                 * @since 1.5.2
1450                 */
1451                 function getImageScale() {
1452                         return $this->imgscale;
1453                 }
1454
1455                 /**
1456                 * Returns the page width in units.
1457                 * @return int page width.
1458                 * @author Nicola Asuni
1459                 * @since 1.5.2
1460                 */
1461                 function getPageWidth() {
1462                         return $this->w;
1463                 }
1464
1465                 /**
1466                 * Returns the page height in units.
1467                 * @return int page height.
1468                 * @author Nicola Asuni
1469                 * @since 1.5.2
1470                 */
1471                 function getPageHeight() {
1472                         return $this->h;
1473                 }
1474
1475                 /**
1476                 * Returns the page break margin.
1477                 * @return int page break margin.
1478                 * @author Nicola Asuni
1479                 * @since 1.5.2
1480                 */
1481                 function getBreakMargin() {
1482                         return $this->bMargin;
1483                 }
1484
1485                 /**
1486                 * Returns the scale factor (number of points in user unit).
1487                 * @return int scale factor.
1488                 * @author Nicola Asuni
1489                 * @since 1.5.2
1490                 */
1491                 function getScaleFactor() {
1492                         return $this->k;
1493                 }
1494
1495                 /**
1496                 * Defines the left, top and right margins. By default, they equal 1 cm. Call this method to change them.
1497                 * @param float $left Left margin.
1498                 * @param float $top Top margin.
1499                 * @param float $right Right margin. Default value is the left one.
1500                 * @since 1.0
1501                 * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
1502                 */
1503                 function SetMargins($left, $top, $right=-1) {
1504                         //Set left, top and right margins
1505                         $this->lMargin = $left;
1506                         $this->tMargin = $top;
1507                         if ($right == -1) {
1508                                 $right = $left;
1509                         }
1510                         $this->rMargin = $right;
1511                 }
1512
1513                 /**
1514                 * Defines the left margin. The method can be called before creating the first page. If the current abscissa gets out of page, it is brought back to the margin.
1515                 * @param float $margin The margin.
1516                 * @since 1.4
1517                 * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1518                 */
1519                 function SetLeftMargin($margin) {
1520                         //Set left margin
1521                         $this->lMargin=$margin;
1522                         if (($this->page > 0) AND ($this->x < $margin)) {
1523                                 $this->x = $margin;
1524                         }
1525                 }
1526
1527                 /**
1528                 * Defines the top margin. The method can be called before creating the first page.
1529                 * @param float $margin The margin.
1530                 * @since 1.5
1531                 * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
1532                 */
1533                 function SetTopMargin($margin) {
1534                         //Set top margin
1535                         $this->tMargin=$margin;
1536                         if (($this->page > 0) AND ($this->y < $margin)) {
1537                                 $this->y = $margin;
1538                         }
1539                 }
1540
1541                 /**
1542                 * Defines the right margin. The method can be called before creating the first page.
1543                 * @param float $margin The margin.
1544                 * @since 1.5
1545                 * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1546                 */
1547                 function SetRightMargin($margin) {
1548                         $this->rMargin=$margin;
1549                         if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
1550                                 $this->x = $this->w - $margin;
1551                         }
1552                 }
1553
1554                 /**
1555                 * Set the internal Cell padding.
1556                 * @param float $pad internal padding.
1557                 * @since 2.1.000 (2008-01-09)
1558                 * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
1559                 */
1560                 function SetCellPadding($pad) {
1561                         $this->cMargin = $pad;
1562                 }
1563
1564                 /**
1565                 * Enables or disables the automatic page breaking mode. When enabling, the second parameter is the distance from the bottom of the page that defines the triggering limit. By default, the mode is on and the margin is 2 cm.
1566                 * @param boolean $auto Boolean indicating if mode should be on or off.
1567                 * @param float $margin Distance from the bottom of the page.
1568                 * @since 1.0
1569                 * @see Cell(), MultiCell(), AcceptPageBreak()
1570                 */
1571                 function SetAutoPageBreak($auto, $margin=0) {
1572                         //Set auto page break mode and triggering margin
1573                         $this->AutoPageBreak = $auto;
1574                         $this->bMargin = $margin;
1575                         $this->PageBreakTrigger = $this->h - $margin;
1576                 }
1577
1578                 /**
1579                 * Defines the way the document is to be displayed by the viewer.
1580                 * @param mixed $zoom The zoom to use. It can be one of the following string values or a number indicating the zooming factor to use. <ul><li>fullpage: displays the entire page on screen </li><li>fullwidth: uses maximum width of window</li><li>real: uses real size (equivalent to 100% zoom)</li><li>default: uses viewer default mode</li></ul>
1581                 * @param string $layout The page layout. Possible values are:<ul><li>SinglePage Display one page at a time</li><li>OneColumn Display the pages in one column</li><li>TwoColumnLeft Display the pages in two columns, with odd-numbered pages on the left</li><li>TwoColumnRight Display the pages in two columns, with odd-numbered pages on the right</li><li>TwoPageLeft (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the left</li><li>TwoPageRight (PDF 1.5) Display the pages two at a time, with odd-numbered pages on the right</li></ul>
1582                 * @param string $mode A name object specifying how the document should be displayed when opened:<ul><li>UseNone Neither document outline nor thumbnail images visible</li><li>UseOutlines Document outline visible</li><li>UseThumbs Thumbnail images visible</li><li>FullScreen Full-screen mode, with no menu bar, window controls, or any other window visible</li><li>UseOC (PDF 1.5) Optional content group panel visible</li><li>UseAttachments (PDF 1.6) Attachments panel visible</li></ul>
1583                 * @since 1.2
1584                 */
1585                 function SetDisplayMode($zoom, $layout='SinglePage', $mode="UseNone") {
1586                         //Set display mode in viewer
1587                         if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
1588                                 $this->ZoomMode = $zoom;
1589                         } else {
1590                                 $this->Error('Incorrect zoom display mode: '.$zoom);
1591                         }
1592                         switch ($layout) {
1593                                 case "default":
1594                                 case "single":
1595                                 case "SinglePage": {
1596                                         $this->LayoutMode = "SinglePage";
1597                                         break;
1598                                 }
1599                                 case "continuous":
1600                                 case "OneColumn": {
1601                                         $this->LayoutMode = "OneColumn";
1602                                         break;
1603                                 }
1604                                 case "two":
1605                                 case "TwoColumnLeft": {
1606                                         $this->LayoutMode = "TwoColumnLeft";
1607                                         break;
1608                                 }
1609                                 case "TwoColumnRight": {
1610                                         $this->LayoutMode = "TwoColumnRight";
1611                                         break;
1612                                 }
1613                                 case "TwoPageLeft": {
1614                                         $this->LayoutMode = "TwoPageLeft";
1615                                         break;
1616                                 }
1617                                 case "TwoPageRight": {
1618                                         $this->LayoutMode = "TwoPageRight";
1619                                         break;
1620                                 }
1621                                 default: {
1622                                         $this->LayoutMode = "SinglePage";
1623                                 }
1624                         }
1625                         switch ($mode) {
1626                                 case "UseNone": {
1627                                         $this->PageMode = "UseNone";
1628                                         break;
1629                                 }
1630                                 case "UseOutlines": {
1631                                         $this->PageMode = "UseOutlines";
1632                                         break;
1633                                 }
1634                                 case "UseThumbs": {
1635                                         $this->PageMode = "UseThumbs";
1636                                         break;
1637                                 }
1638                                 case "FullScreen": {
1639                                         $this->PageMode = "FullScreen";
1640                                         break;
1641                                 }
1642                                 case "UseOC": {
1643                                         $this->PageMode = "UseOC";
1644                                         break;
1645                                 }
1646                                 case "": {
1647                                         $this->PageMode = "UseAttachments";
1648                                         break;
1649                                 }
1650                                 default: {
1651                                         $this->PageMode = "UseNone";
1652                                 }
1653                         }
1654                 }
1655
1656                 /**
1657                 * Activates or deactivates page compression. When activated, the internal representation of each page is compressed, which leads to a compression ratio of about 2 for the resulting document. Compression is on by default.
1658                 * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
1659                 * @param boolean $compress Boolean indicating if compression must be enabled.
1660                 * @since 1.4
1661                 */
1662                 function SetCompression($compress) {
1663                         //Set page compression
1664                         if (function_exists('gzcompress')) {
1665                                 $this->compress = $compress;
1666                         } else {
1667                                 $this->compress = false;
1668                         }
1669                 }
1670
1671                 /**
1672                 * Defines the title of the document.
1673                 * @param string $title The title.
1674                 * @since 1.2
1675                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
1676                 */
1677                 function SetTitle($title) {
1678                         //Title of document
1679                         $this->title = $title;
1680                 }
1681
1682                 /**
1683                 * Defines the subject of the document.
1684                 * @param string $subject The subject.
1685                 * @since 1.2
1686                 * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
1687                 */
1688                 function SetSubject($subject) {
1689                         //Subject of document
1690                         $this->subject = $subject;
1691                 }
1692
1693                 /**
1694                 * Defines the author of the document.
1695                 * @param string $author The name of the author.
1696                 * @since 1.2
1697                 * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
1698                 */
1699                 function SetAuthor($author) {
1700                         //Author of document
1701                         $this->author = $author;
1702                 }
1703
1704                 /**
1705                 * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
1706                 * @param string $keywords The list of keywords.
1707                 * @since 1.2
1708                 * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
1709                 */
1710                 function SetKeywords($keywords) {
1711                         //Keywords of document
1712                         $this->keywords = $keywords;
1713                 }
1714
1715                 /**
1716                 * Defines the creator of the document. This is typically the name of the application that generates the PDF.
1717                 * @param string $creator The name of the creator.
1718                 * @since 1.2
1719                 * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
1720                 */
1721                 function SetCreator($creator) {
1722                         //Creator of document
1723                         $this->creator = $creator;
1724                 }
1725
1726                 /**
1727                 * Defines an alias for the total number of pages. It will be substituted as the document is closed.<br />
1728                 * @param string $alias The alias. Default value: {nb}.
1729                 * @since 1.4
1730                 * @see getAliasNbPages(), PageNo(), Footer()
1731                 */
1732                 function AliasNbPages($alias='{nb}') {
1733                         //Define an alias for total number of pages
1734                         $this->AliasNbPages = $alias;
1735                 }
1736                 
1737                 /**
1738                  * Returns the string alias used for the total number of pages.
1739          * If the current font is unicode type, the returned string is surrounded by additional curly braces.
1740                  * @return string
1741                  * @since 4.0.018 (2008-08-08)
1742                  * @see AliasNbPages(), PageNo(), Footer()
1743                 */
1744                 function getAliasNbPages() {
1745                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
1746                                 return "{".$this->AliasNbPages."}";
1747             }
1748                         return $this->AliasNbPages;
1749                 }
1750
1751                 /**
1752                 * This method is automatically called in case of fatal error; it simply outputs the message and halts the execution. An inherited class may override it to customize the error handling but should always halt the script, or the resulting document would probably be invalid.
1753                 * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
1754                 * @param string $msg The error message
1755                 * @since 1.0
1756                 */
1757                 function Error($msg) {
1758                         //Fatal error
1759                         display_error('<strong>TCPDF error: </strong>'.$msg);
1760                         exit;
1761                 }
1762
1763                 /**
1764                 * This method begins the generation of the PDF document. It is not necessary to call it explicitly because AddPage() does it automatically.
1765                 * Note: no page is created by this method
1766                 * @since 1.0
1767                 * @see AddPage(), Close()
1768                 */
1769                 function Open() {
1770                         //Begin document
1771                         $this->state = 1;
1772                 }
1773
1774                 /**
1775                 * Terminates the PDF document. It is not necessary to call this method explicitly because Output() does it automatically. If the document contains no page, AddPage() is called to prevent from getting an invalid document.
1776                 * @since 1.0
1777                 * @see Open(), Output()
1778                 */
1779                 function Close() {
1780                         //Terminate document
1781                         if ($this->state == 3) {
1782                                 return;
1783                         }
1784                         if ($this->page == 0) {
1785                                 $this->AddPage();
1786                         }
1787                         //Page footer
1788                         $this->setFooter();
1789                         //Close page
1790                         $this->_endpage();
1791                         //Close document
1792                         $this->_enddoc();
1793                 }
1794
1795                 /**
1796                 * Move pointer at the specified document page and update page dimensions.
1797                 * @param int $pnum page number
1798                 * @param boolean $resetmargins if true reset left, right, top margins and Y position.
1799                 * @since 2.1.000 (2008-01-07)
1800                 * @see getPage(), lastpage(), getNumPages()
1801                 */
1802                 function setPage($pnum, $resetmargins=false) {
1803                         if (($pnum > 0) AND ($pnum <= count($this->pages))) {
1804                                 $this->page = $pnum;
1805                                 $this->wPt = $this->pagedim[$this->page]['w'];
1806                                 $this->hPt = $this->pagedim[$this->page]['h'];
1807                                 $this->w = $this->wPt / $this->k;
1808                                 $this->h = $this->hPt / $this->k;
1809                                 $this->tMargin = $this->pagedim[$this->page]['tm'];
1810                                 $this->bMargin = $this->pagedim[$this->page]['bm'];
1811                                 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
1812                                 $this->CurOrientation = $this->pagedim[$this->page]['or'];
1813                                 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
1814                                 if ($resetmargins) {
1815                                         $this->lMargin = $this->pagedim[$this->page]['lm'];
1816                                         $this->rMargin = $this->pagedim[$this->page]['rm'];
1817                                         $this->SetY($this->tMargin);
1818                                 }
1819                         } else {
1820                                 $this->Error('Wrong page number on setPage() function.');
1821                         }
1822                 }
1823
1824                 /**
1825                 * Reset pointer to the last document page.
1826                 * @since 2.0.000 (2008-01-04)
1827                 * @see setPage(), getPage(), getNumPages()
1828                 */
1829                 function lastPage() {
1830                         $this->setPage($this->getNumPages());
1831                 }
1832
1833                 /**
1834                 * Get current document page number.
1835                 * @return int page number
1836                 * @since 2.1.000 (2008-01-07)
1837                 * @see setPage(), lastpage(), getNumPages()
1838                 */
1839                 function getPage() {
1840                         return $this->page;
1841                 }
1842
1843
1844                 /**
1845                 * Get the total number of insered pages.
1846                 * @return int number of pages
1847                 * @since 2.1.000 (2008-01-07)
1848                 * @see setPage(), getPage(), lastpage()
1849                 */
1850                 function getNumPages() {
1851                         return count($this->pages);
1852                 }
1853
1854                 /**
1855                 * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer. Then the page is added, the current position set to the top-left corner according to the left and top margins, and Header() is called to display the header.
1856                 * The font which was set before calling is automatically restored. There is no need to call SetFont() again if you want to continue with the same font. The same is true for colors and line width.
1857                 * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
1858                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1859                 * @param mixed $format The format used for pages. It can be either one of the following values (case insensitive) or a custom format in the form of a two-element array containing the width and the height (expressed in the unit given by unit).<ul><li>4A0</li><li>2A0</li><li>A0</li><li>A1</li><li>A2</li><li>A3</li><li>A4 (default)</li><li>A5</li><li>A6</li><li>A7</li><li>A8</li><li>A9</li><li>A10</li><li>B0</li><li>B1</li><li>B2</li><li>B3</li><li>B4</li><li>B5</li><li>B6</li><li>B7</li><li>B8</li><li>B9</li><li>B10</li><li>C0</li><li>C1</li><li>C2</li><li>C3</li><li>C4</li><li>C5</li><li>C6</li><li>C7</li><li>C8</li><li>C9</li><li>C10</li><li>RA0</li><li>RA1</li><li>RA2</li><li>RA3</li><li>RA4</li><li>SRA0</li><li>SRA1</li><li>SRA2</li><li>SRA3</li><li>SRA4</li><li>LETTER</li><li>LEGAL</li><li>EXECUTIVE</li><li>FOLIO</li></ul>
1860                 * @since 1.0
1861                 * @see TCPDF(), Header(), Footer(), SetMargins()
1862                 */
1863                 function AddPage($orientation='', $format='') {
1864                         if (!isset($this->original_lMargin)) {
1865                                 $this->original_lMargin = $this->lMargin;
1866                         }
1867                         if (!isset($this->original_rMargin)) {
1868                                 $this->original_rMargin = $this->rMargin;
1869                         }
1870                         if (count($this->pages) > $this->page) {
1871                                 // this page has been already added
1872                                 $this->setPage(($this->page + 1));
1873                                 $this->SetY($this->tMargin);
1874                                 return;
1875                         }
1876                         //Start a new page
1877                         if ($this->state == 0) {
1878                                 $this->Open();
1879                         }
1880                         // save current settings
1881                         $font_family = $this->FontFamily;
1882                         $font_style = $this->FontStyle.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '');
1883                         $font_size = $this->FontSizePt;
1884                         $prev_rMargin = $this->rMargin;
1885                         $prev_lMargin = $this->lMargin;
1886                         $prev_cMargin = $this->cMargin;
1887                         $prev_linestyleWidth = $this->linestyleWidth;
1888                         $prev_linestyleCap = $this->linestyleCap;
1889                         $prev_linestyleJoin = $this->linestyleJoin;
1890                         $prev_linestyleDash = $this->linestyleDash;
1891                         $prev_DrawColor = $this->DrawColor;
1892                         $prev_FillColor = $this->FillColor;
1893                         $prev_TextColor = $this->TextColor;
1894                         $prev_ColorFlag = $this->ColorFlag;
1895                         if ($this->page > 0) {
1896                                 //Page footer
1897                                 $this->setFooter();
1898                                 //Close page
1899                                 $this->_endpage();
1900                         }
1901                         //Start new page
1902                         $this->_beginpage($orientation, $format);
1903                         // restore graphic styles
1904                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1905                         if (!empty($font_family)) {
1906                                 $this->SetFont($font_family, $font_style, $font_size);
1907                         }
1908                         //Page header
1909                         $this->setHeader();
1910                         // restore graphic styles
1911                         $this->_out("".$prev_linestyleWidth." ".$prev_linestyleCap." ".$prev_linestyleJoin." ".$prev_linestyleDash." ".$prev_DrawColor." ".$prev_FillColor."");
1912                         if (!empty($font_family)) {
1913                                 $this->SetFont($font_family, $font_style, $font_size);
1914                         }
1915                         // restore settings
1916                         $this->FontFamily = $font_family;
1917                         $this->FontStyle = $font_style;
1918                         $this->FontSizePt = $font_size;
1919                         $this->rMargin = $prev_rMargin;
1920                         $this->lMargin = $prev_lMargin;
1921                         $this->cMargin = $prev_cMargin;
1922                         $this->linestyleWidth = $prev_linestyleWidth;
1923                         $this->linestyleCap = $prev_linestyleCap;
1924                         $this->linestyleJoin = $prev_linestyleJoin;
1925                         $this->linestyleDash = $prev_linestyleDash;
1926                         $this->DrawColor = $prev_DrawColor;
1927                         $this->FillColor = $prev_FillColor;
1928                         $this->TextColor = $prev_TextColor;
1929                         $this->ColorFlag = $prev_ColorFlag;
1930                         // mark this point
1931                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1932                 }
1933
1934                 /**
1935                  * Set start-writing mark on current page for multicell borders and fills.
1936                  * This function must be called after calling Image() function for a background image.
1937                  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
1938                  * @access public
1939                  * @since 4.0.016 (2008-07-30)
1940                  */
1941                 function setPageMark() {
1942                         $this->intmrk[$this->page] = strlen($this->pages[$this->page]);
1943                 }
1944
1945                 /**
1946                  * Set header data.
1947                  * @param string $ln header image logo
1948                  * @param string $lw header image logo width in mm
1949                  * @param string $ht string to print as title on document header
1950                  * @param string $hs string to print on document header
1951                 */
1952                 function setHeaderData($ln="", $lw=0, $ht="", $hs="") {
1953                         $this->header_logo = $ln;
1954                         $this->header_logo_width = $lw;
1955                         $this->header_title = $ht;
1956                         $this->header_string = $hs;
1957                 }
1958
1959                 /**
1960                  * Returns header data:
1961                  * <ul><li>$ret['logo'] = logo image</li><li>$ret['logo_width'] = width of the image logo in user units</li><li>$ret['title'] = header title</li><li>$ret['string'] = header description string</li></ul>
1962                  * @return array()
1963                  * @since 4.0.012 (2008-07-24)
1964                  */
1965                 function getHeaderData() {
1966                         $ret = array();
1967                         $ret['logo'] = $this->header_logo;
1968                         $ret['logo_width'] = $this->header_logo_width;
1969                         $ret['title'] = $this->header_title;
1970                         $ret['string'] = $this->header_string;
1971                         return $ret;
1972                 }
1973
1974                 /**
1975                  * Set header margin.
1976                  * (minimum distance between header and top page margin)
1977                  * @param int $hm distance in user units
1978                 */
1979                 function setHeaderMargin($hm=10) {
1980                         $this->header_margin = $hm;
1981                 }
1982
1983                 /**
1984                  * Returns header margin in user units.
1985                  * @return float
1986                  * @since 4.0.012 (2008-07-24)
1987                 */
1988                 function getHeaderMargin() {
1989                         return $this->header_margin;
1990                 }
1991
1992                 /**
1993                  * Set footer margin.
1994                  * (minimum distance between footer and bottom page margin)
1995                  * @param int $fm distance in user units
1996                 */
1997                 function setFooterMargin($fm=10) {
1998                         $this->footer_margin = $fm;
1999                 }
2000
2001                 /**
2002                  * Returns footer margin in user units.
2003                  * @return float
2004                  * @since 4.0.012 (2008-07-24)
2005                 */
2006                 function getFooterMargin() {
2007                         return $this->footer_margin;
2008                 }
2009                 /**
2010                  * Set a flag to print page header.
2011                  * @param boolean $val set to true to print the page header (default), false otherwise.
2012                  */
2013                 function setPrintHeader($val=true) {
2014                         $this->print_header = $val;
2015                 }
2016
2017                 /**
2018                  * Set a flag to print page footer.
2019                  * @param boolean $value set to true to print the page footer (default), false otherwise.
2020                  */
2021                 function setPrintFooter($val=true) {
2022                         $this->print_footer = $val;
2023                 }
2024
2025                 /**
2026                  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2027                  * @return float
2028                  */
2029                 function getImageRBX() {
2030                         return $this->img_rb_x;
2031                 }
2032
2033                 /**
2034                  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2035                  * @return float
2036                  */
2037                 function getImageRBY() {
2038                         return $this->img_rb_y;
2039                 }
2040
2041                 /**
2042                  * This method is used to render the page header.
2043                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2044                  */
2045                 function Header1() {
2046                         $ormargins = $this->getOriginalMargins();
2047                         $headerfont = $this->getHeaderFont();
2048                         $headerdata = $this->getHeaderData();
2049                         if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2050                                 $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2051                                 $imgy = $this->getImageRBY();
2052                         } else {
2053                                 $imgy = $this->GetY();
2054                         }
2055                         $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2056                         // set starting margin for text data cell
2057                         if ($this->getRTL()) {
2058                                 $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2059                         } else {
2060                                 $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2061                         }
2062                         $this->SetTextColor(0, 0, 0);
2063                         // header title
2064                         $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2065                         $this->SetX($header_x);
2066                         $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '');
2067                         // header string
2068                         $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2069                         $this->SetX($header_x);
2070                         $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, 0, 0, true, 0);
2071                         // print an ending header line
2072                         $this->SetLineStyle(array("width" => 0.85 / $this->getScaleFactor(), "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2073                         $this->SetY(1 + max($imgy, $this->GetY()));
2074                         if ($this->getRTL()) {
2075                                 $this->SetX($ormargins['right']);
2076                         } else {
2077                                 $this->SetX($ormargins['left']);
2078                         }
2079                         $this->Cell(0, 0, '', 'T', 0, 'C');
2080                 }
2081
2082                 /**
2083                  * This method is used to render the page footer.
2084                  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2085                  */
2086                 function Footer() {
2087                         $cur_y = $this->GetY();
2088                         $ormargins = $this->getOriginalMargins();
2089                         $this->SetTextColor(0, 0, 0);
2090                         //set style for cell border
2091                         $line_width = 0.85 / $this->getScaleFactor();
2092                         $this->SetLineStyle(array("width" => $line_width, "cap" => "butt", "join" => "miter", "dash" => 0, "color" => array(0, 0, 0)));
2093                         //print document barcode
2094                         $barcode = $this->getBarcode();
2095                         if (!empty($barcode)) {
2096                                 $this->Ln();
2097                                 $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2098                                 $this->write1DBarcode($barcode, "C128B", $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2099                         }
2100                         $pagenumtxt = $this->l['w_page']." ".$this->PageNo().' / '.$this->getAliasNbPages();
2101                         $this->SetY($cur_y);
2102                         //Print page number
2103                         if ($this->getRTL()) {
2104                                 $this->SetX($ormargins['right']);
2105                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2106                         } else {
2107                                 $this->SetX($ormargins['left']);
2108                                 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2109                         }
2110                 }
2111
2112                 /**
2113                  * This method is used to render the page header.
2114                  * @access protected
2115                  * @since 4.0.012 (2008-07-24)
2116                  */
2117                 function setHeader() {
2118                         if ($this->print_header) {
2119                                 $lasth = $this->lasth;
2120                                 $this->_out("q");
2121                                 $this->rMargin = $this->original_rMargin;
2122                                 $this->lMargin = $this->original_lMargin;
2123                                 //set current position
2124                                 if ($this->rtl) {
2125                                         $this->SetXY($this->original_rMargin, $this->header_margin);
2126                                 } else {
2127                                         $this->SetXY($this->original_lMargin, $this->header_margin);
2128                                 }
2129                                 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2130                                 $this->Header1();
2131                                 //restore position
2132                                 if ($this->rtl) {
2133                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2134                                 } else {
2135                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2136                                 }
2137                                 $this->_out("Q");
2138                                 $this->lasth = $lasth;
2139                         }
2140                 }
2141
2142                 /**
2143                  * This method is used to render the page footer.
2144                  * @access protected
2145                  * @since 4.0.012 (2008-07-24)
2146                  */
2147                 function setFooter() {
2148                         //Page footer
2149                         $this->InFooter = true;
2150                         // mark this point
2151                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
2152                         if ($this->print_footer) {
2153                                 $lasth = $this->lasth;
2154                                 $this->_out("q");
2155                                 $this->rMargin = $this->original_rMargin;
2156                                 $this->lMargin = $this->original_lMargin;
2157                                 //set current position
2158                                 $footer_y = $this->h - $this->footer_margin;
2159                                 if ($this->rtl) {
2160                                         $this->SetXY($this->original_rMargin, $footer_y);
2161                                 } else {
2162                                         $this->SetXY($this->original_lMargin, $footer_y);
2163                                 }
2164                                 $this->SetFont($this->footer_font[0], $this->footer_font[1] , $this->footer_font[2]);
2165                                 $this->Footer();
2166                                 //restore position
2167                                 if ($this->rtl) {
2168                                         $this->SetXY($this->original_rMargin, $this->tMargin);
2169                                 } else {
2170                                         $this->SetXY($this->original_lMargin, $this->tMargin);
2171                                 }
2172                                 $this->_out("Q");
2173                                 $this->lasth = $lasth;
2174                         }
2175                         $this->footerlen[$this->page] = strlen($this->pages[$this->page]) - $this->footerpos[$this->page];
2176                         $this->InFooter = false;
2177                 }
2178
2179                 /**
2180                 * Returns the current page number.
2181                 * @return int page number
2182                 * @since 1.0
2183                 * @see AliasNbPages(), getAliasNbPages()
2184                 */
2185                 function PageNo() {
2186                         return $this->page;
2187                 }
2188
2189                 /**
2190                 * Defines a new spot color. 
2191                 * It can be expressed in RGB components or gray scale. 
2192                 * The method can be called before the first page is created and the value is retained from page to page.
2193                 * @param int $c Cyan color for CMYK. Value between 0 and 255
2194                 * @param int $m Magenta color for CMYK. Value between 0 and 255
2195                 * @param int $y Yellow color for CMYK. Value between 0 and 255
2196                 * @param int $k Key (Black) color for CMYK. Value between 0 and 255
2197                 * @since 4.0.024 (2008-09-12)
2198                 * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2199                 */
2200                 function AddSpotColor($name, $c, $m, $y, $k) {
2201                         if (!isset($this->spot_colors[$name])) {
2202                                 $i = 1 + count($this->spot_colors);
2203                                 $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
2204                         }
2205                 }
2206
2207                 /**
2208                 * Defines the color used for all drawing operations (lines, rectangles and cell borders).
2209                 * It can be expressed in RGB components or gray scale.
2210                 * The method can be called before the first page is created and the value is retained from page to page.
2211                 * @param array $color array of colors
2212                 * @since 3.1.000 (2008-06-11)
2213                 * @see SetDrawColor()
2214                 */
2215                 function SetDrawColorArray($color) {
2216                         if (isset($color)) {
2217                                 $color = array_values($color);
2218                                 $r = isset($color[0]) ? $color[0] : -1;
2219                                 $g = isset($color[1]) ? $color[1] : -1;
2220                                 $b = isset($color[2]) ? $color[2] : -1;
2221                                 $k = isset($color[3]) ? $color[3] : -1;
2222                                 if ($r >= 0) {
2223                                         $this->SetDrawColor($r, $g, $b, $k);
2224                                 }
2225                         }
2226                 }
2227
2228                 /**
2229                 * Defines the color used for all drawing operations (lines, rectangles and cell borders). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2230                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2231                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2232                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2233                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2234                 * @since 1.3
2235                 * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
2236                 */
2237                 function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2238                         // set default values
2239                         if (!is_numeric($col1)) {
2240                                 $col1 = 0;
2241                         }
2242                         if (!is_numeric($col2)) {
2243                                 $col2 = -1;
2244                         }
2245                         if (!is_numeric($col3)) {
2246                                 $col3 = -1;
2247                         }
2248                         if (!is_numeric($col4)) {
2249                                 $col4 = -1;
2250                         }
2251                         //Set color for all stroking operations
2252                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2253                                 // Grey scale
2254                                 $this->DrawColor = sprintf('%.3f G', $col1/255);
2255                         } elseif ($col4 == -1) {
2256                                 // RGB
2257                                 $this->DrawColor = sprintf('%.3f %.3f %.3f RG', $col1/255, $col2/255, $col3/255);
2258                         } else {
2259                                 // CMYK
2260                                 $this->DrawColor = sprintf('%.3f %.3f %.3f %.3f K', $col1/100, $col2/100, $col3/100, $col4/100);
2261                         }
2262                         if ($this->page > 0) {
2263                                 $this->_out($this->DrawColor);
2264                         }
2265                 }
2266                 
2267                 /**
2268                 * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
2269                 * @param string $name name of the spot color
2270                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2271                 * @since 4.0.024 (2008-09-12)
2272                 * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
2273                 */
2274                 function SetDrawSpotColor($name, $tint=100) {
2275                         if (!isset($this->spot_colors[$name])) {
2276                                 $this->Error('Undefined spot color: '.$name);
2277                         }
2278                         $this->DrawColor = sprintf('/CS%d CS %.3f SCN', $this->spot_colors[$name]['i'], $tint/100);
2279                         if ($this->page > 0) {
2280                                 $this->_out($this->DrawColor);
2281                         }
2282                 }
2283
2284                 /**
2285                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
2286                 * It can be expressed in RGB components or gray scale.
2287                 * The method can be called before the first page is created and the value is retained from page to page.
2288                 * @param array $color array of colors
2289                 * @since 3.1.000 (2008-6-11)
2290                 * @see SetFillColor()
2291                 */
2292                 function SetFillColorArray($color) {
2293                         if (isset($color)) {
2294                                 $color = array_values($color);
2295                                 $r = isset($color[0]) ? $color[0] : -1;
2296                                 $g = isset($color[1]) ? $color[1] : -1;
2297                                 $b = isset($color[2]) ? $color[2] : -1;
2298                                 $k = isset($color[3]) ? $color[3] : -1;
2299                                 if ($r >= 0) {
2300                                         $this->SetFillColor($r, $g, $b, $k);
2301                                 }
2302                         }
2303                 }
2304
2305                 /**
2306                 * Defines the color used for all filling operations (filled rectangles and cell backgrounds). It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2307                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2308                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2309                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2310                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2311                 * @since 1.3
2312                 * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
2313                 */
2314                 function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2315                         // set default values
2316                         if (!is_numeric($col1)) {
2317                                 $col1 = 0;
2318                         }
2319                         if (!is_numeric($col2)) {
2320                                 $col2 = -1;
2321                         }
2322                         if (!is_numeric($col3)) {
2323                                 $col3 = -1;
2324                         }
2325                         if (!is_numeric($col4)) {
2326                                 $col4 = -1;
2327                         }
2328                         //Set color for all filling operations
2329                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2330                                 // Grey scale
2331                                 $this->FillColor = sprintf('%.3f g', $col1/255);
2332                                 $this->bgcolor = array('G' => $col1);
2333                         } elseif ($col4 == -1) {
2334                                 // RGB
2335                                 $this->FillColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2336                                 $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2337                         } else {
2338                                 // CMYK
2339                                 $this->FillColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2340                                 $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2341                         }
2342                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2343                         if ($this->page > 0) {
2344                                 $this->_out($this->FillColor);
2345                         }
2346                 }
2347                 
2348                 /**
2349                 * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
2350                 * @param string $name name of the spot color
2351                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2352                 * @since 4.0.024 (2008-09-12)
2353                 * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
2354                 */
2355                 function SetFillSpotColor($name, $tint=100) {
2356                         if (!isset($this->spot_colors[$name])) {
2357                                 $this->Error('Undefined spot color: '.$name);
2358                         }
2359                         $this->FillColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2360                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2361                         if ($this->page > 0) {
2362                                 $this->_out($this->FillColor);
2363                         }
2364                 }
2365
2366                 /**
2367                 * Defines the color used for text. It can be expressed in RGB components or gray scale.
2368                 * The method can be called before the first page is created and the value is retained from page to page.
2369                 * @param array $color array of colors
2370                 * @since 3.1.000 (2008-6-11)
2371                 * @see SetFillColor()
2372                 */
2373                 function SetTextColorArray($color) {
2374                         if (isset($color)) {
2375                                 $color = array_values($color);
2376                                 $r = isset($color[0]) ? $color[0] : -1;
2377                                 $g = isset($color[1]) ? $color[1] : -1;
2378                                 $b = isset($color[2]) ? $color[2] : -1;
2379                                 $k = isset($color[3]) ? $color[3] : -1;
2380                                 if ($r >= 0) {
2381                                         $this->SetTextColor($r, $g, $b, $k);
2382                                 }
2383                         }
2384                 }
2385
2386                 /**
2387                 * Defines the color used for text. It can be expressed in RGB components or gray scale. The method can be called before the first page is created and the value is retained from page to page.
2388                 * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
2389                 * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
2390                 * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
2391                 * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
2392                 * @since 1.3
2393                 * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
2394                 */
2395                 function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
2396                         // set default values
2397                         if (!is_numeric($col1)) {
2398                                 $col1 = 0;
2399                         }
2400                         if (!is_numeric($col2)) {
2401                                 $col2 = -1;
2402                         }
2403                         if (!is_numeric($col3)) {
2404                                 $col3 = -1;
2405                         }
2406                         if (!is_numeric($col4)) {
2407                                 $col4 = -1;
2408                         }
2409                         //Set color for text
2410                         if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
2411                                 // Grey scale
2412                                 $this->TextColor = sprintf('%.3f g', $col1/255);
2413                                 $this->fgcolor = array('G' => $col1);
2414                         } elseif ($col4 == -1) {
2415                                 // RGB
2416                                 $this->TextColor = sprintf('%.3f %.3f %.3f rg', $col1/255, $col2/255, $col3/255);
2417                                 $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
2418                         } else {
2419                                 // CMYK
2420                                 $this->TextColor = sprintf('%.3f %.3f %.3f %.3f k', $col1/100, $col2/100, $col3/100, $col4/100);
2421                                 $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
2422                         }
2423                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2424                 }
2425
2426                 /**
2427                 * Defines the spot color used for text.
2428                 * @param string $name name of the spot color
2429                 * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
2430                 * @since 4.0.024 (2008-09-12)
2431                 * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
2432                 */
2433                 function SetTextSpotColor($name, $tint=100) {
2434                         if (!isset($this->spot_colors[$name])) {
2435                                 $this->Error('Undefined spot color: '.$name);
2436                         }
2437                         $this->TextColor = sprintf('/CS%d cs %.3f scn', $this->spot_colors[$name]['i'], $tint/100);
2438                         $this->ColorFlag = ($this->FillColor != $this->TextColor);
2439                         if ($this->page > 0) {
2440                                 $this->_out($this->TextColor);
2441                         }
2442                 }
2443
2444                 /**
2445                 * Returns the length of a string in user unit. A font must be selected.<br>
2446                 * @param string $s The string whose length is to be computed
2447                 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
2448                 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.
2449                 * @param float $fontsize Font size in points. The default value is the current size.
2450                 * @return int string length
2451                 * @author Nicola Asuni
2452                 * @since 1.2
2453                 */
2454                 function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0) {
2455                         return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $this->tmprtl), $fontname, $fontstyle, $fontsize);
2456                 }
2457
2458                 /**
2459                 * Returns the string length of an array of chars in user unit. A font must be selected.<br>
2460                 * @param string $arr The array of chars whose total length is to be computed
2461                 * @param string $fontname Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
2462                 * @param string $fontstyle Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular.
2463                 * @param float $fontsize Font size in points. The default value is the current size.
2464                 * @return int string length
2465                 * @author Nicola Asuni
2466                 * @since 2.4.000 (2008-03-06)
2467                 */
2468                 function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0) {
2469                         // store current values
2470                         if (!empty($fontname)) {
2471                                 $prev_FontFamily = $this->FontFamily;
2472                                 $prev_FontStyle = $this->FontStyle;
2473                                 $prev_FontSizePt = $this->FontSizePt;
2474                                 $this->SetFont($fontname, $fontstyle, $fontsize);
2475                         }
2476                         $w = 0;
2477                         foreach($sa as $char) {
2478                                 $w += $this->GetCharWidth($char);
2479                         }
2480                         // restore previous values
2481                         if (!empty($fontname)) {
2482                                 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
2483                         }
2484                         return $w;
2485                 }
2486
2487                 /**
2488                 * Returns the length of the char in user unit for the current font.<br>
2489                 * @param int $char The char code whose length is to be returned
2490                 * @return int char width
2491                 * @author Nicola Asuni
2492                 * @since 2.4.000 (2008-03-06)
2493                 */
2494                 function GetCharWidth($char) {
2495                         $cw = &$this->CurrentFont['cw'];
2496                         if (isset($cw[$char])) {
2497                                 $w = $cw[$char];
2498                                 /*
2499                         } elseif (isset($cw[ord($char)])) {
2500                                 $w = $cw[ord($char)];
2501                         } elseif (isset($cw[chr($char)])) {
2502                                 $w = $cw[chr($char)];
2503                                 */
2504                         } elseif (isset($this->CurrentFont['dw'])) {
2505                                 $w = $this->CurrentFont['dw'];
2506                         } elseif (isset($this->CurrentFont['desc']['MissingWidth'])) {
2507                                 $w = $this->CurrentFont['desc']['MissingWidth']; // set default size
2508                         } else {
2509                                 $w = 500; // default width
2510                         }
2511                         return ($w * $this->FontSize / 1000);
2512                 }
2513
2514                 /**
2515                 * Returns the numbero of characters in a string.
2516                 * @param string $s The input string.
2517                 * @return int number of characters
2518                 * @since 2.0.0001 (2008-01-07)
2519                 */
2520                 function GetNumChars($s) {
2521                         if ($this->isunicode) {
2522                                 return count($this->UTF8StringToArray($s));
2523                         }
2524                         return strlen($s);
2525                 }
2526
2527                 /**
2528                 * Fill the list of available fonts ($this->fontlist).
2529                 * @access protected
2530                 * @since 4.0.013 (2008-07-28)
2531                 */
2532                 function getFontsList() {
2533                         $fontsdir = opendir($this->_getfontpath());
2534                         while (($file = readdir($fontsdir)) !== false) {
2535                                 if (substr($file, -4) == ".php") {
2536                                                 array_push($this->fontlist, strtolower(basename($file, ".php")));
2537                                 }
2538                         }
2539                         closedir($fontsdir);
2540                 }
2541
2542                 /**
2543                 * Imports a TrueType, Type1, core, or CID0 font and makes it available.
2544                 * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
2545                 * The definition file (and the font file itself when embedding) must be present either in the current directory or in the one indicated by K_PATH_FONTS if the constant is defined. If it could not be found, the error "Could not include font definition file" is generated.
2546                 * Changed to support UTF-8 Unicode [Nicola Asuni, 2005-01-02].
2547                 * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
2548                 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular (default)</li><li>B: bold</li><li>I: italic</li><li>BI or IB: bold italic</li></ul>
2549                 * @param string $file The font definition file. By default, the name is built from the family and style, in lower case with no space.
2550                 * @return array containing the font data, or false in case of error.
2551                 * @since 1.5
2552                 * @see SetFont()
2553                 */
2554                 function AddFont($family, $style='', $file='') {
2555                         if (empty($family)) {
2556                                 if (!empty($this->FontFamily)) {
2557                                         $family = $this->FontFamily;
2558                                 } else {
2559                                         $this->Error('Empty font family');
2560                                 }
2561                         }
2562                         $family = strtolower($family);
2563                         if ((!$this->isunicode) AND ($family == 'arial')) {
2564                                 $family = 'helvetica';
2565                         }
2566                         if (($family == "symbol") OR ($family == "zapfdingbats")) {
2567                                 $style = '';
2568                         }
2569                         $style = strtoupper($style);
2570                         // underline
2571                         if (strpos($style,'U') !== false) {
2572                                 $this->underline = true;
2573                                 $style = str_replace('U', '', $style);
2574                         } else {
2575                                 $this->underline = false;
2576                         }
2577                         //line through (deleted)
2578                         if (strpos($style,'D') !== false) {
2579                                 $this->linethrough = true;
2580                                 $style = str_replace('D', '', $style);
2581                         } else {
2582                                 $this->linethrough = false;
2583                         }
2584                         if ($style == 'IB') {
2585                                 $style = 'BI';
2586                         }
2587                         $fontkey = $family.$style;
2588                         $fontdata = array("fontkey" => $fontkey, "family" => $family, "style" => $style);
2589                         // check if the font has been already added
2590                         if (isset($this->fonts[$fontkey])) {
2591                                 return $fontdata;
2592                         }
2593                         if ($file == '') {
2594                                 $file = str_replace(' ', '', $family).strtolower($style).'.php';
2595                         }
2596                         if (!file_exists($this->_getfontpath().$file)) {
2597                                 // try to load the basic file without styles
2598                                 $file = str_replace(' ', '', $family).'.php';
2599                         }
2600                         if (isset($type)) {
2601                                 unset($type);
2602                         }
2603                         if (isset($cw)) {
2604                                 unset($cw);
2605                         }
2606                         @include($this->_getfontpath().$file);
2607                         if ((!isset($type)) OR (!isset($cw))) {
2608                                 $this->Error("Could not include font definition file: ".$file);
2609                         }
2610                         $i = count($this->fonts) + 1;
2611                         // register CID font (all styles at once)
2612                         if ($type == 'cidfont0') {
2613                                 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
2614                                 foreach ($styles as $skey => $qual) {
2615                                         $sname = $name.$qual;
2616                                         $sfontkey = $family.$skey;
2617                                         $this->fonts[$sfontkey] = array('i' => $i, 'type' => $type, 'name' => $sname, 'desc' => $desc, 'cidinfo' => $cidinfo, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc);
2618                                         $i = count($this->fonts) + 1;
2619                                 }
2620                                 $file = '';
2621                         } elseif ($type == 'core') {
2622                                 $def_width = $cw[ord('?')];
2623                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => 'core', 'name' => $this->CoreFonts[$fontkey], 'up' => -100, 'ut' => 50, 'cw' => $cw, 'dw' => $def_width);
2624                         } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
2625                                 if (!isset($file)) {
2626                                         $file = '';
2627                                 }
2628                                 if (!isset($enc)) {
2629                                         $enc = '';
2630                                 }
2631                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'file' => $file, 'enc' => $enc, 'desc' => $desc);
2632                         } elseif ($type == 'TrueTypeUnicode') {
2633                                 $this->fonts[$fontkey] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file, 'ctg' => $ctg);
2634                         } else {
2635                                 $this->Error('Unknow font type');
2636                         }
2637                         if (isset($diff) AND (!empty($diff))) {
2638                                 //Search existing encodings
2639                                 $d = 0;
2640                                 $nb = count($this->diffs);
2641                                 for($i=1; $i <= $nb; $i++) {
2642                                         if ($this->diffs[$i] == $diff) {
2643                                                 $d = $i;
2644                                                 break;
2645                                         }
2646                                 }
2647                                 if ($d == 0) {
2648                                         $d = $nb + 1;
2649                                         $this->diffs[$d] = $diff;
2650                                 }
2651                                 $this->fonts[$fontkey]['diff'] = $d;
2652                         }
2653                         if (!empty($file)) {
2654                                 if ((strcasecmp($type,"TrueType") == 0) OR (strcasecmp($type,"TrueTypeUnicode") == 0)) {
2655                                         $this->FontFiles[$file] = array('length1' => $originalsize);
2656                                 } elseif ($type != 'core') {
2657                                         $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2);
2658                                 }
2659                         }
2660                         return $fontdata;
2661                 }
2662
2663                 /**
2664                 * Sets the font used to print character strings.
2665                 * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
2666                 * The method can be called before the first page is created and the font is retained from page to page.
2667                 * If you just wish to change the current font size, it is simpler to call SetFontSize().
2668                 * Note: for the standard fonts, the font metric files must be accessible. There are three possibilities for this:<ul><li>They are in the current directory (the one where the running script lies)</li><li>They are in one of the directories defined by the include_path parameter</li><li>They are in the directory defined by the K_PATH_FONTS constant</li></ul><br />
2669                 * @param string $family Family font. It can be either a name defined by AddFont() or one of the standard Type1 families (case insensitive):<ul><li>times (Times-Roman)</li><li>timesb (Times-Bold)</li><li>timesi (Times-Italic)</li><li>timesbi (Times-BoldItalic)</li><li>helvetica (Helvetica)</li><li>helveticab (Helvetica-Bold)</li><li>helveticai (Helvetica-Oblique)</li><li>helveticabi (Helvetica-BoldOblique)</li><li>courier (Courier)</li><li>courierb (Courier-Bold)</li><li>courieri (Courier-Oblique)</li><li>courierbi (Courier-BoldOblique)</li><li>symbol (Symbol)</li><li>zapfdingbats (ZapfDingbats)</li></ul> It is also possible to pass an empty string. In that case, the current family is retained.
2670                 * @param string $style Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line trough</li></ul> or any combination. The default value is regular. Bold and italic styles do not apply to Symbol and ZapfDingbats basic fonts or other fonts when not defined.
2671                 * @param float $size Font size in points. The default value is the current size. If no size has been specified since the beginning of the document, the value taken is 12
2672                 * @since 1.0
2673                 * @see AddFont(), SetFontSize()
2674                 */
2675                 function SetFont($family, $style='', $size=0) {
2676                         //Select a font; size given in points
2677                         if ($size == 0) {
2678                                 $size = $this->FontSizePt;
2679                         }
2680                         // try to add font (if not already added)
2681                         $fontdata =  $this->AddFont($family, $style);
2682                         $this->FontFamily = $fontdata['family'];
2683                         $this->FontStyle = $fontdata['style'];
2684                         $this->CurrentFont = &$this->fonts[$fontdata['fontkey']];
2685                         $this->SetFontSize($size);
2686                 }
2687
2688                 /**
2689                 * Defines the size of the current font.
2690                 * @param float $size The size (in points)
2691                 * @since 1.0
2692                 * @see SetFont()
2693                 */
2694                 function SetFontSize($size) {
2695                         //Set font size in points
2696                         $this->FontSizePt = $size;
2697                         $this->FontSize = $size / $this->k;
2698                         if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
2699                                 $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
2700                         } else {
2701                                 $this->FontAscent = 0.8 * $this->FontSize;
2702                         }
2703                         if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] > 0)) {
2704                                 $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
2705                         } else {
2706                                 $this->FontDescent = 0.2 * $this->FontSize;
2707                         }
2708                         if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
2709                                 $this->_out(sprintf('BT /F%d %.2f Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
2710                         }
2711                 }
2712
2713                 /**
2714                 * Creates a new internal link and returns its identifier. An internal link is a clickable area which directs to another place within the document.<br />
2715                 * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
2716                 * @since 1.5
2717                 * @see Cell(), Write(), Image(), Link(), SetLink()
2718                 */
2719                 function AddLink() {
2720                         //Create a new internal link
2721                         $n = count($this->links) + 1;
2722                         $this->links[$n] = array(0, 0);
2723                         return $n;
2724                 }
2725
2726                 /**
2727                 * Defines the page and position a link points to.
2728                 * @param int $link The link identifier returned by AddLink()
2729                 * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
2730                 * @param int $page Number of target page; -1 indicates the current page. This is the default value
2731                 * @since 1.5
2732                 * @see AddLink()
2733                 */
2734                 function SetLink($link, $y=0, $page=-1) {
2735                         if ($y == -1) {
2736                                 $y = $this->y;
2737                         }
2738                         if ($page == -1) {
2739                                 $page = $this->page;
2740                         }
2741                         $this->links[$link] = array($page, $y);
2742                 }
2743
2744                 /**
2745                 * Puts a link on a rectangular area of the page.
2746                 * Text or image links are generally put via Cell(), Write() or Image(), but this method can be useful for instance to define a clickable area inside an image.
2747                 * @param float $x Abscissa of the upper-left corner of the rectangle
2748                 * @param float $y Ordinate of the upper-left corner of the rectangle
2749                 * @param float $w Width of the rectangle
2750                 * @param float $h Height of the rectangle
2751                 * @param mixed $link URL or identifier returned by AddLink()
2752                 * @since 1.5
2753                 * @see AddLink(), Annotation(), Cell(), Write(), Image()
2754                 */
2755                 function Link($x, $y, $w, $h, $link) {
2756                         $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'));
2757                 }
2758                 
2759                 /**
2760                 * Puts a text annotation on a rectangular area of the page.
2761                 * !!!! THIS FUNCTION IS NOT YET FULLY IMPLEMENTED !!!!
2762                 * @param float $x Abscissa of the upper-left corner of the rectangle
2763                 * @param float $y Ordinate of the upper-left corner of the rectangle
2764                 * @param float $w Width of the rectangle
2765                 * @param float $h Height of the rectangle
2766                 * @param string $text annotation text
2767                 * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
2768                 * @since 4.0.018 (2008-08-06)
2769                 */
2770                 function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text')) {
2771                         $this->PageAnnots[$this->page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt);
2772                 }
2773
2774                 /**
2775                 * Prints a character string. The origin is on the left of the first charcter, on the baseline. This method allows to place a string precisely on the page, but it is usually easier to use Cell(), MultiCell() or Write() which are the standard methods to print text.
2776                 * @param float $x Abscissa of the origin
2777                 * @param float $y Ordinate of the origin
2778                 * @param string $txt String to print
2779                 * @param int $stroke outline size in points (0 = disable)
2780                 * @param boolean $clip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
2781                 * @since 1.0
2782                 * @see SetFont(), SetTextColor(), Cell(), MultiCell(), Write()
2783                 */
2784                 function Text($x, $y, $txt, $stroke=0, $clip=false) {
2785                         //Output a string
2786                         if ($this->rtl) {
2787                                 // bidirectional algorithm (some chars may be changed affecting the line length)
2788                                 $s = $this->utf8Bidi($this->UTF8StringToArray($txt), $this->tmprtl);
2789                                 $l = $this->GetArrStringWidth($s);
2790                                 $xr = $this->w - $x - $this->GetArrStringWidth($s);
2791                         } else {
2792                                 $xr = $x;
2793                         }
2794                         $opt = "";
2795                         if (($stroke > 0) AND (!$clip)) {
2796                                 $opt .= "1 Tr ".intval($stroke)." w ";
2797                         } elseif (($stroke > 0) AND $clip) {
2798                                 $opt .= "5 Tr ".intval($stroke)." w ";
2799                         } elseif ($clip) {
2800                                 $opt .= "7 Tr ";
2801                         }
2802                         $s = sprintf('BT %.2f %.2f Td %s(%s) Tj ET 0 Tr', $xr * $this->k, ($this->h-$y) * $this->k, $opt, $this->_escapetext($txt));
2803                         if ($this->underline AND ($txt!='')) {
2804                                 $s .= ' '.$this->_dounderline($xr, $y, $txt);
2805                         }
2806                         if ($this->linethrough AND ($txt!='')) {
2807                                 $s .= ' '.$this->_dolinethrough($xr, $y, $txt);
2808                         }
2809                         if ($this->ColorFlag AND (!$clip)) {
2810                                 $s='q '.$this->TextColor.' '.$s.' Q';
2811                         }
2812                         $this->_out($s);
2813                 }
2814
2815                 /**
2816                 * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
2817                 * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
2818                 * This method is called automatically and should not be called directly by the application.
2819                 * @return boolean
2820                 * @since 1.4
2821                 * @see SetAutoPageBreak()
2822                 */
2823                 function AcceptPageBreak() {
2824                         return $this->AutoPageBreak;
2825                 }
2826
2827                 /**
2828                 * Add page if needed.
2829                 * @param float $h Cell height. Default value: 0.
2830                 * @since 3.2.000 (2008-07-01)
2831                 * @access protected
2832                 */
2833                 function checkPageBreak($h) {
2834                         if ((($this->y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
2835                                 $rs = "";
2836                                 //Automatic page break
2837                                 $x = $this->x;
2838                                 $ws = $this->ws;
2839                                 if ($ws > 0) {
2840                                         $this->ws = 0;
2841                                         $rs .= '0 Tw';
2842                                 }
2843                                 $this->AddPage($this->CurOrientation);
2844                                 if ($ws > 0) {
2845                                         $this->ws = $ws;
2846                                         $rs .= sprintf('%.3f Tw', $ws * $k);
2847                                 }
2848                                 $this->_out($rs);
2849                                 $this->y = $this->tMargin;
2850                                 $this->x = $x;
2851                         }
2852                 }
2853
2854                 /**
2855                 * Prints a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
2856                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2857                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2858                 * @param float $h Cell height. Default value: 0.
2859                 * @param string $txt String to print. Default value: empty string.
2860                 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
2861                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>
2862                 Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2863                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
2864                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2865                 * @param mixed $link URL or identifier returned by AddLink().
2866                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
2867                 * @since 1.0
2868                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
2869                 */
2870                 function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2871                         //$min_cell_height = $this->FontAscent + $this->FontDescent;
2872                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2873                         if ($h < $min_cell_height) {
2874                                 $h = $min_cell_height;
2875                         }
2876                         $this->checkPageBreak($h);
2877                         $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch));
2878                 }
2879
2880                 /**
2881                 * Returns the PDF string code to print a cell (rectangular area) with optional borders, background color and character string. The upper-left corner of the cell corresponds to the current position. The text can be aligned or centered. After the call, the current position moves to the right or to the next line. It is possible to put a link on the text.<br />
2882                 * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
2883                 * @param float $w Cell width. If 0, the cell extends up to the right margin.
2884                 * @param float $h Cell height. Default value: 0.
2885                 * @param string $txt String to print. Default value: empty string.
2886                 * @param mixed $border Indicates if borders must be drawn around the cell. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
2887                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right (or left for RTL languages)</li><li>1: to the beginning of the next line</li><li>2: below</li></ul>Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
2888                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
2889                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
2890                 * @param mixed $link URL or identifier returned by AddLink().
2891                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
2892                 * @since 1.0
2893                 * @see Cell()
2894                 */
2895                 function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0) {
2896                         $rs = ""; //string to be returned
2897                         $min_cell_height = $this->FontSize * $this->cell_height_ratio;
2898                         if ($h < $min_cell_height) {
2899                                 $h = $min_cell_height;
2900                         }
2901                         $k = $this->k;
2902                         if (empty($w) OR ($w <= 0)) {
2903                                 if ($this->rtl) {
2904                                         $w = $this->x - $this->lMargin;
2905                                 } else {
2906                                         $w = $this->w - $this->rMargin - $this->x;
2907                                 }
2908                         }
2909                         $s = '';
2910                         if (($fill == 1) OR ($border == 1)) {
2911                                 if ($fill == 1) {
2912                                         $op = ($border == 1) ? 'B' : 'f';
2913                                 } else {
2914                                         $op = 'S';
2915                                 }
2916                                 if ($this->rtl) {
2917                                         $xk = (($this->x  - $w) * $k);
2918                                 } else {
2919                                         $xk = ($this->x * $k);
2920                                 }
2921                                 $s .= sprintf('%.2f %.2f %.2f %.2f re %s ', $xk, (($this->h - $this->y) * $k), ($w * $k), (-$h * $k), $op);
2922                         }
2923
2924                         if (is_string($border)) {
2925                                 $x = $this->x;
2926                                 $y = $this->y;
2927                                 if (strpos($border,'L') !== false) {
2928                                         if ($this->rtl) {
2929                                                 $xk = ($x - $w) * $k;
2930                                         } else {
2931                                                 $xk = $x * $k;
2932                                         }
2933                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h)) * $k));
2934                                 }
2935                                 if (strpos($border,'T') !== false) {
2936                                         if ($this->rtl) {
2937                                                 $xk = ($x - $w) * $k;
2938                                                 $xwk = $x * $k;
2939                                         } else {
2940                                                 $xk = $x * $k;
2941                                                 $xwk = ($x + $w) * $k;
2942                                         }
2943                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
2944                                 }
2945                                 if (strpos($border,'R') !== false) {
2946                                         if ($this->rtl) {
2947                                                 $xk = $x * $k;
2948                                         } else {
2949                                                 $xk = ($x + $w) * $k;
2950                                         }
2951                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - $y) * $k), $xk, (($this->h - ($y + $h))* $k));
2952                                 }
2953                                 if (strpos($border,'B') !== false) {
2954                                         if ($this->rtl) {
2955                                                 $xk = ($x - $w) * $k;
2956                                                 $xwk = $x * $k;
2957                                         } else {
2958                                                 $xk = $x * $k;
2959                                                 $xwk = ($x + $w) * $k;
2960                                         }
2961                                         $s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
2962                                 }
2963                         }
2964                         if ($txt != '') {
2965                                 // text lenght
2966                                 $width = $this->GetStringWidth($txt);
2967                                 // ratio between cell lenght and text lenght
2968                                 $ratio = ($w - (2 * $this->cMargin)) / $width;
2969
2970                                 // stretch text if required
2971                                 if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
2972                                         if ($stretch > 2) {
2973                                                 // spacing
2974                                                 //Calculate character spacing in points
2975                                                 $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
2976                                                 //Set character spacing
2977                                                 $rs .= sprintf('BT %.2f Tc ET ', $char_space);
2978                                         } else {
2979                                                 // scaling
2980                                                 //Calculate horizontal scaling
2981                                                 $horiz_scale = $ratio * 100.0;
2982                                                 //Set horizontal scaling
2983                                                 $rs .= sprintf('BT %.2f Tz ET ', $horiz_scale);
2984                                         }
2985                                         $align = '';
2986                                         $width = $w - (2 * $this->cMargin);
2987                                 } else {
2988                                         $stretch == 0;
2989                                 }
2990                                 if ($align == 'L') {
2991                                         if ($this->rtl) {
2992                                                 $dx = $w - $width - $this->cMargin;
2993                                         } else {
2994                                                 $dx = $this->cMargin;
2995                                         }
2996                                 } elseif ($align == 'R') {
2997                                         if ($this->rtl) {
2998                                                 $dx = $this->cMargin;
2999                                         } else {
3000                                                 $dx = $w - $width - $this->cMargin;
3001                                         }
3002                                 } elseif ($align == 'C') {
3003                                         $dx = ($w - $width) / 2;
3004                                 } elseif ($align == 'J') {
3005                                         if ($this->rtl) {
3006                                                 $dx = $w - $width - $this->cMargin;
3007                                         } else {
3008                                                 $dx = $this->cMargin;
3009                                         }
3010                                 } else {
3011                                         $dx = $this->cMargin;
3012                                 }
3013                                 if ($this->ColorFlag) {
3014                                         $s .= 'q '.$this->TextColor.' ';
3015                                 }
3016                                 $txt2 = $this->_escapetext($txt);
3017                                 if ($this->rtl) {
3018                                         $xdk = ($this->x - $dx - $width) * $k;
3019                                 } else {
3020                                         $xdk = ($this->x + $dx) * $k;
3021                                 }
3022                                 // Justification
3023                                 if ($align == 'J') {
3024                                         // count number of spaces
3025                                         $ns = substr_count($txt, ' ');
3026                                         //if ($this->isunicode) {
3027                                         if (($this->CurrentFont['type'] == "TrueTypeUnicode") OR ($this->CurrentFont['type'] == "cidfont0")) {
3028                                                 // get string width without spaces
3029                                                 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
3030                                                 // calculate average space width
3031                                                 $spacewidth = ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize / $this->k;
3032                                                 // set word position to be used with TJ operator
3033                                                 $txt2 = str_replace(chr(0).' ', ') '.(-2830 * $spacewidth).' (', $txt2);
3034                                         } else {
3035                                                 // get string width
3036                                                 $width = $this->GetStringWidth($txt);
3037                                                 $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
3038                                                 $rs .= sprintf('BT %.3f Tw ET ', $spacewidth);
3039                                         }
3040                                 }
3041                                 // calculate approximate position of the font base line
3042                                 //$basefonty = $this->y + (($h + $this->FontAscent - $this->FontDescent)/2);
3043                                 $basefonty = $this->y + ($h/2) + ($this->FontSize/3);
3044                                 // print text
3045                                 $s .= sprintf('BT %.2f %.2f Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
3046                                 if ($this->rtl) {
3047                                         $xdx = $this->x - $dx - $width;
3048                                 } else {
3049                                         $xdx = $this->x + $dx;
3050                                 }
3051                                 if ($this->underline)  {
3052                                         $s .= ' '.$this->_dounderline($xdx, $basefonty, $txt);
3053                                 }
3054                                 if ($this->linethrough) {
3055                                         $s .= ' '.$this->_dolinethrough($xdx, $basefonty, $txt);
3056                                 }
3057                                 if ($this->ColorFlag) {
3058                                         $s .= ' Q';
3059                                 }
3060                                 if ($link) {
3061                                         $this->Link($xdx, $this->y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link);
3062                                 }
3063                         }
3064                         // output cell
3065                         if ($s) {
3066                                 // output cell
3067                                 $rs .= $s;
3068                                 // reset text stretching
3069                                 if ($stretch > 2) {
3070                                         //Reset character horizontal spacing
3071                                         $rs .= ' BT 0 Tc ET';
3072                                 } elseif ($stretch > 0) {
3073                                         //Reset character horizontal scaling
3074                                         $rs .= ' BT 100 Tz ET';
3075                                 }
3076                         }
3077                         // reset word spacing
3078                         if ((!$this->isunicode) AND ($align == 'J')) {
3079                                 $rs .= ' BT 0 Tw ET';
3080                         }
3081                         $this->lasth = $h;
3082                         if ($ln > 0) {
3083                                 //Go to the beginning of the next line
3084                                 $this->y += $h;
3085                                 if ($ln == 1) {
3086                                         if ($this->rtl) {
3087                                                 $this->x = $this->w - $this->rMargin;
3088                                         } else {
3089                                                 $this->x = $this->lMargin;
3090                                         }
3091                                 }
3092                         } else {
3093                                 // go left or right by case
3094                                 if ($this->rtl) {
3095                                         $this->x -= $w;
3096                                 } else {
3097                                         $this->x += $w;
3098                                 }
3099                         }
3100                         $gstyles = $this->linestyleWidth." ".$this->linestyleCap." ".$this->linestyleJoin." ".$this->linestyleDash." ".$this->DrawColor." ".$this->FillColor."\n";
3101                         $rs = $gstyles.$rs;
3102                         return $rs;
3103                 }
3104
3105                 /**
3106                 * This method allows printing text with line breaks.
3107                 * They can be automatic (as soon as the text reaches the right border of the cell) or explicit (via the \n character). As many cells as necessary are output, one below the other.<br />
3108                 * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
3109                 * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
3110                 * @param float $h Cell minimum height. The cell extends automatically if needed.
3111                 * @param string $txt String to print
3112                 * @param mixed $border Indicates if borders must be drawn around the cell block. The value can be either a number:<ul><li>0: no border (default)</li><li>1: frame</li></ul>or a string containing some or all of the following characters (in any order):<ul><li>L: left</li><li>T: top</li><li>R: right</li><li>B: bottom</li></ul>
3113                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align</li><li>C: center</li><li>R: right align</li><li>J: justification (default value when $ishtml=false)</li></ul>
3114                 * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3115                 * @param int $ln Indicates where the current position should go after the call. Possible values are:<ul><li>0: to the right</li><li>1: to the beginning of the next line [DEFAULT]</li><li>2: below</li></ul>
3116                 * @param int $x x position in user units
3117                 * @param int $y y position in user units
3118                 * @param boolean $reseth if true reset the last cell height (default true).
3119                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3120                 * @param boolean $ishtml se to true if $txt is HTML content (default = false).
3121                 * @return int Return the number of cells or 1 for html mode.
3122                 * @since 1.3
3123                 * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
3124                 */
3125                 function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false) {
3126                         if ((empty($this->lasth))OR ($reseth)) {
3127                                 //set row height
3128                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
3129                         }
3130                         if (!empty($y)) {
3131                                 $this->SetY($y);
3132                         } else {
3133                                 $y = $this->GetY();
3134                         }
3135                         // check for page break
3136                         $this->checkPageBreak($h);
3137                         $y = $this->GetY();
3138                         // get current page number
3139                         $startpage = $this->page;
3140                         if (!empty($x)) {
3141                                 $this->SetX($x);
3142                         } else {
3143                                 $x = $this->GetX();
3144                         }
3145                         if (empty($w) OR ($w <= 0)) {
3146                                 if ($this->rtl) {
3147                                         $w = $this->x - $this->lMargin;
3148                                 } else {
3149                                         $w = $this->w - $this->rMargin - $this->x;
3150                                 }
3151                         }
3152                         // store original margin values
3153                         $lMargin = $this->lMargin;
3154                         $rMargin = $this->rMargin;
3155                         if ($this->rtl) {
3156                                 $this->SetRightMargin($this->w - $this->x);
3157                                 $this->SetLeftMargin($this->x - $w);
3158                         } else {
3159                                 $this->SetLeftMargin($this->x);
3160                                 $this->SetRightMargin($this->w - $this->x - $w);
3161                         }
3162                         // calculate remaining vertical space on first page ($startpage)
3163                         $restspace = $this->getPageHeight() - $this->GetY() - $this->getBreakMargin();
3164                         // Adjust internal padding
3165                         if ($this->cMargin < ($this->LineWidth / 2)) {
3166                                 $this->cMargin = ($this->LineWidth / 2);
3167                         }
3168                         // Add top space if needed
3169                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3170                                 $this->y += $this->LineWidth / 2;
3171                         }
3172                         // add top padding
3173                         $this->y += $this->cMargin;
3174                         if ($ishtml) {
3175                                 // Write HTML text
3176                                 $this->writeHTML($txt, true, 0, $reseth, true, $align);
3177                                 $nl = 1;
3178                         } else {
3179                                 // Write text
3180                                 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false);
3181                         }
3182                         // add bottom padding
3183                         $this->y += $this->cMargin;
3184                         // Add bottom space if needed
3185                         if (($this->lasth - $this->FontSize) < $this->LineWidth) {
3186                                 $this->y += $this->LineWidth / 2;
3187                         }
3188                         // Get end-of-text Y position
3189                         $currentY = $this->GetY();
3190                         // get latest page number
3191                         $endpage = $this->page;
3192                         // check if a new page has been created
3193                         if ($endpage > $startpage) {
3194                                 // design borders around HTML cells.
3195                                 for ($page=$startpage; $page <= $endpage; $page++) {
3196                                         $this->setPage($page);
3197                                         if ($page == $startpage) {
3198                                                 $this->SetY($this->getPageHeight() - $restspace - $this->getBreakMargin());
3199                                                 $h = $restspace;
3200                                         } elseif ($page == $endpage) {
3201                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3202                                                 $h = $currentY - $this->tMargin;
3203                                         } else {
3204                                                 $this->SetY($this->tMargin); // put cursor at the beginning of text
3205                                                 $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
3206                                         }
3207                                         $this->SetX($x);
3208                                         $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3209                                         if ($border OR $fill) {
3210                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3211                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3212                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3213                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
3214                                         }
3215                                 }
3216                         } else {
3217                                 $h = max($h, ($currentY - $y));
3218                                 // put cursor at the beginning of text
3219                                 $this->SetY($y);
3220                                 $this->SetX($x);
3221                                 $ccode = $this->getCellCode($w, $h, "", $border, 1, '', $fill);
3222                                 if ($border OR $fill) {
3223                                         // design a cell around the text
3224                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
3225                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
3226                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
3227                                         $this->intmrk[$this->page] += strlen($ccode."\n");
3228                                 }
3229                         }
3230                         // Get end-of-cell Y position
3231                         $currentY = $this->GetY();
3232                         // restore original margin values
3233                         $this->SetLeftMargin($lMargin);
3234                         $this->SetRightMargin($rMargin);
3235                         if ($ln > 0) {
3236                                 //Go to the beginning of the next line
3237                                 $this->SetY($currentY);
3238                                 if ($ln == 2) {
3239                                         $this->SetX($x + $w);
3240                                 }
3241                         } else {
3242                                 // go left or right by case
3243                                 $this->setPage($startpage);
3244                                 $this->y = $y;
3245                                 $this->SetX($x + $w);
3246                         }
3247                         return $nl;
3248                 }
3249
3250                 /**
3251                 * This method prints text from the current position.<br />
3252                 * @param float $h Line height
3253                 * @param string $txt String to print
3254                 * @param mixed $link URL or identifier returned by AddLink()
3255                 * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
3256                 * @param string $align Allows to center or align the text. Possible values are:<ul><li>L or empty string: left align (default value)</li><li>C: center</li><li>R: right align</li><li>J: justify</li></ul>
3257                 * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
3258                 * @param int $stretch stretch carachter mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if necessary</li><li>2 = forced horizontal scaling</li><li>3 = character spacing only if necessary</li><li>4 = forced character spacing</li></ul>
3259                 * @param boolean $firstline if true prints only the first line and return the remaining string.
3260                 * @return mixed Return the number of cells or the remaining string if $firstline = true.
3261                 * @since 1.5
3262                 */
3263                 function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false) {
3264                         // remove carriage returns
3265                         $s = str_replace("\r", '', $txt);
3266                         // check if string contains arabic text
3267                         if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
3268                                 $arabic = true;
3269                         } else {
3270                                 $arabic = false;
3271                         }
3272                         // get array of chars
3273                         $chars = $this->UTF8StringToArray($s);
3274                         // get the number of characters
3275                         $nb = count($chars);
3276                         // handle single space character
3277                         if (($nb == 1) AND preg_match("/[\s]/u", $s)) {
3278                                 if ($this->rtl) {
3279                                         $this->x -= $this->GetStringWidth($s);
3280                                 } else {
3281                                         $this->x += $this->GetStringWidth($s);
3282                                 }
3283                                 return;
3284                         }
3285                         // store current position
3286                         $prevx = $this->x;
3287                         $prevy = $this->y;
3288                         // calculate remaining line width ($w)
3289                         if ($this->rtl) {
3290                                 $w = $this->x - $this->lMargin;
3291                         } else {
3292                                 $w = $this->w - $this->rMargin - $this->x;
3293                         }
3294                         // max column width
3295                         $wmax = $w - (2 * $this->cMargin);
3296                         $i = 0; // character position
3297                         $j = 0; // current starting position
3298                         $sep = -1; // position of the last blank space
3299                         $l = 0; // current string lenght
3300                         $nl = 0; //number of lines
3301                         $linebreak = false;
3302                         // for each character
3303                         while ($i < $nb) {
3304                                 //Get the current character
3305                                 $c = $chars[$i];
3306                                 if ($c == 10) { // 10 = "\n" = new line
3307                                         //Explicit line break
3308                                         if ($align == "J") {
3309                                                 if ($this->rtl) {
3310                                                         $talign = "R";
3311                                                 } else {
3312                                                         $talign = "L";
3313                                                 }
3314                                         } else {
3315                                                 $talign = $align;
3316                                         }
3317                                         if ($firstline) {
3318                                                 $startx = $this->x;
3319                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3320                                                 if ($this->rtl) {
3321                                                         $this->endlinex = $startx - $linew;
3322                                                 } else {
3323                                                         $this->endlinex = $startx + $linew;
3324                                                 }
3325                                                 $w = $linew;
3326                                                 $tmpcmargin = $this->cMargin;
3327                                                 $this->cMargin = 0;
3328                                         }
3329                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $talign, $fill, $link, $stretch);
3330                                         if ($firstline) {
3331                                                 $this->cMargin = $tmpcmargin;
3332                                                 return ($this->UTF8ArrSubString($chars, $i));
3333                                         }
3334                                         $nl++;
3335                                         $j = $i + 1;
3336                                         $l = 0;
3337                                         $sep = -1;
3338                                         $w = $this->getRemainingWidth();
3339                                         $wmax = $w - (2 * $this->cMargin);
3340                                 } else {
3341                                         if (preg_match("/[\s]/u", $this->unichr($c))) {
3342                                                 // update last blank space position
3343                                                 $sep = $i;
3344                                         }
3345                                         // update string length
3346                                         if (($this->isunicode) AND ($arabic)) {
3347                                                 // with bidirectional algorithm some chars may be changed affecting the line length
3348                                                 // *** very slow ***
3349                                                 $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i-$j+1), $this->tmprtl));
3350                                         } else {
3351                                                 $l += $this->GetCharWidth($c);
3352                                         }
3353                                         if ($l > $wmax) {
3354                                                 // we have reached the end of column
3355                                                 if ($sep == -1) {
3356                                                         // check if the line was already started
3357                                                         if (($this->rtl AND ($this->x < ($this->w - $this->rMargin)))
3358                                                                 OR ((!$this->rtl) AND ($this->x > $this->lMargin))) {
3359                                                                 // print a void cell and go to next line
3360                                                                 $this->Cell($w, $h, "", 0, 1);
3361                                                                 $linebreak = true;
3362                                                                 if ($firstline) {
3363                                                                         return ($this->UTF8ArrSubString($chars, $j));
3364                                                                 }
3365                                                         } else {
3366                                                                 // truncate the word because do not fit on column
3367                                                                 if ($firstline) {
3368                                                                         $startx = $this->x;
3369                                                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $i), $this->tmprtl));
3370                                                                         if ($this->rtl) {
3371                                                                                 $this->endlinex = $startx - $linew;
3372                                                                         } else {
3373                                                                                 $this->endlinex = $startx + $linew;
3374                                                                         }
3375                                                                         $w = $linew;
3376                                                                         $tmpcmargin = $this->cMargin;
3377                                                                         $this->cMargin = 0;
3378                                                                 }
3379                                                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $i), 0, 1, $align, $fill, $link, $stretch);
3380                                                                 if ($firstline) {
3381                                                                         $this->cMargin = $tmpcmargin;
3382                                                                         return ($this->UTF8ArrSubString($chars, $i));
3383                                                                 }
3384                                                                 $j = $i;
3385                                                                 $i--;
3386                                                         }
3387                                                 } else {
3388                                                         // word wrapping
3389                                                         if ($firstline) {
3390                                                                 $startx = $this->x;
3391                                                                 $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $sep), $this->tmprtl));
3392                                                                 if ($this->rtl) {
3393                                                                         $this->endlinex = $startx - $linew;
3394                                                                 } else {
3395                                                                         $this->endlinex = $startx + $linew;
3396                                                                 }
3397                                                                 $w = $linew;
3398                                                                 $tmpcmargin = $this->cMargin;
3399                                                                 $this->cMargin = 0;
3400                                                         }
3401                                                         $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $sep), 0, 1, $align, $fill, $link, $stretch);
3402                                                         if ($firstline) {
3403                                                                 $this->cMargin = $tmpcmargin;
3404                                                                 return ($this->UTF8ArrSubString($chars, $sep));
3405                                                         }
3406                                                         $i = $sep;
3407                                                         $sep = -1;
3408                                                         $j = ($i+1);
3409                                                 }
3410                                                 $w = $this->getRemainingWidth();
3411                                                 $wmax = $w - (2 * $this->cMargin);
3412                                                 if ($linebreak) {
3413                                                         $linebreak = false;
3414                                                 } else {
3415                                                         $nl++;
3416                                                         $l = 0;
3417                                                 }
3418                                         }
3419                                 }
3420                                 $i++;
3421                         } // end while i < nb
3422                         // print last substring (if any)
3423                         if ($l > 0) {
3424                                 switch ($align) {
3425                                         case "J":
3426                                         case "C": {
3427                                                 $w = $w;
3428                                                 break;
3429                                         }
3430                                         case "L": {
3431                                                 if ($this->rtl) {
3432                                                         $w = $w;
3433                                                 } else {
3434                                                         $w = $l;
3435                                                 }
3436                                                 break;
3437                                         }
3438                                         case "R": {
3439                                                 if ($this->rtl) {
3440                                                         $w = $l;
3441                                                 } else {
3442                                                         $w = $w;
3443                                                 }
3444                                                 break;
3445                                         }
3446                                         default: {
3447                                                 $w = $l;
3448                                                 break;
3449                                         }
3450                                 }
3451                                 if ($firstline) {
3452                                         $startx = $this->x;
3453                                         $linew = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, $nb), $this->tmprtl));
3454                                         if ($this->rtl) {
3455                                                 $this->endlinex = $startx - $linew;
3456                                         } else {
3457                                                 $this->endlinex = $startx + $linew;
3458                                         }
3459                                         $w = $linew;
3460                                         $tmpcmargin = $this->cMargin;
3461                                         $this->cMargin = 0;
3462                                 }
3463                                 $this->Cell($w, $h, $this->UTF8ArrSubString($chars, $j, $nb), 0, $ln, $align, $fill, $link, $stretch);
3464                                 if ($firstline) {
3465                                         $this->cMargin = $tmpcmargin;
3466                                         return ($this->UTF8ArrSubString($chars, $nb));
3467                                 }
3468                                 $nl++;
3469                         }
3470                         if ($firstline) {
3471                                 return "";
3472                         }
3473                         return $nl;
3474                 }
3475
3476                 /**
3477                 * Returns the remaining width between the current position and margins.
3478                 * @return int Return the remaining width
3479                 * @access protected
3480                 */
3481                 function getRemainingWidth() {
3482                         if ($this->rtl) {
3483                                 return ($this->x - $this->lMargin);
3484                         } else {
3485                                 return ($this->w - $this->rMargin - $this->x);
3486                         }
3487                 }
3488
3489          /**
3490                 * Extract a slice of the $strarr array and return it as string.
3491                 * @param string $strarr The input array of characters.
3492                 * @param int $start the starting element of $strarr.
3493                 * @param int $end first element that will not be returned.
3494                 * @return Return part of a string
3495                 */
3496                 function UTF8ArrSubString($strarr, $start='', $end='') {
3497                         if (strlen($start) == 0) {
3498                                 $start = 0;
3499                         }
3500                         if (strlen($end) == 0) {
3501                                 $end = count($strarr);
3502                         }
3503                         $string = "";
3504                         for ($i=$start; $i < $end; $i++) {
3505                                 $string .= $this->unichr($strarr[$i]);
3506                         }
3507                         return $string;
3508                 }
3509
3510                 /**
3511                 * Returns the unicode caracter specified by UTF-8 code
3512                 * @param int $c UTF-8 code
3513                 * @return Returns the specified character.
3514                 * @author Miguel Perez, Nicola Asuni
3515                 * @since 2.3.000 (2008-03-05)
3516                 */
3517                 function unichr($c) {
3518                         if (!$this->isunicode) {
3519                                 return chr($c);
3520                         } elseif ($c <= 0x7F) {
3521                                 // one byte
3522                                 return chr($c);
3523                         } elseif ($c <= 0x7FF) {
3524                                 // two bytes
3525                                 return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
3526                         } elseif ($c <= 0xFFFF) {
3527                                 // three bytes
3528                                 return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3529                         } elseif ($c <= 0x10FFFF) {
3530                                 // four bytes
3531                                 return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
3532                         } else {
3533                                 return "";
3534                         }
3535                 }
3536
3537                 /**
3538                 * Puts an image in the page.
3539                 * The upper-left corner must be given.
3540                 * The dimensions can be specified in different ways:<ul>
3541                 * <li>explicit width and height (expressed in user unit)</li>
3542                 * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
3543                 * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
3544                 * Supported formats are JPEG and PNG images whitout GD library and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;
3545                 * The format can be specified explicitly or inferred from the file extension.<br />
3546                 * It is possible to put a link on the image.<br />
3547                 * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
3548                 * @param string $file Name of the file containing the image.
3549                 * @param float $x Abscissa of the upper-left corner.
3550                 * @param float $y Ordinate of the upper-left corner.
3551                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
3552                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
3553                 * @param string $type Image format. Possible values are (case insensitive): JPEG and PNG (whitout GD library) and all images supported by GD: GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM;. If not specified, the type is inferred from the file extension.
3554                 * @param mixed $link URL or identifier returned by AddLink().
3555                 * @param string $align Indicates the alignment of the pointer next to image insertion relative to image height. The value can be:<ul><li>T: top-right for LTR or top-left for RTL</li><li>M: middle-right for LTR or middle-left for RTL</li><li>B: bottom-right for LTR or bottom-left for RTL</li><li>N: next line</li></ul>
3556                 * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
3557                 * @param int $dpi dot-per-inch resolution used on resize
3558                 * @param string $palign Allows to center or align the image on the current line. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
3559                 * @since 1.1
3560                 * @see AddLink()
3561                 */
3562                 function Image($file, $x, $y, $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
3563                         // get image size
3564                         list($pixw, $pixh) = getimagesize($file);
3565                         // calculate image width and height on document
3566                         if (($w <= 0) AND ($h <= 0)) {
3567                                 // convert image size to document unit
3568                                 $w = $pixw / ($this->imgscale * $this->k);
3569                                 $h = $pixh / ($this->imgscale * $this->k);
3570                         } elseif ($w <= 0) {
3571                                 $w = $h * $pixw / $pixh;
3572                         } elseif ($h <= 0) {
3573                                 $h = $w * $pixh / $pixw;
3574                         }
3575                         // calculate new minimum dimensions in pixels
3576                         $neww = round($w * $this->k * $dpi / $this->dpi);
3577                         $newh = round($h * $this->k * $dpi / $this->dpi);
3578                         // check if resize is necessary (resize is used only to reduce the image)
3579                         if (($neww * $newh) >= ($pixw * $pixh)) {
3580                                 $resize = false;
3581                         }
3582                         // check if image has been already added on document
3583                         if (!isset($this->images[$file])) {
3584                                 //First use of image, get info
3585                                 if ($type == '') {
3586                                         $fileinfo = pathinfo($file);
3587                                         if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
3588                                                 $type = $fileinfo['extension'];
3589                                         } else {
3590                                                 $this->Error('Image file has no extension and no type was specified: '.$file);
3591                                         }
3592                                 }
3593                                 $type = strtolower($type);
3594                                 if ($type == "jpg") {
3595                                         $type = "jpeg";
3596                                 }
3597                                 $mqr = get_magic_quotes_runtime();
3598                                 set_magic_quotes_runtime(0);
3599                                 // Specific image handlers
3600                                 $mtd = '_parse'.$type;
3601                                 // GD image handler function
3602                                 $gdfunction = "imagecreatefrom".$type;
3603                                 $info = false;
3604                                 if ((method_exists($this,$mtd)) AND (!($resize AND function_exists($gdfunction)))) {
3605                                         $info = $this->$mtd($file);
3606                                 }
3607                                 if (!$info) {
3608                                         if (function_exists($gdfunction)) {
3609                                                 $img = $gdfunction($file);
3610                                                 if ($resize) {
3611                                                         $imgr = imagecreatetruecolor($neww, $newh);
3612                                                         imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
3613                                                         $info = $this->_toJPEG($imgr);
3614                                                 } else {
3615                                                         $info = $this->_toJPEG($img);
3616                                                 }
3617                                         } else {
3618                                                 return;
3619                                         }
3620                                 }
3621                                 if ($info === false) {
3622                                         //If false, we cannot process image
3623                                         return;
3624                                 }
3625                                 set_magic_quotes_runtime($mqr);
3626                                 $info['i'] = count($this->images) + 1;
3627                                 // add image to document
3628                                 $this->images[$file] = $info;
3629                         } else {
3630                                 $info = $this->images[$file];
3631                         }
3632                         // Check whether we need a new page first as this does not fit
3633                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
3634                                 // Automatic page break
3635                                 $this->AddPage($this->CurOrientation);
3636                                 // Reset coordinates to top fo next page
3637                                 //$x = $this->GetX();
3638                                 $y = $this->GetY() + $this->cMargin;
3639                         }
3640                         // 2007-10-19 Warren Sherliker: End Edit
3641                         // set bottomcoordinates
3642                         $this->img_rb_y = $y + $h;
3643                         // set alignment
3644                         if ($this->rtl) {
3645                                 if ($palign == 'L') {
3646                                         $ximg = $this->lMargin;
3647                                         // set right side coordinate
3648                                         $this->img_rb_x = $ximg + $w;
3649                                 } elseif ($palign == 'C') {
3650                                         $ximg = ($this->w - $x - $w) / 2;
3651                                         // set right side coordinate
3652                                         $this->img_rb_x = $ximg + $w;
3653                                 } else {
3654                                         $ximg = $this->w - $x - $w;
3655                                         // set left side coordinate
3656                                         $this->img_rb_x = $ximg;
3657                                 }
3658                         } else {
3659                                 if ($palign == 'R') {
3660                                         $ximg = $this->w - $this->rMargin - $w;
3661                                         // set left side coordinate
3662                                         $this->img_rb_x = $ximg;
3663                                 } elseif ($palign == 'C') {
3664                                         $ximg = ($this->w - $x - $w) / 2;
3665                                         // set right side coordinate
3666                                         $this->img_rb_x = $ximg + $w;
3667                                 } else {
3668                                         $ximg = $x;
3669                                         // set right side coordinate
3670                                         $this->img_rb_x = $ximg + $w;
3671                                 }
3672                         }
3673                         $xkimg = $ximg * $this->k;
3674                         $this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
3675                         if ($link) {
3676                                 $this->Link($ximg, $y, $w, $h, $link);
3677                         }
3678                         // set pointer to align the successive text/objects
3679                         switch($align) {
3680                                 case 'T': {
3681                                         $this->y = $y;
3682                                         $this->x = $this->img_rb_x;
3683                                         break;
3684                                 }
3685                                 case 'M': {
3686                                         $this->y = $y + round($h/2);
3687                                         $this->x = $this->img_rb_x;
3688                                         break;
3689                                 }
3690                                 case 'B': {
3691                                         $this->y = $this->img_rb_y;
3692                                         $this->x = $this->img_rb_x;
3693                                         break;
3694                                 }
3695                                 case 'N': {
3696                                         $this->SetY($this->img_rb_y);
3697                                         break;
3698                                 }
3699                                 default:{
3700                                         break;
3701                                 }
3702                         }
3703                         $this->endlinex = $this->img_rb_x;
3704                 }
3705
3706                 /**
3707                 * Convert the loaded php image to a JPEG and then return a structure for the PDF creator.
3708                 * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
3709                 * @param string $file Image file name.
3710                 * @param image $image Image object.
3711                 * return image JPEG image object.
3712                 * @access protected
3713                 */
3714                 function _toJPEG($image) {
3715                         $tempname = tempnam(K_PATH_CACHE,'jpg');
3716                         imagejpeg($image, $tempname, $this->jpeg_quality);
3717                         imagedestroy($image);
3718                         $retvars = $this->_parsejpeg($tempname);
3719                         // tidy up by removing temporary image
3720                         unlink($tempname);
3721                         return $retvars;
3722                 }
3723
3724                 /**
3725                 * Extract info from a JPEG file without using the GD library.
3726                 * @param string $file image file to parse
3727                 * @return array structure containing the image data
3728                 * @access protected
3729                 */
3730                 function _parsejpeg($file) {
3731                         $a = getimagesize($file);
3732                         if (empty($a)) {
3733                                 $this->Error('Missing or incorrect image file: '.$file);
3734                         }
3735                         if ($a[2] != 2) {
3736                                 $this->Error('Not a JPEG file: '.$file);
3737                         }
3738                         if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
3739                                 $colspace = 'DeviceRGB';
3740                         } elseif ($a['channels'] == 4) {
3741                                 $colspace = 'DeviceCMYK';
3742                         }       else {
3743                                 $colspace = 'DeviceGray';
3744                         }
3745                         $bpc = isset($a['bits']) ? $a['bits'] : 8;
3746                         $data = file_get_contents($file);
3747                         return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
3748                 }
3749
3750                 /**
3751                 * Extract info from a PNG file without using the GD library.
3752                 * @param string $file image file to parse
3753                 * @return array structure containing the image data
3754                 * @access protected
3755                 */
3756                 function _parsepng($file) {
3757                         $f = fopen($file,'rb');
3758                         if (empty($f)) {
3759                                 $this->Error('Can\'t open image file: '.$file);
3760                         }
3761                         //Check signature
3762                         if (fread($f,8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
3763                                 $this->Error('Not a PNG file: '.$file);
3764                         }
3765                         //Read header chunk
3766                         fread($f,4);
3767                         if (fread($f,4) != 'IHDR') {
3768                                 $this->Error('Incorrect PNG file: '.$file);
3769                         }
3770                         $w = $this->_freadint($f);
3771                         $h = $this->_freadint($f);
3772                         $bpc = ord(fread($f,1));
3773                         if ($bpc > 8) {
3774                                 //$this->Error('16-bit depth not supported: '.$file);
3775                                 return false;
3776                         }
3777                         $ct = ord(fread($f,1));
3778                         if ($ct == 0) {
3779                                 $colspace = 'DeviceGray';
3780                         } elseif ($ct == 2) {
3781                                 $colspace = 'DeviceRGB';
3782                         } elseif ($ct == 3) {
3783                                 $colspace = 'Indexed';
3784                         } else {
3785                                 //$this->Error('Alpha channel not supported: '.$file);
3786                                 return false;
3787                         }
3788                         if (ord(fread($f,1)) != 0) {
3789                                 //$this->Error('Unknown compression method: '.$file);
3790                                 return false;
3791                         }
3792                         if (ord(fread($f,1)) != 0) {
3793                                 //$this->Error('Unknown filter method: '.$file);
3794                                 return false;
3795                         }
3796                         if (ord(fread($f,1)) != 0) {
3797                                 //$this->Error('Interlacing not supported: '.$file);
3798                                 return false;
3799                         }
3800                         fread($f,4);
3801                         $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
3802                         //Scan chunks looking for palette, transparency and image data
3803                         $pal = '';
3804                         $trns = '';
3805                         $data = '';
3806                         do {
3807                                 $n = $this->_freadint($f);
3808                                 $type = fread($f,4);
3809                                 if ($type == 'PLTE') {
3810                                         //Read palette
3811                                         $pal = fread($f,$n);
3812                                         fread($f,4);
3813                                 } elseif ($type == 'tRNS') {
3814                                         //Read transparency info
3815                                         $t = fread($f,$n);
3816                                         if ($ct == 0) {
3817                                                 $trns = array(ord(substr($t,1,1)));
3818                                         }
3819                                         elseif ($ct == 2) {
3820                                                 $trns = array(ord(substr($t,1,1)), ord(substr($t,3,1)), ord(substr($t,5,1)));
3821                                         } else {
3822                                                 $pos = strpos($t,chr(0));
3823                                                 if ($pos !== false) {
3824                                                         $trns = array($pos);
3825                                                 }
3826                                         }
3827                                         fread($f, 4);
3828                                 } elseif ($type == 'IDAT') {
3829                                         //Read image data block
3830                                         $data .= fread($f,$n);
3831                                         fread($f, 4);
3832                                 } elseif ($type == 'IEND') {
3833                                         break;
3834                                 } else {
3835                                         fread($f, $n+4);
3836                                 }
3837                         }
3838                         while ($n);
3839                         if (($colspace == 'Indexed') AND (empty($pal))) {
3840                                 //$this->Error('Missing palette in '.$file);
3841                                 return false;
3842                         }
3843                         fclose($f);
3844                         return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
3845                 }
3846
3847                 /**
3848                 * Performs a line break.
3849                 * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
3850                 * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
3851                 * @param boolean $cell if true add a cMargin to the x coordinate
3852                 * @since 1.0
3853                 * @see Cell()
3854                 */
3855                 function Ln($h='', $cell=false) {
3856                         //Line feed; default value is last cell height
3857                         if ($cell) {
3858                                 $cellmargin = $this->cMargin;
3859                         } else {
3860                                 $cellmargin = 0;
3861                         }
3862                         if ($this->rtl) {
3863                                 $this->x = $this->w - $this->rMargin - $cellmargin;
3864                         } else {
3865                                 $this->x = $this->lMargin + $cellmargin;
3866                         }
3867                         if (is_string($h)) {
3868                                 $this->y += $this->lasth;
3869                         } else {
3870                                 $this->y += $h;
3871                         }
3872                         $this->newline = true;
3873                 }
3874
3875                 /**
3876                 * Returns the relative X value of current position.
3877                 * The value is relative to the left border for LTR languages and to the right border for RTL languages.
3878                 * @return float
3879                 * @since 1.2
3880                 * @see SetX(), GetY(), SetY()
3881                 */
3882                 function GetX() {
3883                         //Get x position
3884                         if ($this->rtl) {
3885                                 return ($this->w - $this->x);
3886                         } else {
3887                                 return $this->x;
3888                         }
3889                 }
3890
3891                 /**
3892                 * Returns the absolute X value of current position.
3893                 * @return float
3894                 * @since 1.2
3895                 * @see SetX(), GetY(), SetY()
3896                 */
3897                 function GetAbsX() {
3898                         return $this->x;
3899                 }
3900
3901                 /**
3902                 * Returns the ordinate of the current position.
3903                 * @return float
3904                 * @since 1.0
3905                 * @see SetY(), GetX(), SetX()
3906                 */
3907                 function GetY() {
3908                         //Get y position
3909                         return $this->y;
3910                 }
3911
3912                 /**
3913                 * Defines the abscissa of the current position.
3914                 * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
3915                 * @param float $x The value of the abscissa.
3916                 * @since 1.2
3917                 * @see GetX(), GetY(), SetY(), SetXY()
3918                 */
3919                 function SetX($x) {
3920                         //Set x position
3921                         if ($this->rtl) {
3922                                 if ($x >= 0) {
3923                                         $this->x = $this->w - $x;
3924                                 } else {
3925                                         $this->x = abs($x);
3926                                 }
3927                         } else {
3928                                 if ($x >= 0) {
3929                                         $this->x = $x;
3930                                 } else {
3931                                         $this->x = $this->w + $x;
3932                                 }
3933                         }
3934                 }
3935
3936                 /**
3937                 * Moves the current abscissa back to the left margin and sets the ordinate.
3938                 * If the passed value is negative, it is relative to the bottom of the page.
3939                 * @param float $y The value of the ordinate.
3940                 * @since 1.0
3941                 * @see GetX(), GetY(), SetY(), SetXY()
3942                 */
3943                 function SetY($y) {
3944                         //Set y position and reset x
3945                         if ($this->rtl) {
3946                                 $this->x = $this->w - $this->rMargin;
3947                         } else {
3948                                 $this->x = $this->lMargin;
3949                         }
3950                         if ($y >= 0) {
3951                                 $this->y = $y;
3952                         } else {
3953                                 $this->y = $this->h + $y;
3954                         }
3955                 }
3956
3957                 /**
3958                 * Defines the abscissa and ordinate of the current position.
3959                 * If the passed values are negative, they are relative respectively to the right and bottom of the page.
3960                 * @param float $x The value of the abscissa
3961                 * @param float $y The value of the ordinate
3962                 * @since 1.2
3963                 * @see SetX(), SetY()
3964                 */
3965                 function SetXY($x, $y) {
3966                         //Set x and y positions
3967                         $this->SetY($y);
3968                         $this->SetX($x);
3969                 }
3970
3971                 /**
3972                 * Send the document to a given destination: string, local file or browser.
3973                 * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
3974                 * The method first calls Close() if necessary to terminate the document.
3975                 * @param string $name The name of the file when saved.
3976                 * @param string $dest Destination where to send the document. It can take one of the following values:<ul><li>I: send the file inline to the browser (default). The plug-in is used if available. The name given by name is used when one selects the "Save as" option on the link generating the PDF.</li><li>D: send to the browser and force a file download with the name given by name.</li><li>F: save to a local file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li></ul>
3977                 * @since 1.0
3978                 * @see Close()
3979                 */
3980                 function Output($name='doc.pdf', $dest='I') {
3981                         //Output PDF to some destination
3982                         //Finish document if necessary
3983                         if ($this->state < 3) {
3984                                 $this->Close();
3985                         }
3986                         //Normalize parameters
3987                         if (is_bool($dest)) {
3988                                 $dest = $dest ? 'D' : 'F';
3989                         }
3990                         $dest = strtoupper($dest);
3991                         if ($dest != 'F') {
3992                                 $name = str_replace("+", "%20", urlencode($name));
3993                                 $name = preg_replace('/[\r\n]+\s*/', '' , $name);
3994                         }
3995                         switch($dest) {
3996                                 case 'I': {
3997                                         //Send to standard output
3998                                         if (ob_get_contents()) {
3999                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4000                                         }
4001                                         if (php_sapi_name() != 'cli') {
4002                                                 //We send to a browser
4003                                                 header('Content-Type: application/pdf');
4004                                                 if (headers_sent()) {
4005                                                         $this->Error('Some data has already been output to browser, can\'t send PDF file');
4006                                                 }
4007                                                 header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4008                                                 header("Pragma: public");
4009                                                 header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4010                                                 header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");      
4011                                                 header('Content-Length: '.strlen($this->buffer));
4012                                                 header('Content-Disposition: inline; filename="'.basename($name).'";');
4013                                         }
4014                                         echo $this->buffer;
4015                                         break;
4016                                 }
4017                                 case 'D': {
4018                                         //Download file
4019                                         if (ob_get_contents()) {
4020                                                 $this->Error('Some data has already been output, can\'t send PDF file');
4021                                         }
4022                                         header('Content-Description: File Transfer');
4023                                         if (headers_sent()) {
4024                                                 $this->Error('Some data has already been output to browser, can\'t send PDF file');
4025                                         }
4026                                         header("Cache-Control: public, must-revalidate, max-age=0"); // HTTP/1.1
4027                                         header("Pragma: public");
4028                                         header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date in the past
4029                                         header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
4030                                         // force download dialog
4031                                         header("Content-Type: application/force-download");
4032                                         header("Content-Type: application/octet-stream", false);
4033                                         header("Content-Type: application/download", false);
4034                                         // use the Content-Disposition header to supply a recommended filename
4035                                         header('Content-Disposition: attachment; filename="'.basename($name).'";');
4036                                         header("Content-Transfer-Encoding: binary");
4037                                         header("Content-Length: ".strlen($this->buffer));
4038                                         echo $this->buffer;
4039                                         break;
4040                                 }
4041                                 case 'F': {
4042                                         //Save to local file
4043                                         $f = fopen($name, 'wb');
4044                                         if (!$f) {
4045                                                 $this->Error('Unable to create output file: '.$name);
4046                                         }
4047                                         fwrite($f, $this->buffer,strlen($this->buffer));
4048                                         fclose($f);
4049                                         break;
4050                                 }
4051                                 case 'S': {
4052                                         //Return as a string
4053                                         return $this->buffer;
4054                                 }
4055                                 default: {
4056                                         $this->Error('Incorrect output destination: '.$dest);
4057                                 }
4058                         }
4059                         return '';
4060                 }
4061
4062                 /**
4063                 * Check for locale-related bug
4064                 * @access protected
4065                 */
4066                 function _dochecks() {
4067                         //Check for locale-related bug
4068                         if (1.1 == 1) {
4069                                 $this->Error('Don\'t alter the locale before including class file');
4070                         }
4071                         //Check for decimal separator
4072                         if (sprintf('%.1f', 1.0) != '1.0') {
4073                                 setlocale(LC_NUMERIC, 'C');
4074                         }
4075                 }
4076
4077                 /**
4078                 * Return fonts path
4079                 * @return string
4080                 * @access protected
4081                 */
4082                 function _getfontpath() {
4083                         if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
4084                                 define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
4085                         }
4086                         return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
4087                 }
4088
4089                 /**
4090                 * Output pages.
4091                 * @access protected
4092                 */
4093                 function _putpages() {
4094                         $nb = count($this->pages);
4095                         if (!empty($this->pagegroups)) {
4096                                 // do page number replacement
4097                                 foreach ($this->pagegroups as $k => $v) {
4098                                         $vu = $this->UTF8ToUTF16BE($v, false);
4099                                         $alias_a = $this->_escape($k);
4100                                         $alias_au = $this->_escape("{".$k."}");
4101                                         if ($this->isunicode) {
4102                                                 $alias_b = $this->_escape($this->UTF8ToLatin1($k));
4103                                                 $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$k."}"));
4104                                                 $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
4105                                                 $alias_cu = $this->_escape($this->utf8StrRev("{".$k."}", false, $this->tmprtl));
4106                                         }
4107                                         for ($n = 1; $n <= $nb; $n++) {
4108                                                 $this->pages[$n] = str_replace($alias_au, $vu, $this->pages[$n]);
4109                                                 if ($this->isunicode) {
4110                                                         $this->pages[$n] = str_replace($alias_bu, $vu, $this->pages[$n]);
4111                                                         $this->pages[$n] = str_replace($alias_cu, $vu, $this->pages[$n]);
4112                                                         $this->pages[$n] = str_replace($alias_b, $v, $this->pages[$n]);
4113                                                         $this->pages[$n] = str_replace($alias_c, $v, $this->pages[$n]);
4114                                                 }
4115                                                 $this->pages[$n] = str_replace($alias_a, $v, $this->pages[$n]);
4116                                         }
4117                                 }
4118                         }
4119                         if (!empty($this->AliasNbPages)) {
4120                                 $nbu = $this->UTF8ToUTF16BE($nb, false); // replacement for unicode font
4121                                 $alias_a = $this->_escape($this->AliasNbPages);
4122                                 $alias_au = $this->_escape("{".$this->AliasNbPages."}");
4123                                 if ($this->isunicode) {
4124                                         $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
4125                                         $alias_bu = $this->_escape($this->UTF8ToLatin1("{".$this->AliasNbPages."}"));
4126                                         $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
4127                                         $alias_cu = $this->_escape($this->utf8StrRev("{".$this->AliasNbPages."}", false, $this->tmprtl));
4128                                 }
4129                                 //Replace number of pages
4130                                 for($n = 1; $n <= $nb; $n++) {
4131                                         $this->pages[$n] = str_replace($alias_au, $nbu, $this->pages[$n]);
4132                                         if ($this->isunicode) {
4133                                                 $this->pages[$n] = str_replace($alias_bu, $nbu, $this->pages[$n]);
4134                                                 $this->pages[$n] = str_replace($alias_cu, $nbu, $this->pages[$n]);
4135                                                 $this->pages[$n] = str_replace($alias_b, $nb, $this->pages[$n]);
4136                                                 $this->pages[$n] = str_replace($alias_c, $nb, $this->pages[$n]);
4137                                         }
4138                                         $this->pages[$n] = str_replace($alias_a, $nb, $this->pages[$n]);
4139                                 }
4140                         }
4141                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4142                         for($n=1; $n <= $nb; $n++) {
4143                                 //Page
4144                                 $this->_newobj();
4145                                 $this->_out('<</Type /Page');
4146                                 $this->_out('/Parent 1 0 R');
4147                                 $this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']));
4148                                 $this->_out('/Resources 2 0 R');
4149                                 $this->_putannots($n);
4150                                 $this->_out('/Contents '.($this->n + 1).' 0 R>>');
4151                                 $this->_out('endobj');
4152                                 //Page content
4153                                 $p = ($this->compress) ? gzcompress($this->pages[$n]) : $this->pages[$n];
4154                                 $this->_newobj();
4155                                 $this->_out('<<'.$filter.'/Length '.strlen($p).'>>');
4156                                 $this->_putstream($p);
4157                                 $this->_out('endobj');
4158                         }
4159                         //Pages root
4160                         $this->offsets[1] = strlen($this->buffer);
4161                         $this->_out('1 0 obj');
4162                         $this->_out('<</Type /Pages');
4163                         $kids='/Kids [';
4164                         for($i=0; $i < $nb; $i++) {
4165                                 $kids .= (3+2*$i).' 0 R ';
4166                         }
4167                         $this->_out($kids.']');
4168                         $this->_out('/Count '.$nb);
4169                         //$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]',$this->pagedim[0]['w'],$this->pagedim[0]['h']));
4170                         $this->_out('>>');
4171                         $this->_out('endobj');
4172                 }
4173
4174                 /**
4175                 * Output Page Annotations.
4176                 * See section 8.4 of PDF reference.
4177                 * @param int $n page number
4178                 * @access protected
4179                 * @author Nicola Asuni
4180                 * @since 4.0.018 (2008-08-06)
4181                 */
4182                 function _putannots($n) {
4183                         if (isset($this->PageAnnots[$n])) {
4184                                 $annots = '/Annots [';
4185                                 foreach ($this->PageAnnots[$n] as $key => $pl) {
4186                                         $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
4187                                         $a = $pl['x'] * $this->k;
4188                                         $b = $this->hPt - $pl['y'] * $this->k;
4189                                         $c = $pl['w'] * $this->k;
4190                                         $d = $pl['h'] * $this->k;
4191                                         $rect = sprintf('%.2f %.2f %.2f %.2f', $a, $b, $a+$c, $b-$d);
4192                                         $annots .= '<</Type /Annot';
4193                                         $annots .= ' /Subtype /'.$pl['opt']['subtype'];
4194                                         $annots .= ' /Rect ['.$rect.']';
4195                                         $annots .= ' /Contents '.$this->_textstring($pl['txt']);
4196                                         //$annots .= ' /P ';
4197                                         $annots .= ' /NM '.$this->_textstring(sprintf('%04u-%04u', $n, $key));
4198                                         $annots .= ' /M '.$this->_datestring('D:'.date('YmdHis'));
4199                                         if (isset($pl['opt']['f'])) {
4200                                                 $val = 0;
4201                                                 if (is_array($pl['opt']['f'])) {
4202                                                         foreach ($pl['opt']['f'] as $f) {
4203                                                                 switch (strtolower($f)) {
4204                                                                         case 'invisible': {
4205                                                                                 $val += 1 << 0;
4206                                                                                 break;
4207                                                                         }
4208                                                                         case 'hidden': {
4209                                                                                 $val += 1 << 1;
4210                                                                                 break;
4211                                                                         }
4212                                                                         case 'print': {
4213                                                                                 $val += 1 << 2;
4214                                                                                 break;
4215                                                                         }
4216                                                                         case 'nozoom': {
4217                                                                                 $val += 1 << 3;
4218                                                                                 break;
4219                                                                         }
4220                                                                         case 'norotate': {
4221                                                                                 $val += 1 << 4;
4222                                                                                 break;
4223                                                                         }
4224                                                                         case 'noview': {
4225                                                                                 $val += 1 << 5;
4226                                                                                 break;
4227                                                                         }
4228                                                                         case 'readonly': {
4229                                                                                 $val += 1 << 6;
4230                                                                                 break;
4231                                                                         }
4232                                                                         case 'locked': {
4233                                                                                 $val += 1 << 8;
4234                                                                                 break;
4235                                                                         }
4236                                                                         case 'togglenoview': {
4237                                                                                 $val += 1 << 9;
4238                                                                                 break;
4239                                                                         }
4240                                                                         case 'lockedcontents': {
4241                                                                                 $val += 1 << 10;
4242                                                                                 break;
4243                                                                         }
4244                                                                         default: {
4245                                                                                 break;
4246                                                                         }
4247                                                                 }
4248                                                         }
4249                                                 }
4250                                                 $annots .= ' /F '.intval($val);
4251                                         }
4252                                         //$annots .= ' /AP ';
4253                                         //$annots .= ' /AS ';
4254                                         $annots .= ' /Border [';
4255                                         if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
4256                                                 $annots .= intval($pl['opt']['border'][0]).' ';
4257                                                 $annots .= intval($pl['opt']['border'][1]).' ';
4258                                                 $annots .= intval($pl['opt']['border'][2]);
4259                                                 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
4260                                                         $annots .= ' [';
4261                                                         foreach ($pl['opt']['border'][3] as $dash) {
4262                                                                 $annots .= intval($dash).' ';
4263                                                         }
4264                                                         $annots .= ']';
4265                                                 }
4266                                         } else {
4267                                                 $annots .= '0 0 0';
4268                                         }
4269                                         $annots .= ']';
4270                                         if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
4271                                                 $annots .= ' /BS <<Type /Border';
4272                                                 if (isset($pl['opt']['bs']['w'])) {
4273                                                         $annots .= ' /W '.sprintf("%.4f", floatval($pl['opt']['bs']['w']));
4274                                                 }
4275                                                 $bstyles = array('S', 'D', 'B', 'I', 'U');
4276                                                 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $markups)) {
4277                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4278                                                 }
4279                                                 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
4280                                                         $annots .= ' /D [';
4281                                                         foreach ($pl['opt']['bs']['d'] as $cord) {
4282                                                                 $cord = floatval($cord);
4283                                                                 $annots .= sprintf(" %.4f", $cord);
4284                                                         }
4285                                                         $annots .= ']';
4286                                                 }
4287                                                 $annots .= '>>';
4288                                         }
4289                                         if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
4290                                                 $annots .= ' /BE <<';
4291                                                 $bstyles = array('S', 'C');
4292                                                 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
4293                                                         $annots .= ' /S /'.$pl['opt']['bs']['s'];
4294                                                 } else {
4295                                                         $annots .= ' /S /S';
4296                                                 }
4297                                                 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
4298                                                         $annots .= ' /I '.sprintf(" %.4f", $pl['opt']['be']['i']);
4299                                                 }
4300                                                 $annots .= '>>';
4301                                         }
4302                                         $annots .= ' /C [';
4303                                         if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c']))) {
4304                                                 foreach ($pl['opt']['c'] as $col) {
4305                                                         $col = intval($col);
4306                                                         $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
4307                                                         $annots .= sprintf(" %.4f", $color);
4308                                                 }
4309                                         }
4310                                         $annots .= ']';
4311                                         //$annots .= ' /StructParent ';
4312                                         //$annots .= ' /OC ';
4313                                         $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight',  'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
4314                                         if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
4315                                                 // this is a markup type
4316                                                 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
4317                                                         $annots .= ' /T '.$this->_textstring($pl['opt']['t']);
4318                                                 }
4319                                                 //$annots .= ' /Popup ';
4320                                                 if (isset($pl['opt']['ca'])) {
4321                                                         $annots .= ' /CA '.sprintf("%.4f", floatval($pl['opt']['ca']));
4322                                                 }
4323                                                 if (isset($pl['opt']['rc'])) {
4324                                                         $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4325                                                 }
4326                                                 $annots .= ' /CreationDate '.$this->_datestring('D:'.date('YmdHis'));
4327                                                 //$annots .= ' /IRT ';
4328                                                 if (isset($pl['opt']['subj'])) {
4329                                                         $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj']);
4330                                                 }
4331                                                 //$annots .= ' /RT ';
4332                                                 //$annots .= ' /IT ';
4333                                                 //$annots .= ' /ExData ';
4334                                         }
4335                                         switch (strtolower($pl['opt']['subtype'])) {
4336                                                 case 'text': {
4337                                                         if (isset($pl['opt']['open'])) {
4338                                                                 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
4339                                                         }
4340                                                         $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
4341                                                         if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
4342                                                                 $annots .= ' /Name /'.$pl['opt']['name'];
4343                                                         } else {
4344                                                                 $annots .= ' /Name /Note';
4345                                                         }
4346                                                         $statemodels = array('Marked', 'Review');
4347                                                         if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
4348                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4349                                                         } else {
4350                                                                 $pl['opt']['statemodel'] = 'Marked';
4351                                                                 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
4352                                                         }
4353                                                         if ($pl['opt']['statemodel'] == 'Marked') {
4354                                                                 $states = array('Accepted', 'Unmarked');
4355                                                         } else {
4356                                                                 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
4357                                                         }
4358                                                         if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
4359                                                                 $annots .= ' /State /'.$pl['opt']['state'];
4360                                                         } else {
4361                                                                 if ($pl['opt']['statemodel'] == 'Marked') {
4362                                                                         $annots .= ' /State /Unmarked';
4363                                                                 } else {
4364                                                                         $annots .= ' /State /None';
4365                                                                 }
4366                                                         }
4367                                                         break;
4368                                                 }
4369                                                 case 'link': {
4370                                                         $annots .= ' /A <</S /URI /URI '.$this->_uristring($pl['txt']).'>>';
4371                                                         $hmodes = array('N', 'I', 'O', 'P');
4372                                                         if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
4373                                                                 $annots .= ' /H /'.$pl['opt']['h'];
4374                                                         } else {
4375                                                                 $annots .= ' /H /I';
4376                                                         }
4377                                                         //$annots .= ' /Dest ';
4378                                                         //$annots .= ' /PA ';
4379                                                         //$annots .= ' /Quadpoints ';
4380                                                         break;
4381                                                 }
4382                                                 case 'freetext': {
4383                                                         $annots .= ' /DA '.$this->_textstring($pl['txt']);
4384                                                         if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
4385                                                                 $annots .= ' /Q '.intval($pl['opt']['q']);
4386                                                         }
4387                                                         if (isset($pl['opt']['rc'])) {
4388                                                                 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc']);
4389                                                         }
4390                                                         if (isset($pl['opt']['ds'])) {
4391                                                                 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds']);
4392                                                         }
4393                                                         if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
4394                                                                 $annots .= ' /CL [';
4395                                                                 foreach ($pl['opt']['cl'] as $cl) {
4396                                                                         $annots .= sprintf("%.4f ", $cl * $this->k);
4397                                                                 }
4398                                                                 $annots .= ']';
4399                                                         }
4400                                                         $tfit = array('FreeTextCallout', 'FreeTextTypeWriter');
4401                                                         if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
4402                                                                 $annots .= ' /IT '.$pl['opt']['it'];
4403                                                         }
4404                                                         if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
4405                                                                 $l = $pl['opt']['rd'][0] * $this->k;
4406                                                                 $r = $pl['opt']['rd'][1] * $this->k;
4407                                                                 $t = $pl['opt']['rd'][2] * $this->k;
4408                                                                 $b = $pl['opt']['rd'][3] * $this->k;
4409                                                                 $annots .= ' /RD ['.sprintf('%.2f %.2f %.2f %.2f', $l, $r, $t, $b).']';
4410                                                         }
4411                                                         //$annots .= ' /LE ';
4412                                                         break;
4413                                                 }
4414                                                 // ... to be completed ...
4415                                                 case 'line': {
4416                                                         break;
4417                                                 }
4418                                                 case 'square': {
4419                                                         break;
4420                                                 }
4421                                                 case 'circle': {
4422                                                         break;
4423                                                 }
4424                                                 case 'polygon': {
4425                                                         break;
4426                                                 }
4427                                                 case 'polyline': {
4428                                                         break;
4429                                                 }
4430                                                 case 'highlight': {
4431                                                         break;
4432                                                 }
4433                                                 case 'underline': {
4434                                                         break;
4435                                                 }
4436                                                 case 'squiggly': {
4437                                                         break;
4438                                                 }
4439                                                 case 'strikeout': {
4440                                                         break;
4441                                                 }
4442                                                 case 'stamp': {
4443                                                         break;
4444                                                 }
4445                                                 case 'caret': {
4446                                                         break;
4447                                                 }
4448                                                 case 'ink': {
4449                                                         break;
4450                                                 }
4451                                                 case 'popup': {
4452                                                         break;
4453                                                 }
4454                                                 case 'fileattachment': {
4455                                                         break;
4456                                                 }
4457                                                 case 'sound': {
4458                                                         break;
4459                                                 }
4460                                                 case 'movie': {
4461                                                         break;
4462                                                 }
4463                                                 case 'widget': {
4464                                                         break;
4465                                                 }
4466                                                 case 'screen': {
4467                                                         break;
4468                                                 }
4469                                                 case 'printermark': {
4470                                                         break;
4471                                                 }
4472                                                 case 'trapnet': {
4473                                                         break;
4474                                                 }
4475                                                 case 'watermark': {
4476                                                         break;
4477                                                 }
4478                                                 case '3d': {
4479                                                         break;
4480                                                 }
4481                                                 default: {
4482                                                         break;
4483                                                 }
4484                                         }
4485                                         
4486                                 $annots .= '>>';
4487                                 }
4488                                 $this->_out($annots.']');
4489                         }
4490                 }
4491
4492                 /**
4493                 * Output fonts.
4494                 * _putfonts
4495                 * @access protected
4496                 */
4497                 function _putfonts() {
4498                         $nf = $this->n;
4499                         foreach($this->diffs as $diff) {
4500                                 //Encodings
4501                                 $this->_newobj();
4502                                 $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>>');
4503                                 $this->_out('endobj');
4504                         }
4505                         $mqr = get_magic_quotes_runtime();
4506                         set_magic_quotes_runtime(0);
4507                         foreach($this->FontFiles as $file => $info) {
4508                                 //Font file embedding
4509                                 $this->_newobj();
4510                                 $this->FontFiles[$file]['n'] = $this->n;
4511                                 $font = file_get_contents($this->_getfontpath().strtolower($file));
4512                                 $compressed = (substr($file,-2)=='.z');
4513                                 if ((!$compressed) AND (isset($info['length2']))) {
4514                                         $header = (ord($font{0}) == 128);
4515                                         if ($header) {
4516                                                 //Strip first binary header
4517                                                 $font = substr($font,6);
4518                                         }
4519                                         if ($header AND (ord($font{$info['length1']}) == 128)) {
4520                                                 //Strip second binary header
4521                                                 $font = substr($font, 0, $info['length1']).substr($font, $info['length1']+6);
4522                                         }
4523                                 }
4524                                 $this->_out('<</Length '.strlen($font));
4525                                 if ($compressed) {
4526                                         $this->_out('/Filter /FlateDecode');
4527                                 }
4528                                 $this->_out('/Length1 '.$info['length1']);
4529                                 if (isset($info['length2'])) {
4530                                         $this->_out('/Length2 '.$info['length2'].' /Length3 0');
4531                                 }
4532                                 $this->_out('>>');
4533                                 $this->_putstream($font);
4534                                 $this->_out('endobj');
4535                         }
4536                         set_magic_quotes_runtime($mqr);
4537                         foreach($this->fonts as $k => $font) {
4538                                 //Font objects
4539                                 $this->fonts[$k]['n'] = $this->n + 1;
4540                                 $type = $font['type'];
4541                                 $name = $font['name'];
4542                                 if ($type == 'core') {
4543                                         //Standard font
4544                                         $this->_newobj();
4545                                         $this->_out('<</Type /Font');
4546                                         $this->_out('/BaseFont /'.$name);
4547                                         $this->_out('/Subtype /Type1');
4548                                         if (($name != 'symbol') AND ($name != 'zapfdingbats')) {
4549                                                 $this->_out('/Encoding /WinAnsiEncoding');
4550                                         }
4551                                         $this->_out('>>');
4552                                         $this->_out('endobj');
4553                                 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
4554                                         //Additional Type1 or TrueType font
4555                                         $this->_newobj();
4556                                         $this->_out('<</Type /Font');
4557                                         $this->_out('/BaseFont /'.$name);
4558                                         $this->_out('/Subtype /'.$type);
4559                                         $this->_out('/FirstChar 32 /LastChar 255');
4560                                         $this->_out('/Widths '.($this->n + 1).' 0 R');
4561                                         $this->_out('/FontDescriptor '.($this->n + 2).' 0 R');
4562                                         if ($font['enc']) {
4563                                                 if (isset($font['diff'])) {
4564                                                         $this->_out('/Encoding '.($nf + $font['diff']).' 0 R');
4565                                                 } else {
4566                                                         $this->_out('/Encoding /WinAnsiEncoding');
4567                                                 }
4568                                         }
4569                                         $this->_out('>>');
4570                                         $this->_out('endobj');
4571                                         //Widths
4572                                         $this->_newobj();
4573                                         $cw = &$font['cw'];
4574                                         $s = '[';
4575                                         for($i=32; $i <= 255; $i++) {
4576                                                 //$s .= $cw[chr($i)].' ';
4577                                                 $s .= $cw[$i].' ';
4578                                         }
4579                                         $this->_out($s.']');
4580                                         $this->_out('endobj');
4581                                         //Descriptor
4582                                         $this->_newobj();
4583                                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4584                                         foreach($font['desc'] as $k => $v) {
4585                                                 $s .= ' /'.$k.' '.$v;
4586                                         }
4587                                         $file = $font['file'];
4588                                         if ($file) {
4589                                                 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$file]['n'].' 0 R';
4590                                         }
4591                                         $this->_out($s.'>>');
4592                                         $this->_out('endobj');
4593                                 } else {
4594                                         //Allow for additional types
4595                                         $mtd = '_put'.strtolower($type);
4596                                         if (!method_exists($this, $mtd)) {
4597                                                 $this->Error('Unsupported font type: '.$type);
4598                                         }
4599                                         $this->$mtd($font);
4600                                 }
4601                         }
4602                 }
4603
4604                 /**
4605                  * Output CID-0 fonts.
4606                  * @param array $font font data
4607                  * @access protected
4608                  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
4609                  * @since 3.2.000 (2008-06-23)
4610                  */
4611                 function _putcidfont0($font) {
4612                         if (isset($font['cidinfo']['uni2cid'])) {
4613                                 // convert unicode to cid.
4614                                 $uni2cid = $font['cidinfo']['uni2cid'];
4615                                 $cw = array();
4616                                 foreach ($font['cw'] as $uni => $width) {
4617                                         if (isset($uni2cid[$uni])) {
4618                                                 $cw[($uni2cid[$uni] + 31)] = $width;
4619                                         } elseif ($uni <= 255) {
4620                                                 $cw[$uni] = $width;
4621                                         } // else unknown character
4622                                 }
4623                                 ksort($cw);
4624                                 $font = array_merge($font, array('cw'=>$cw));
4625                         }
4626                         $longname = $name = $font['name'];
4627                         $enc = $font['enc'];
4628                         if ($enc) {
4629                                 $longname .= "-$enc";
4630                         }
4631                         $this->_newobj();
4632                         $this->_out('<</Type /Font');
4633                         $this->_out('/BaseFont /'.$longname);
4634                         $this->_out('/Subtype /Type0');
4635                         if ($enc) {
4636                                 $this->_out('/Encoding /'.$enc);
4637                         }
4638                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
4639                         $this->_out('>>');
4640                         $this->_out('endobj');
4641                         $this->_newobj();
4642                         $this->_out('<</Type /Font');
4643                         $this->_out('/BaseFont /'.$name);
4644                         $this->_out('/Subtype /CIDFontType0');
4645                         $cidinfo = '/Registry ('.$font['cidinfo']['Registry'].') ';
4646                         $cidinfo .= '/Ordering ('.$font['cidinfo']['Ordering'].') ';
4647                         $cidinfo .= '/Supplement '.$font['cidinfo']['Supplement'];
4648                         $this->_out('/CIDSystemInfo <<'.$cidinfo.'>>');
4649                         $this->_out('/FontDescriptor '.($this->n + 1).' 0 R');
4650                         $codes = array_keys($font['cw']);
4651                         $first = current($codes);
4652                         $last = end($codes);
4653                         $this->_out('/DW '.$font['dw']);
4654                         $w = '/W [';
4655                         $ranges = array();
4656                         $currange = 0;
4657                         for($i = $first; $i <= $last; $i++) {
4658                                 if (isset($font['cw'][$i]) AND (!$currange)) {
4659                                         $currange = $i - 31;
4660                                 } elseif (!isset($font['cw'][$i])) {
4661                                         $currange = 0;
4662                                 }
4663                                 if ($currange) {
4664                                         $ranges[$currange][] = $font['cw'][$i];
4665                                 }
4666                         }
4667                         foreach($ranges as $k => $ws) {
4668                                 $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
4669                         }
4670                         $w .= ' ]';
4671                         $this->_out($w);
4672                         $this->_out('>>');
4673                         $this->_out('endobj');
4674                         $this->_newobj();
4675                         $s = '<</Type /FontDescriptor /FontName /'.$name;
4676                         foreach($font['desc'] as $k => $v) {
4677                                 $s .= ' /'.$k.' '.$v;
4678                         }
4679                         $this->_out($s.'>>');
4680                         $this->_out('endobj');
4681                 }
4682
4683                 /**
4684                  * Output images.
4685                  * @access protected
4686                  */
4687                 function _putimages() {
4688                         $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
4689                         reset($this->images);
4690                         while (list($file, $info) = each($this->images)) {
4691                                 $this->_newobj();
4692                                 $this->images[$file]['n'] = $this->n;
4693                                 $this->_out('<</Type /XObject');
4694                                 $this->_out('/Subtype /Image');
4695                                 $this->_out('/Width '.$info['w']);
4696                                 $this->_out('/Height '.$info['h']);
4697                                 if (isset($info["masked"])) {
4698                                         $this->_out('/SMask '.($this->n-1).' 0 R');
4699                                 }
4700                                 if ($info['cs'] == 'Indexed') {
4701                                         $this->_out('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]');
4702                                 } else {
4703                                         $this->_out('/ColorSpace /'.$info['cs']);
4704                                         if ($info['cs'] == 'DeviceCMYK') {
4705                                                 $this->_out('/Decode [1 0 1 0 1 0 1 0]');
4706                                         }
4707                                 }
4708                                 $this->_out('/BitsPerComponent '.$info['bpc']);
4709                                 if (isset($info['f'])) {
4710                                         $this->_out('/Filter /'.$info['f']);
4711                                 }
4712                                 if (isset($info['parms'])) {
4713                                         $this->_out($info['parms']);
4714                                 }
4715                                 if (isset($info['trns']) and is_array($info['trns'])) {
4716                                         $trns='';
4717                                         for($i=0; $i < count($info['trns']); $i++) {
4718                                                 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
4719                                         }
4720                                         $this->_out('/Mask ['.$trns.']');
4721                                 }
4722                                 $this->_out('/Length '.strlen($info['data']).'>>');
4723                                 $this->_putstream($info['data']);
4724                                 unset($this->images[$file]['data']);
4725                                 $this->_out('endobj');
4726                                 //Palette
4727                                 if ($info['cs'] == 'Indexed') {
4728                                         $this->_newobj();
4729                                         $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
4730                                         $this->_out('<<'.$filter.'/Length '.strlen($pal).'>>');
4731                                         $this->_putstream($pal);
4732                                         $this->_out('endobj');
4733                                 }
4734                         }
4735                 }
4736
4737                 /**
4738                 * Output Spot Colors Resources.
4739                 * @access protected
4740                 * @since 4.0.024 (2008-09-12)
4741                 */
4742                 function _putspotcolors() {
4743                         foreach ($this->spot_colors as $name => $color) {
4744                                 $this->_newobj();
4745                                 $this->spot_colors[$name]['n'] = $this->n;
4746                                 $this->_out('[/Separation /'.str_replace(' ', '#20', $name));
4747                                 $this->_out('/DeviceCMYK <<');
4748                                 $this->_out('/Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0] ');
4749                                 $this->_out(sprintf('/C1 [%.4f %.4f %.4f %.4f] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100));
4750                                 $this->_out('/FunctionType 2 /Domain [0 1] /N 1>>]');
4751                                 $this->_out('endobj');
4752                         }
4753                 }
4754
4755                 /**
4756                 * Output object dictionary for images.
4757                 * @access protected
4758                 */
4759                 function _putxobjectdict() {
4760                         foreach($this->images as $image) {
4761                                 $this->_out('/I'.$image['i'].' '.$image['n'].' 0 R');
4762                         }
4763                 }
4764
4765                 /**
4766                 * Output Resources Dictionary.
4767                 * @access protected
4768                 */
4769                 function _putresourcedict(){
4770                         $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
4771                         $this->_out('/Font <<');
4772                         foreach($this->fonts as $font) {
4773                                 $this->_out('/F'.$font['i'].' '.$font['n'].' 0 R');
4774                         }
4775                         $this->_out('>>');
4776                         $this->_out('/XObject <<');
4777                         $this->_putxobjectdict();
4778                         $this->_out('>>');
4779                         // visibility
4780                         $this->_out('/Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>');
4781                         // transparency
4782                         $this->_out('/ExtGState <<');
4783                         foreach($this->extgstates as $k => $extgstate) {
4784                                 $this->_out('/GS'.$k.' '.$extgstate['n'].' 0 R');
4785                         }
4786                         $this->_out('>>');
4787                         // gradients
4788                         if (isset($this->gradients) AND (count($this->gradients) > 0)) {
4789                                 $this->_out('/Shading <<');
4790                                 foreach($this->gradients as $id => $grad) {
4791                                         $this->_out('/Sh'.$id.' '.$grad['id'].' 0 R');
4792                                 }
4793                                 $this->_out('>>');
4794                         }
4795                         // spot colors
4796                         if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
4797                                 $this->_out('/ColorSpace <<');
4798                                 foreach ($this->spot_colors as $color) {
4799                                         $this->_out('/CS'.$color['i'].' '.$color['n'].' 0 R');
4800                                 }
4801                                 $this->_out('>>');
4802                         }
4803                 }
4804
4805                 /**
4806                 * Output Resources.
4807                 * @access protected
4808                 */
4809                 function _putresources() {
4810                         $this->_putextgstates();
4811                         $this->_putocg();
4812                         $this->_putfonts();
4813                         $this->_putimages();
4814                         $this->_putspotcolors();
4815                         $this->_putshaders();
4816                         //Resource dictionary
4817                         $this->offsets[2] = strlen($this->buffer);
4818                         $this->_out('2 0 obj');
4819                         $this->_out('<<');
4820                         $this->_putresourcedict();
4821                         $this->_out('>>');
4822                         $this->_out('endobj');
4823                         $this->_putjavascript();
4824                         $this->_putbookmarks();
4825                         // encryption
4826                         if ($this->encrypted) {
4827                                 $this->_newobj();
4828                                 $this->enc_obj_id = $this->n;
4829                                 $this->_out('<<');
4830                                 $this->_putencryption();
4831                                 $this->_out('>>');
4832                                 $this->_out('endobj');
4833                         }
4834                 }
4835
4836                 /**
4837                 * Adds some Metadata information
4838                 * (see Chapter 10.2 of PDF Reference)
4839                 * @access protected
4840                 */
4841                 function _putinfo() {
4842                         if (!empty($this->title)) {
4843                                 $this->_out('/Title '.$this->_textstring($this->title));
4844                         }
4845                         if (!empty($this->author)) {
4846                                 $this->_out('/Author '.$this->_textstring($this->author));
4847                         }
4848                         if (!empty($this->subject)) {
4849                                 $this->_out('/Subject '.$this->_textstring($this->subject));
4850                         }
4851                         if (!empty($this->keywords)) {
4852                                 $this->_out('/Keywords '.$this->_textstring($this->keywords));
4853                         }
4854                         if (!empty($this->creator)) {
4855                                 $this->_out('/Creator '.$this->_textstring($this->creator));
4856                         }
4857                         if (defined('PDF_PRODUCER')) {
4858                                 $this->_out('/Producer '.$this->_textstring(PDF_PRODUCER));
4859                         }
4860                         $this->_out('/CreationDate '.$this->_datestring('D:'.date('YmdHis')));
4861                         $this->_out('/ModDate '.$this->_datestring('D:'.date('YmdHis')));
4862                 }
4863
4864                 /**
4865                 * Format a date string for meta information
4866                 * @param string $s date string to escape.
4867                 * @return string escaped string.
4868                 * @access protected
4869                 */
4870                 function _datestring($s) {
4871                         if ($this->encrypted) {
4872                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
4873                         }
4874                         return '('. $this->_escape($s).')';
4875                 }
4876
4877                 /**
4878                 * Output Catalog.
4879                 * @access protected
4880                 */
4881                 function _putcatalog() {
4882                         $this->_out('/Type /Catalog');
4883                         $this->_out('/Pages 1 0 R');
4884
4885                         if ($this->ZoomMode == 'fullpage') {
4886                                 $this->_out('/OpenAction [3 0 R /Fit]');
4887                         } elseif ($this->ZoomMode == 'fullwidth') {
4888                                 $this->_out('/OpenAction [3 0 R /FitH null]');
4889                         } elseif ($this->ZoomMode == 'real') {
4890                                 $this->_out('/OpenAction [3 0 R /XYZ null null 1]');
4891                         } elseif (!is_string($this->ZoomMode)) {
4892                                 $this->_out('/OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']');
4893                         }
4894                         if (isset($this->LayoutMode) AND (!empty($this->LayoutMode))) {
4895                                 $this->_out('/PageLayout /'.$this->LayoutMode.'');
4896                         }
4897                         if (isset($this->PageMode) AND (!empty($this->PageMode))) {
4898                                 $this->_out('/PageMode /'.$this->PageMode);
4899                         }
4900                         if (isset($this->l['a_meta_language'])) {
4901                                 $this->_out('/Lang /'.$this->l['a_meta_language']);
4902                         }
4903                         if (!empty($this->javascript)) {
4904                                 $this->_out('/Names <</JavaScript '.($this->n_js).' 0 R>>');
4905                         }
4906                         if (count($this->outlines) > 0) {
4907                                 $this->_out('/Outlines '.$this->OutlineRoot.' 0 R');
4908                                 $this->_out('/PageMode /UseOutlines');
4909                         }
4910                         $this->_putviewerpreferences();
4911                         $p = $this->n_ocg_print.' 0 R';
4912                         $v = $this->n_ocg_view.' 0 R';
4913                         $as = "<</Event /Print /OCGs [".$p." ".$v."] /Category [/Print]>> <</Event /View /OCGs [".$p." ".$v."] /Category [/View]>>";
4914                         $this->_out("/OCProperties <</OCGs [".$p." ".$v."] /D <</ON [".$p."] /OFF [".$v."] /AS [".$as."]>>>>");
4915                         $this->_putuserrights();
4916                 }
4917
4918                 /**
4919                 * Output viewer preferences.
4920                 * @author Nicola asuni
4921                 * @since 3.1.000 (2008-06-09)
4922                 * @access protected
4923                 */
4924                 function _putviewerpreferences() {
4925                         $this->_out('/ViewerPreferences<<');
4926                         if ($this->rtl) {
4927                                 $this->_out('/Direction /R2L');
4928                         } else {
4929                                 $this->_out('/Direction /L2R');
4930                         }
4931                         if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
4932                                 $this->_out('/HideToolbar true');
4933                         }
4934                         if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
4935                                 $this->_out('/HideMenubar true');
4936                         }
4937                         if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
4938                                 $this->_out('/HideWindowUI true');
4939                         }
4940                         if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
4941                                 $this->_out('/FitWindow true');
4942                         }
4943                         if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
4944                                 $this->_out('/CenterWindow true');
4945                         }
4946                         if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
4947                                 $this->_out('/DisplayDocTitle true');
4948                         }
4949                         if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
4950                                 $this->_out('/NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'].'');
4951                         }
4952                         if (isset($this->viewer_preferences['ViewArea'])) {
4953                                 $this->_out('/ViewArea /'.$this->viewer_preferences['ViewArea']);
4954                         }
4955                         if (isset($this->viewer_preferences['ViewClip'])) {
4956                                 $this->_out('/ViewClip /'.$this->viewer_preferences['ViewClip']);
4957                         }
4958                         if (isset($this->viewer_preferences['PrintArea'])) {
4959                                 $this->_out('/PrintArea /'.$this->viewer_preferences['PrintArea']);
4960                         }
4961                         if (isset($this->viewer_preferences['PrintClip'])) {
4962                                 $this->_out('/PrintClip /'.$this->viewer_preferences['PrintClip']);
4963                         }
4964                         if (isset($this->viewer_preferences['PrintScaling'])) {
4965                                 $this->_out('/PrintScaling /'.$this->viewer_preferences['PrintScaling']);
4966                         }
4967                         if (isset($this->viewer_preferences['Duplex']) AND (!empty($this->viewer_preferences['Duplex']))) {
4968                                 $this->_out('/Duplex /'.$this->viewer_preferences['Duplex']);
4969                         }
4970                         if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
4971                                 if ($this->viewer_preferences['PickTrayByPDFSize']) {
4972                                         $this->_out('/PickTrayByPDFSize true');
4973                                 } else {
4974                                         $this->_out('/PickTrayByPDFSize false');
4975                                 }
4976                         }
4977                         if (isset($this->viewer_preferences['PrintPageRange'])) {
4978                                 $PrintPageRangeNum = "";
4979                                 foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
4980                                         $PrintPageRangeNum .= " ".($v-1)."";
4981                                 }
4982                                 $this->_out('/PrintPageRange ['.substr($PrintPageRangeNum,1).']');
4983                         }
4984                         if (isset($this->viewer_preferences['NumCopies'])) {
4985                                 $this->_out('/NumCopies '.intval($this->viewer_preferences['NumCopies']));
4986                         }
4987                         $this->_out('>>');
4988                 }
4989
4990                 /**
4991                 * Output trailer.
4992                 * @access protected
4993                 */
4994                 function _puttrailer() {
4995                         $this->_out('/Size '.($this->n + 1));
4996                         $this->_out('/Root '.$this->n.' 0 R');
4997                         $this->_out('/Info '.($this->n - 1).' 0 R');
4998                         if ($this->encrypted) {
4999                                 $this->_out('/Encrypt '.$this->enc_obj_id.' 0 R');
5000                                 $this->_out('/ID [()()]');
5001                         }
5002                 }
5003
5004                 /**
5005                 * Output PDF header.
5006                 * @access protected
5007                 */
5008                 function _putheader() {
5009                         $this->buffer = '%PDF-'.$this->PDFVersion."\n".$this->buffer;
5010 //                      $this->_out('%PDF-'.$this->PDFVersion);
5011                 }
5012
5013                 /**
5014                 * Output end of document (EOF).
5015                 * @access protected
5016                 */
5017                 function _enddoc() {
5018                         $this->_putheader();
5019                         $this->_putpages();
5020                         $this->_putresources();
5021                         //Info
5022                         $this->_newobj();
5023                         $this->_out('<<');
5024                         $this->_putinfo();
5025                         $this->_out('>>');
5026                         $this->_out('endobj');
5027                         //Catalog
5028                         $this->_newobj();
5029                         $this->_out('<<');
5030                         $this->_putcatalog();
5031                         $this->_out('>>');
5032                         $this->_out('endobj');
5033                         //Cross-ref
5034                         $o = strlen($this->buffer);
5035                         $this->_out('xref');
5036                         $this->_out('0 '.($this->n + 1));
5037                         $this->_out('0000000000 65535 f ');
5038                         for($i=1; $i <= $this->n; $i++) {
5039                                 $this->_out(sprintf('%010d 00000 n ',$this->offsets[$i]));
5040                         }
5041                         //Trailer
5042                         $this->_out('trailer');
5043                         $this->_out('<<');
5044                         $this->_puttrailer();
5045                         $this->_out('>>');
5046                         $this->_out('startxref');
5047                         $this->_out($o);
5048                         $this->_out('%%EOF');
5049                         $this->state = 3;
5050                 }
5051
5052                 /**
5053                 * Initialize a new page.
5054                 * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
5055                 * @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>
5056                 * @access protected
5057                 */
5058                 function _beginpage($orientation='', $format='') {
5059                         $this->page++;
5060                         $this->pages[$this->page] = ""; // this mark should be removed before output
5061                         $this->state = 2;
5062                         if (empty($orientation)) {
5063                                 if (isset($this->CurOrientation)) {
5064                                         $orientation = $this->CurOrientation;
5065                                 } else {
5066                                         $orientation = 'P';
5067                                 }
5068                         }
5069                         if (!empty($format)) {
5070                                 $this->setPageFormat($format, $orientation);
5071                         } else {
5072                                 $this->setPageOrientation($orientation);
5073                         }
5074                         if ($this->rtl) {
5075                                 $this->x = $this->w - $this->rMargin;
5076                         } else {
5077                                 $this->x = $this->lMargin;
5078                         }
5079                         $this->y = $this->tMargin;
5080                         if ($this->newpagegroup){
5081                                 // start a new group
5082                                 $n = sizeof($this->pagegroups) + 1;
5083                                 $alias = "{nb".$n."}";
5084                                 $this->pagegroups[$alias] = 1;
5085                                 $this->currpagegroup = $alias;
5086                                 $this->newpagegroup = false;
5087                         } elseif ($this->currpagegroup) {
5088                                 $this->pagegroups[$this->currpagegroup]++;
5089                         }
5090                 }
5091
5092                 /**
5093                 * Mark end of page.
5094                 * @access protected
5095                 */
5096                 function _endpage() {
5097                         $this->setVisibility("all");
5098                         $this->state = 1;
5099                 }
5100
5101                 /**
5102                 * Begin a new object.
5103                 * @access protected
5104                 */
5105                 function _newobj() {
5106                         $this->n++;
5107                         $this->offsets[$this->n] = strlen($this->buffer);
5108                         $this->_out($this->n.' 0 obj');
5109                 }
5110
5111                 /**
5112                 * Underline text.
5113                 * @param int $x X coordinate
5114                 * @param int $y Y coordinate
5115                 * @param string $txt text to underline
5116                 * @access protected
5117                 */
5118                 function _dounderline($x, $y, $txt) {
5119                         $up = $this->CurrentFont['up'];
5120                         $ut = $this->CurrentFont['ut'];
5121                         $w = $this->GetStringWidth($txt);
5122                         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);
5123                 }
5124
5125                 /**
5126                 * Line through text.
5127                 * @param int $x X coordinate
5128                 * @param int $y Y coordinate
5129                 * @param string $txt text to underline
5130                 * @access protected
5131                 */
5132                 function _dolinethrough($x, $y, $txt) {
5133                         $up = $this->CurrentFont['up'];
5134                         $ut = $this->CurrentFont['ut'];
5135                         $w = $this->GetStringWidth($txt);
5136                         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);
5137                 }
5138
5139                 /**
5140                 * Read a 4-byte integer from file.
5141                 * @param string $f file name.
5142                 * @return 4-byte integer
5143                 * @access protected
5144                 */
5145                 function _freadint($f) {
5146                         $a = unpack('Ni', fread($f,4));
5147                         return $a['i'];
5148                 }
5149
5150                 /**
5151                 * Format a text string for meta information
5152                 * @param string $s string to escape.
5153                 * @return string escaped string.
5154                 * @access protected
5155                 */
5156                 function _textstring($s) {
5157                         if ($this->isunicode) {
5158                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5159                                         $s = $this->UTF8ToLatin1($s);
5160                                 } else {
5161                                         //Convert string to UTF-16BE
5162                                         $s = $this->UTF8ToUTF16BE($s, true);
5163                                 }
5164                         }
5165                         if ($this->encrypted) {
5166                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5167                         }
5168                         return '('. $this->_escape($s).')';
5169                 }
5170
5171                 /**
5172                 * Format an URI string
5173                 * @param string $s string to escape.
5174                 * @return string escaped string.
5175                 * @access protected
5176                 */
5177                 function _uristring($s) {
5178                         if ($this->encrypted) {
5179                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5180                         }
5181                         return '('.$this->_escape($s).')';
5182                 }
5183
5184                 /**
5185                 * Format a text string
5186                 * @param string $s string to escape.
5187                 * @return string escaped string.
5188                 * @access protected
5189                 */
5190                 function _escapetext($s) {
5191                         if ($this->isunicode) {
5192                                 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5193                                         $s = $this->UTF8ToLatin1($s);
5194                                 } else {
5195                                         //Convert string to UTF-16BE and reverse RTL language
5196                                         $s = $this->utf8StrRev($s, false, $this->tmprtl);
5197                                 }
5198                         }
5199                         return $this->_escape($s);
5200                 }
5201
5202                 /**
5203                 * Add "\" before "\", "(" and ")"
5204                 * @param string $s string to escape.
5205                 * @return string escaped string.
5206                 * @access protected
5207                 */
5208                 function _escape($s) {
5209                         // the chr(13) substitution fixes the Bugs item #1421290.
5210                         return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
5211                 }
5212
5213                 /**
5214                 * Output a stream.
5215                 * @param string $s string to output.
5216                 * @access protected
5217                 */
5218                 function _putstream($s) {
5219                         if ($this->encrypted) {
5220                                 $s = $this->_RC4($this->_objectkey($this->n), $s);
5221                         }
5222                         $this->_out('stream');
5223                         $this->_out($s);
5224                         $this->_out('endstream');
5225                 }
5226
5227                 /**
5228                 * Output a string to the document.
5229                 * @param string $s string to output.
5230                 * @access protected
5231                 */
5232                 function _out($s) {
5233                         if ($this->state == 2) {
5234                                 if (isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
5235                                         // puts data before page footer
5236                                         $page = substr($this->pages[$this->page], 0, -$this->footerlen[$this->page]);
5237                                         $footer = substr($this->pages[$this->page], -$this->footerlen[$this->page]);
5238                                         $this->pages[$this->page] = $page." ".$s."\n".$footer;
5239                                 } else {
5240                                         $this->pages[$this->page] .= $s."\n";
5241                                 }
5242                         } else {
5243                                 $this->buffer .= $s."\n";
5244                         }
5245                 }
5246
5247                 /**
5248                 * Adds unicode fonts.<br>
5249                 * Based on PDF Reference 1.3 (section 5)
5250                 * @access protected
5251                 * @author Nicola Asuni
5252                 * @since 1.52.0.TC005 (2005-01-05)
5253                 */
5254                 function _puttruetypeunicode($font) {
5255                         // Type0 Font
5256                         // A composite font composed of other fonts, organized hierarchically
5257                         $this->_newobj();
5258                         $this->_out('<</Type /Font');
5259                         $this->_out('/Subtype /Type0');
5260                         $this->_out('/BaseFont /'.$font['name'].'');
5261                         $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.
5262                         $this->_out('/DescendantFonts ['.($this->n + 1).' 0 R]');
5263                         $this->_out('/ToUnicode '.($this->n + 2).' 0 R');
5264                         $this->_out('>>');
5265                         $this->_out('endobj');
5266                         // CIDFontType2
5267                         // A CIDFont whose glyph descriptions are based on TrueType font technology
5268                         $this->_newobj();
5269                         $this->_out('<</Type /Font');
5270                         $this->_out('/Subtype /CIDFontType2');
5271                         $this->_out('/BaseFont /'.$font['name'].'');
5272                         $this->_out('/CIDSystemInfo '.($this->n + 2).' 0 R');
5273                         $this->_out('/FontDescriptor '.($this->n + 3).' 0 R');
5274                         if (isset($font['desc']['MissingWidth'])){
5275                                 $this->_out('/DW '.$font['desc']['MissingWidth'].''); // The default width for glyphs in the CIDFont MissingWidth
5276                         }
5277                         $w = "";
5278                         foreach ($font['cw'] as $cid => $width) {
5279                                 $w .= ''.$cid.' ['.$width.'] '; // define a specific width for each individual CID
5280                         }
5281                         $this->_out('/W ['.$w.']'); // A description of the widths for the glyphs in the CIDFont
5282                         $this->_out('/CIDToGIDMap '.($this->n + 4).' 0 R');
5283                         $this->_out('>>');
5284                         $this->_out('endobj');
5285                         // ToUnicode
5286                         // is a stream object that contains the definition of the CMap
5287                         // (PDF Reference 1.3 chap. 5.9)
5288                         $this->_newobj();
5289                         $this->_out('<</Length 345>>');
5290                         $this->_out('stream');
5291                         $this->_out('/CIDInit /ProcSet findresource begin');
5292                         $this->_out('12 dict begin');
5293                         $this->_out('begincmap');
5294                         $this->_out('/CIDSystemInfo');
5295                         $this->_out('<</Registry (Adobe)');
5296                         $this->_out('/Ordering (UCS)');
5297                         $this->_out('/Supplement 0');
5298                         $this->_out('>> def');
5299                         $this->_out('/CMapName /Adobe-Identity-UCS def');
5300                         $this->_out('/CMapType 2 def');
5301                         $this->_out('1 begincodespacerange');
5302                         $this->_out('<0000> <FFFF>');
5303                         $this->_out('endcodespacerange');
5304                         $this->_out('1 beginbfrange');
5305                         $this->_out('<0000> <FFFF> <0000>');
5306                         $this->_out('endbfrange');
5307                         $this->_out('endcmap');
5308                         $this->_out('CMapName currentdict /CMap defineresource pop');
5309                         $this->_out('end');
5310                         $this->_out('end');
5311                         $this->_out('endstream');
5312                         $this->_out('endobj');
5313                         // CIDSystemInfo dictionary
5314                         // A dictionary containing entries that define the character collection of the CIDFont.
5315                         $this->_newobj();
5316                         $this->_out('<</Registry (Adobe)'); // A string identifying an issuer of character collections
5317                         $this->_out('/Ordering (UCS)'); // A string that uniquely names a character collection issued by a specific registry
5318                         $this->_out('/Supplement 0'); // The supplement number of the character collection.
5319                         $this->_out('>>');
5320                         $this->_out('endobj');
5321                         // Font descriptor
5322                         // A font descriptor describing the CIDFont default metrics other than its glyph widths
5323                         $this->_newobj();
5324                         $this->_out('<</Type /FontDescriptor');
5325                         $this->_out('/FontName /'.$font['name']);
5326                         foreach ($font['desc'] as $key => $value) {
5327                                 $this->_out('/'.$key.' '.$value);
5328                         }
5329                         if ($font['file']) {
5330                                 // A stream containing a TrueType font program
5331                                 $this->_out('/FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R');
5332                         }
5333                         $this->_out('>>');
5334                         $this->_out('endobj');
5335                         // Embed CIDToGIDMap
5336                         // A specification of the mapping from CIDs to glyph indices
5337                         $this->_newobj();
5338                         $ctgfile = $this->_getfontpath().strtolower($font['ctg']);
5339                         if (!file_exists($ctgfile)) {
5340                                 $this->Error('Font file not found: '.$ctgfile);
5341                         }
5342                         $size = filesize($ctgfile);
5343                         $this->_out('<</Length '.$size.'');
5344                         if (substr($ctgfile, -2) == '.z') { // check file extension
5345                                 /* Decompresses data encoded using the public-domain
5346                                 zlib/deflate compression method, reproducing the
5347                                 original text or binary data */
5348                                 $this->_out('/Filter /FlateDecode');
5349                         }
5350                         $this->_out('>>');
5351                         $this->_putstream(file_get_contents($ctgfile));
5352                         $this->_out('endobj');
5353                 }
5354
5355                  /**
5356                  * Converts UTF-8 strings to codepoints array.<br>
5357                  * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
5358                  * Based on: http://www.faqs.org/rfcs/rfc3629.html
5359                  * <pre>
5360                  *        Char. number range  |        UTF-8 octet sequence
5361                  *       (hexadecimal)    |              (binary)
5362                  *    --------------------+-----------------------------------------------
5363                  *    0000 0000-0000 007F | 0xxxxxxx
5364                  *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
5365                  *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
5366                  *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5367                  *    ---------------------------------------------------------------------
5368                  *
5369                  *   ABFN notation:
5370                  *   ---------------------------------------------------------------------
5371                  *   UTF8-octets = *( UTF8-char )
5372                  *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
5373                  *   UTF8-1      = %x00-7F
5374                  *   UTF8-2      = %xC2-DF UTF8-tail
5375                  *
5376                  *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
5377                  *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
5378                  *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
5379                  *                 %xF4 %x80-8F 2( UTF8-tail )
5380                  *   UTF8-tail   = %x80-BF
5381                  *   ---------------------------------------------------------------------
5382                  * </pre>
5383                  * @param string $str string to process.
5384                  * @return array containing codepoints (UTF-8 characters values)
5385                  * @access protected
5386                  * @author Nicola Asuni
5387                  * @since 1.53.0.TC005 (2005-01-05)
5388                  */
5389                 function UTF8StringToArray($str) {
5390                         if (!$this->isunicode) {
5391                                 // split string into array of equivalent codes
5392                                 $strarr = array();
5393                                 $strlen = strlen($str);
5394                                 for($i=0; $i < $strlen; $i++) {
5395                                         $strarr[] = ord($str{$i});
5396                                 }
5397                                 return $strarr;
5398                         }
5399                         $unicode = array(); // array containing unicode values
5400                         $bytes  = array(); // array containing single character byte sequences
5401                         $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
5402                         $str .= ""; // force $str to be a string
5403                         $length = strlen($str);
5404                         for($i = 0; $i < $length; $i++) {
5405                                 $char = ord($str{$i}); // get one string character at time
5406                                 if (count($bytes) == 0) { // get starting octect
5407                                         if ($char <= 0x7F) {
5408                                                 $unicode[] = $char; // use the character "as is" because is ASCII
5409                                                 $numbytes = 1;
5410                                         } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
5411                                                 $bytes[] = ($char - 0xC0) << 0x06;
5412                                                 $numbytes = 2;
5413                                         } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
5414                                                 $bytes[] = ($char - 0xE0) << 0x0C;
5415                                                 $numbytes = 3;
5416                                         } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
5417                                                 $bytes[] = ($char - 0xF0) << 0x12;
5418                                                 $numbytes = 4;
5419                                         } else {
5420                                                 // use replacement character for other invalid sequences
5421                                                 $unicode[] = 0xFFFD;
5422                                                 $bytes = array();
5423                                                 $numbytes = 1;
5424                                         }
5425                                 } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
5426                                         $bytes[] = $char - 0x80;
5427                                         if (count($bytes) == $numbytes) {
5428                                                 // compose UTF-8 bytes to a single unicode value
5429                                                 $char = $bytes[0];
5430                                                 for($j = 1; $j < $numbytes; $j++) {
5431                                                         $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
5432                                                 }
5433                                                 if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
5434                                                         /* The definition of UTF-8 prohibits encoding character numbers between
5435                                                         U+D800 and U+DFFF, which are reserved for use with the UTF-16
5436                                                         encoding form (as surrogate pairs) and do not directly represent
5437                                                         characters. */
5438                                                         $unicode[] = 0xFFFD; // use replacement character
5439                                                 } else {
5440                                                         $unicode[] = $char; // add char to array
5441                                                 }
5442                                                 // reset data for next char
5443                                                 $bytes = array();
5444                                                 $numbytes = 1;
5445                                         }
5446                                 } else {
5447                                         // use replacement character for other invalid sequences
5448                                         $unicode[] = 0xFFFD;
5449                                         $bytes = array();
5450                                         $numbytes = 1;
5451                                 }
5452                         }
5453                         return $unicode;
5454                 }
5455
5456                 /**
5457                  * Converts UTF-8 strings to UTF16-BE.<br>
5458                  * @param string $str string to process.
5459                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5460                  * @return string
5461                  * @access protected
5462                  * @author Nicola Asuni
5463                  * @since 1.53.0.TC005 (2005-01-05)
5464                  * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
5465                  */
5466                 function UTF8ToUTF16BE($str, $setbom=true) {
5467                         if (!$this->isunicode) {
5468                                 return $str; // string is not in unicode
5469                         }
5470                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5471                         return $this->arrUTF8ToUTF16BE($unicode, $setbom);
5472                 }
5473
5474                 /**
5475                  * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
5476                  * @param string $str string to process.
5477                  * @return string
5478                  * @author Andrew Whitehead, Nicola Asuni
5479                  * @access protected
5480                  * @since 3.2.000 (2008-06-23)
5481                  */
5482                 function UTF8ToLatin1($str) {
5483                         if (!$this->isunicode) {
5484                                 return $str; // string is not in unicode
5485                         }
5486                         $outstr = ""; // string to be returned
5487                         $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
5488                         foreach ($unicode as $char) {
5489                                 if ($char == 0xFFFD) {
5490                                         // skip
5491                                 } elseif ($char == 0x2022) {
5492                                         // fix for middot
5493                                         $outstr .= chr(183);
5494                                 } elseif ($char < 256) {
5495                                         $outstr .= chr($char);
5496                                 } else {
5497                                         $outstr .= '?';
5498                                 }
5499                         }
5500                         return $outstr;
5501                 }
5502
5503                 /**
5504                  * Converts array of UTF-8 characters to UTF16-BE string.<br>
5505                  * Based on: http://www.faqs.org/rfcs/rfc2781.html
5506                  * <pre>
5507                  *   Encoding UTF-16:
5508                  *
5509                  *   Encoding of a single character from an ISO 10646 character value to
5510                  *    UTF-16 proceeds as follows. Let U be the character number, no greater
5511                  *    than 0x10FFFF.
5512                  *
5513                  *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
5514                  *       terminate.
5515                  *
5516                  *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
5517                  *       U' must be less than or equal to 0xFFFFF. That is, U' can be
5518                  *       represented in 20 bits.
5519                  *
5520                  *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
5521                  *       0xDC00, respectively. These integers each have 10 bits free to
5522                  *       encode the character value, for a total of 20 bits.
5523                  *
5524                  *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
5525                  *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
5526                  *       bits of W2. Terminate.
5527                  *
5528                  *    Graphically, steps 2 through 4 look like:
5529                  *    U' = yyyyyyyyyyxxxxxxxxxx
5530                  *    W1 = 110110yyyyyyyyyy
5531                  *    W2 = 110111xxxxxxxxxx
5532                  * </pre>
5533                  * @param array $unicode array containing UTF-8 unicode values
5534                  * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
5535                  * @return string
5536                  * @access protected
5537                  * @author Nicola Asuni
5538                  * @since 2.1.000 (2008-01-08)
5539                  * @see UTF8ToUTF16BE()
5540                  */
5541                 function arrUTF8ToUTF16BE($unicode, $setbom=true) {
5542                         $outstr = ""; // string to be returned
5543                         if ($setbom) {
5544                                 $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
5545                         }
5546                         foreach($unicode as $char) {
5547                                 if ($char == 0xFFFD) {
5548                                         $outstr .= "\xFF\xFD"; // replacement character
5549                                 } elseif ($char < 0x10000) {
5550                                         $outstr .= chr($char >> 0x08);
5551                                         $outstr .= chr($char & 0xFF);
5552                                 } else {
5553                                         $char -= 0x10000;
5554                                         $w1 = 0xD800 | ($char >> 0x10);
5555                                         $w2 = 0xDC00 | ($char & 0x3FF);
5556                                         $outstr .= chr($w1 >> 0x08);
5557                                         $outstr .= chr($w1 & 0xFF);
5558                                         $outstr .= chr($w2 >> 0x08);
5559                                         $outstr .= chr($w2 & 0xFF);
5560                                 }
5561                         }
5562                         return $outstr;
5563                 }
5564                 // ====================================================
5565
5566                 /**
5567                  * Set header font.
5568                  * @param array $font font
5569                  * @since 1.1
5570                  */
5571                 function setHeaderFont($font) {
5572                         $this->header_font = $font;
5573                 }
5574
5575                 /**
5576                  * Get header font.
5577                  * @return array()
5578                  * @since 4.0.012 (2008-07-24)
5579                  */
5580                 function getHeaderFont() {
5581                         return $this->header_font;
5582                 }
5583
5584                 /**
5585                  * Set footer font.
5586                  * @param array $font font
5587                  * @since 1.1
5588                  */
5589                 function setFooterFont($font) {
5590                         $this->footer_font = $font;
5591                 }
5592
5593                 /**
5594                  * Get Footer font.
5595                  * @return array()
5596                  * @since 4.0.012 (2008-07-24)
5597                  */
5598                 function getFooterFont() {
5599                         return $this->footer_font;
5600                 }
5601
5602                 /**
5603                  * Set language array.
5604                  * @param array $language
5605                  * @since 1.1
5606                  */
5607                 function setLanguageArray($language) {
5608                         $this->l = $language;
5609                         $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
5610                 }
5611
5612                 /**
5613                  * Returns the PDF data.
5614                  */
5615                 function getPDFData() {
5616                         if ($this->state < 3) {
5617                                 $this->Close();
5618                         }
5619                         return $this->buffer;
5620                 }
5621
5622                 /**
5623                  * Sets font style.
5624                  * @param string $tag tag name in lowercase. Supported tags are:<ul>
5625                  * <li>b : bold text</li>
5626                  * <li>i : italic</li>
5627                  * <li>u : underlined</li>
5628                  * <li>d : line-through</li></ul>
5629                  * @param boolean $enable
5630                  * @access protected
5631                  */
5632                 function setStyle($tag, $enable) {
5633                         $this->$tag += ($enable ? 1 : -1);
5634                         $style = '';
5635                         foreach(array('b', 'i', 'u', 'd') as $s) {
5636                                 if ($this->$s > 0) {
5637                                         $style .= $s;
5638                                 }
5639                         }
5640                         $this->SetFont('', $style);
5641                 }
5642
5643                 /**
5644                  * Output anchor link.
5645                  * @param string $url link URL
5646                  * @param string $name link name
5647                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
5648                  * @param boolean $firstline if true prints only the first line and return the remaining string.
5649                  * @return the number of cells used or the remaining text if $firstline = true;
5650                  * @access public
5651                  */
5652                 function addHtmlLink($url, $name, $fill=0, $firstline=false) {
5653                         $prevcolor = $this->fgcolor;
5654                         $this->SetTextColor(0, 0, 255);
5655                         $this->setStyle('u', true);
5656                         $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline);
5657                         $this->setStyle('u', false);
5658                         $this->SetTextColorArray($prevcolor);
5659                         return $ret;
5660                 }
5661
5662                 /**
5663                  * 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).
5664                  * @param string $color html color
5665                  * @return array
5666                  * @access protected
5667                  */
5668                 function convertHTMLColorToDec($color="#000000") {
5669                         global $webcolor;
5670                         $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
5671                         // set default color to be returned in case of error
5672                         $returncolor = array ('R' => 0, 'G' => 0, 'B' => 0);
5673                         if (empty($color)) {
5674                                 return $returncolor;
5675                         }
5676                         if (substr(strtolower($color), 0, 3) == 'rgb') {
5677                                 $codes = substr($color, 4);
5678                                 $codes = str_replace(')', '', $codes);
5679                                 $returncolor = explode(',', $codes, 3);
5680                                 return $returncolor;    
5681                         }
5682                         if (substr($color, 0, 1) != "#") {
5683                                 // decode color name
5684                                 if (isset($webcolor[strtolower($color)])) {
5685                                         $color_code = $webcolor[strtolower($color)];
5686                                 } else {
5687                                         return $returncolor;
5688                                 }
5689                         } else {
5690                                 $color_code = substr($color, 1);
5691                         }
5692                         switch (strlen($color_code)) {
5693                                 case 3: {
5694                                         // three-digit hexadecimal representation
5695                                         $r = substr($color_code, 0, 1);
5696                                         $g = substr($color_code, 1, 1);
5697                                         $b = substr($color_code, 2, 1);
5698                                         $returncolor['R'] = hexdec($r.$r);
5699                                         $returncolor['G'] = hexdec($g.$g);
5700                                         $returncolor['B'] = hexdec($b.$b);
5701                                         break;
5702                                 }
5703                                 case 6: {
5704                                         // six-digit hexadecimal representation
5705                                         $returncolor['R'] = hexdec(substr($color_code, 0, 2));
5706                                         $returncolor['G'] = hexdec(substr($color_code, 2, 2));
5707                                         $returncolor['B'] = hexdec(substr($color_code, 4, 2));
5708                                         break;
5709                                 }
5710                         }
5711                         return $returncolor;
5712                 }
5713
5714                 /**
5715                  * Converts pixels to Units.
5716                  * @param int $px pixels
5717                  * @return float millimeters
5718                  * @access public
5719                  */
5720                 function pixelsToUnits($px){
5721                         return $px / $this->k;
5722                 }
5723
5724                 /**
5725                  * Reverse function for htmlentities.
5726                  * Convert entities in UTF-8.
5727                  * @param $text_to_convert Text to convert.
5728                  * @return string converted
5729                  */
5730                 function unhtmlentities($text_to_convert) {
5731                         if (!$this->isunicode) {
5732                                 return html_entity_decode($text_to_convert, ENT_QUOTES);
5733                         }
5734                         return html_entity_decode_php4($text_to_convert);
5735                 }
5736
5737                 // ENCRYPTION METHODS ----------------------------------
5738                 // SINCE 2.0.000 (2008-01-02)
5739                 /**
5740                 * Compute encryption key depending on object number where the encrypted data is stored
5741                 * @param int $n object number
5742                 * @since 2.0.000 (2008-01-02)
5743                 */
5744                 function _objectkey($n) {
5745                         return substr($this->_md5_16($this->encryption_key.pack('VXxx',$n)),0,10);
5746                 }
5747
5748                 /**
5749                  * Put encryption on PDF document.
5750                  * @since 2.0.000 (2008-01-02)
5751                  */
5752                 function _putencryption() {
5753                         $this->_out('/Filter /Standard');
5754                         $this->_out('/V 1');
5755                         $this->_out('/R 2');
5756                         $this->_out('/O ('.$this->_escape($this->Ovalue).')');
5757                         $this->_out('/U ('.$this->_escape($this->Uvalue).')');
5758                         $this->_out('/P '.$this->Pvalue);
5759                 }
5760
5761                 /**
5762                 * Returns the input text exrypted using RC4 algorithm and the specified key.
5763                 * RC4 is the standard encryption algorithm used in PDF format
5764                 * @param string $key encryption key
5765                 * @param String $text input text to be encrypted
5766                 * @return String encrypted text
5767                 * @since 2.0.000 (2008-01-02)
5768                 * @author Klemen Vodopivec
5769                 */
5770                 function _RC4($key, $text) {
5771                         if ($this->last_rc4_key != $key) {
5772                                 $k = str_repeat($key, 256/strlen($key)+1);
5773                                 $rc4 = range(0,255);
5774                                 $j = 0;
5775                                 for ($i=0; $i < 256; $i++) {
5776                                         $t = $rc4[$i];
5777                                         $j = ($j + $t + ord($k{$i})) % 256;
5778                                         $rc4[$i] = $rc4[$j];
5779                                         $rc4[$j] = $t;
5780                                 }
5781                                 $this->last_rc4_key = $key;
5782                                 $this->last_rc4_key_c = $rc4;
5783                         } else {
5784                                 $rc4 = $this->last_rc4_key_c;
5785                         }
5786                         $len = strlen($text);
5787                         $a = 0;
5788                         $b = 0;
5789                         $out = '';
5790                         for ($i=0; $i < $len; $i++) {
5791                                 $a = ($a + 1) % 256;
5792                                 $t = $rc4[$a];
5793                                 $b = ($b + $t) % 256;
5794                                 $rc4[$a] = $rc4[$b];
5795                                 $rc4[$b] = $t;
5796                                 $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
5797                                 $out .= chr(ord($text{$i}) ^ $k);
5798                         }
5799                         return $out;
5800                 }
5801
5802                 /**
5803                 * Encrypts a string using MD5 and returns it's value as a binary string.
5804                 * @param string $str input string
5805                 * @return String MD5 encrypted binary string
5806                 * @since 2.0.000 (2008-01-02)
5807                 * @author Klemen Vodopivec
5808                 */
5809                 function _md5_16($str) {
5810                         return pack('H*',md5($str));
5811                 }
5812
5813                 /**
5814                 * Compute O value (used for RC4 encryption)
5815                 * @param String $user_pass user password
5816                 * @param String $owner_pass user password
5817                 * @return String O value
5818                 * @since 2.0.000 (2008-01-02)
5819                 * @author Klemen Vodopivec
5820                 */
5821                 function _Ovalue($user_pass, $owner_pass) {
5822                         $tmp = $this->_md5_16($owner_pass);
5823                         $owner_RC4_key = substr($tmp,0,5);
5824                         return $this->_RC4($owner_RC4_key, $user_pass);
5825                 }
5826
5827                 /**
5828                 * Compute U value (used for RC4 encryption)
5829                 * @return String U value
5830                 * @since 2.0.000 (2008-01-02)
5831                 * @author Klemen Vodopivec
5832                 */
5833                 function _Uvalue() {
5834                         return $this->_RC4($this->encryption_key, $this->padding);
5835                 }
5836
5837                 /**
5838                 * Compute encryption key
5839                 * @param String $user_pass user password
5840                 * @param String $owner_pass user password
5841                 * @param String $protection protection type
5842                 * @since 2.0.000 (2008-01-02)
5843                 * @author Klemen Vodopivec
5844                 */
5845                 function _generateencryptionkey($user_pass, $owner_pass, $protection) {
5846                         // Pad passwords
5847                         $user_pass = substr($user_pass.$this->padding,0,32);
5848                         $owner_pass = substr($owner_pass.$this->padding,0,32);
5849                         // Compute O value
5850                         $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
5851                         // Compute encyption key
5852                         $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
5853                         $this->encryption_key = substr($tmp,0,5);
5854                         // Compute U value
5855                         $this->Uvalue = $this->_Uvalue();
5856                         // Compute P value
5857                         $this->Pvalue = -(($protection^255)+1);
5858                 }
5859
5860                 /**
5861                 * Set document protection
5862                 * The permission array is composed of values taken from the following ones:
5863                 * - copy: copy text and images to the clipboard
5864                 * - print: print the document
5865                 * - modify: modify it (except for annotations and forms)
5866                 * - annot-forms: add annotations and forms
5867                 * Remark: the protection against modification is for people who have the full Acrobat product.
5868                 * 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.
5869                 * 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.
5870                 * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
5871                 * @param String $user_pass user password. Empty by default.
5872                 * @param String $owner_pass owner password. If not specified, a random value is used.
5873                 * @since 2.0.000 (2008-01-02)
5874                 * @author Klemen Vodopivec
5875                 */
5876                 function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
5877                         $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
5878                         $protection = 192;
5879                         foreach($permissions as $permission) {
5880                                 if (!isset($options[$permission])) {
5881                                         $this->Error('Incorrect permission: '.$permission);
5882                                 }
5883                                 $protection += $options[$permission];
5884                         }
5885                         if ($owner_pass === null) {
5886                                 $owner_pass = uniqid(rand());
5887                         }
5888                         $this->encrypted = true;
5889                         $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
5890                 }
5891
5892                 // END OF ENCRYPTION FUNCTIONS -------------------------
5893
5894                 // START TRANSFORMATIONS SECTION -----------------------
5895                 // authors: Moritz Wagner, Andreas Wurmser, Nicola Asuni
5896
5897                 /**
5898                 * Starts a 2D tranformation saving current graphic state.
5899                 * This function must be called before scaling, mirroring, translation, rotation and skewing.
5900                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5901                 * @since 2.1.000 (2008-01-07)
5902                 * @see StartTransform(), StopTransform()
5903                 */
5904                 function StartTransform() {
5905                         $this->_out('q');
5906                 }
5907
5908                 /**
5909                 * Stops a 2D tranformation restoring previous graphic state.
5910                 * This function must be called after scaling, mirroring, translation, rotation and skewing.
5911                 * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
5912                 * @since 2.1.000 (2008-01-07)
5913                 * @see StartTransform(), StopTransform()
5914                 */
5915                 function StopTransform() {
5916                         $this->_out('Q');
5917                 }
5918                 /**
5919                 * Horizontal Scaling.
5920                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5921                 * @param int $x abscissa of the scaling center. Default is current x position
5922                 * @param int $y ordinate of the scaling center. Default is current y position
5923                 * @since 2.1.000 (2008-01-07)
5924                 * @see StartTransform(), StopTransform()
5925                 */
5926                 function ScaleX($s_x, $x='', $y=''){
5927                         $this->Scale($s_x, 100, $x, $y);
5928                 }
5929
5930                 /**
5931                 * Vertical Scaling.
5932                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5933                 * @param int $x abscissa of the scaling center. Default is current x position
5934                 * @param int $y ordinate of the scaling center. Default is current y position
5935                 * @since 2.1.000 (2008-01-07)
5936                 * @see StartTransform(), StopTransform()
5937                 */
5938                 function ScaleY($s_y, $x='', $y=''){
5939                         $this->Scale(100, $s_y, $x, $y);
5940                 }
5941
5942                 /**
5943                 * Vertical and horizontal proportional Scaling.
5944                 * @param float $s scaling factor for width and height as percent. 0 is not allowed.
5945                 * @param int $x abscissa of the scaling center. Default is current x position
5946                 * @param int $y ordinate of the scaling center. Default is current y position
5947                 * @since 2.1.000 (2008-01-07)
5948                 * @see StartTransform(), StopTransform()
5949                 */
5950                 function ScaleXY($s, $x='', $y=''){
5951                         $this->Scale($s, $s, $x, $y);
5952                 }
5953
5954                 /**
5955                 * Vertical and horizontal non-proportional Scaling.
5956                 * @param float $s_x scaling factor for width as percent. 0 is not allowed.
5957                 * @param float $s_y scaling factor for height as percent. 0 is not allowed.
5958                 * @param int $x abscissa of the scaling center. Default is current x position
5959                 * @param int $y ordinate of the scaling center. Default is current y position
5960                 * @since 2.1.000 (2008-01-07)
5961                 * @see StartTransform(), StopTransform()
5962                 */
5963                 function Scale($s_x, $s_y, $x='', $y=''){
5964                         if ($x === '') {
5965                                 $x=$this->x;
5966                         }
5967                         if ($y === '') {
5968                                 $y=$this->y;
5969                         }
5970                         if ($this->rtl) {
5971                                 $x = $this->w - $x;
5972                         }
5973                         if (($s_x == 0) OR ($s_y == 0)) {
5974                                 $this->Error('Please do not use values equal to zero for scaling');
5975                         }
5976                         $y = ($this->h - $y) * $this->k;
5977                         $x *= $this->k;
5978                         //calculate elements of transformation matrix
5979                         $s_x /= 100;
5980                         $s_y /= 100;
5981                         $tm[0] = $s_x;
5982                         $tm[1] = 0;
5983                         $tm[2] = 0;
5984                         $tm[3] = $s_y;
5985                         $tm[4] = $x * (1 - $s_x);
5986                         $tm[5] = $y * (1 - $s_y);
5987                         //scale the coordinate system
5988                         $this->Transform($tm);
5989                 }
5990
5991                 /**
5992                 * Horizontal Mirroring.
5993                 * @param int $x abscissa of the point. Default is current x position
5994                 * @since 2.1.000 (2008-01-07)
5995                 * @see StartTransform(), StopTransform()
5996                 */
5997                 function MirrorH($x=''){
5998                         $this->Scale(-100, 100, $x);
5999                 }
6000
6001                 /**
6002                 * Verical Mirroring.
6003                 * @param int $y ordinate of the point. Default is current y position
6004                 * @since 2.1.000 (2008-01-07)
6005                 * @see StartTransform(), StopTransform()
6006                 */
6007                 function MirrorV($y=''){
6008                         $this->Scale(100, -100, '', $y);
6009                 }
6010
6011                 /**
6012                 * Point reflection mirroring.
6013                 * @param int $x abscissa of the point. Default is current x position
6014                 * @param int $y ordinate of the point. Default is current y position
6015                 * @since 2.1.000 (2008-01-07)
6016                 * @see StartTransform(), StopTransform()
6017                 */
6018                 function MirrorP($x='',$y=''){
6019                         $this->Scale(-100, -100, $x, $y);
6020                 }
6021
6022                 /**
6023                 * Reflection against a straight line through point (x, y) with the gradient angle (angle).
6024                 * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
6025                 * @param int $x abscissa of the point. Default is current x position
6026                 * @param int $y ordinate of the point. Default is current y position
6027                 * @since 2.1.000 (2008-01-07)
6028                 * @see StartTransform(), StopTransform()
6029                 */
6030                 function MirrorL($angle=0, $x='',$y=''){
6031                         $this->Scale(-100, 100, $x, $y);
6032                         $this->Rotate(-2*($angle-90), $x, $y);
6033                 }
6034
6035                 /**
6036                 * Translate graphic object horizontally.
6037                 * @param int $t_x movement to the right (or left for RTL)
6038                 * @since 2.1.000 (2008-01-07)
6039                 * @see StartTransform(), StopTransform()
6040                 */
6041                 function TranslateX($t_x){
6042                         $this->Translate($t_x, 0);
6043                 }
6044
6045                 /**
6046                 * Translate graphic object vertically.
6047                 * @param int $t_y movement to the bottom
6048                 * @since 2.1.000 (2008-01-07)
6049                 * @see StartTransform(), StopTransform()
6050                 */
6051                 function TranslateY($t_y){
6052                         $this->Translate(0, $t_y);
6053                 }
6054
6055                 /**
6056                 * Translate graphic object horizontally and vertically.
6057                 * @param int $t_x movement to the right
6058                 * @param int $t_y movement to the bottom
6059                 * @since 2.1.000 (2008-01-07)
6060                 * @see StartTransform(), StopTransform()
6061                 */
6062                 function Translate($t_x, $t_y){
6063                         if ($this->rtl) {
6064                                 $t_x = -$t_x;
6065                         }
6066                         //calculate elements of transformation matrix
6067                         $tm[0] = 1;
6068                         $tm[1] = 0;
6069                         $tm[2] = 0;
6070                         $tm[3] = 1;
6071                         $tm[4] = $t_x * $this->k;
6072                         $tm[5] = -$t_y * $this->k;
6073                         //translate the coordinate system
6074                         $this->Transform($tm);
6075                 }
6076
6077                 /**
6078                 * Rotate object.
6079                 * @param float $angle angle in degrees for counter-clockwise rotation
6080                 * @param int $x abscissa of the rotation center. Default is current x position
6081                 * @param int $y ordinate of the rotation center. Default is current y position
6082                 * @since 2.1.000 (2008-01-07)
6083                 * @see StartTransform(), StopTransform()
6084                 */
6085                 function Rotate($angle, $x='', $y=''){
6086                         if ($x === '') {
6087                                 $x=$this->x;
6088                         }
6089                         if ($y === '') {
6090                                 $y=$this->y;
6091                         }
6092                         if ($this->rtl) {
6093                                 $x = $this->w - $x;
6094                                 $angle = -$angle;
6095                         }
6096                         $y = ($this->h - $y) * $this->k;
6097                         $x *= $this->k;
6098                         //calculate elements of transformation matrix
6099                         $tm[0] = cos(deg2rad($angle));
6100                         $tm[1] = sin(deg2rad($angle));
6101                         $tm[2] = -$tm[1];
6102                         $tm[3] = $tm[0];
6103                         $tm[4] = $x + $tm[1] * $y - $tm[0] * $x;
6104                         $tm[5] = $y - $tm[0] * $y - $tm[1] * $x;
6105                         //rotate the coordinate system around ($x,$y)
6106                         $this->Transform($tm);
6107                 }
6108
6109                 /**
6110                 * Skew horizontally.
6111                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6112                 * @param int $x abscissa of the skewing center. default is current x position
6113                 * @param int $y ordinate of the skewing center. default is current y position
6114                 * @since 2.1.000 (2008-01-07)
6115                 * @see StartTransform(), StopTransform()
6116                 */
6117                 function SkewX($angle_x, $x='', $y=''){
6118                         $this->Skew($angle_x, 0, $x, $y);
6119                 }
6120
6121                 /**
6122                 * Skew vertically.
6123                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6124                 * @param int $x abscissa of the skewing center. default is current x position
6125                 * @param int $y ordinate of the skewing center. default is current y position
6126                 * @since 2.1.000 (2008-01-07)
6127                 * @see StartTransform(), StopTransform()
6128                 */
6129                 function SkewY($angle_y, $x='', $y=''){
6130                         $this->Skew(0, $angle_y, $x, $y);
6131                 }
6132
6133                 /**
6134                 * Skew.
6135                 * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
6136                 * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
6137                 * @param int $x abscissa of the skewing center. default is current x position
6138                 * @param int $y ordinate of the skewing center. default is current y position
6139                 * @since 2.1.000 (2008-01-07)
6140                 * @see StartTransform(), StopTransform()
6141                 */
6142                 function Skew($angle_x, $angle_y, $x='', $y=''){
6143                         if ($x === '') {
6144                                 $x = $this->x;
6145                         }
6146                         if ($y === '') {
6147                                 $y = $this->y;
6148                         }
6149                         if ($this->rtl) {
6150                                 $x = $this->w - $x;
6151                                 $angle_x = -$angle_x;
6152                         }
6153                         if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
6154                                 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
6155                         }
6156                         $x *= $this->k;
6157                         $y = ($this->h - $y) * $this->k;
6158                         //calculate elements of transformation matrix
6159                         $tm[0] = 1;
6160                         $tm[1] = tan(deg2rad($angle_y));
6161                         $tm[2] = tan(deg2rad($angle_x));
6162                         $tm[3] = 1;
6163                         $tm[4] = -$tm[2] * $y;
6164                         $tm[5] = -$tm[1] * $x;
6165                         //skew the coordinate system
6166                         $this->Transform($tm);
6167                 }
6168
6169                 /**
6170                 * Apply graphic transformations.
6171                 * @since 2.1.000 (2008-01-07)
6172                 * @see StartTransform(), StopTransform()
6173                 */
6174                 function Transform($tm){
6175                         $this->_out(sprintf('%.3f %.3f %.3f %.3f %.3f %.3f cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
6176                 }
6177
6178                 // END TRANSFORMATIONS SECTION -------------------------
6179
6180
6181                 // START GRAPHIC FUNCTIONS SECTION ---------------------
6182                 // The following section is based on the code provided by David Hernandez Sanz
6183
6184                 /**
6185                 * 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.
6186                 * @param float $width The width.
6187                 * @since 1.0
6188                 * @see Line(), Rect(), Cell(), MultiCell()
6189                 */
6190                 function SetLineWidth($width) {
6191                         //Set line width
6192                         $this->LineWidth = $width;
6193                         $this->linestyleWidth = sprintf('%.2f w', ($width * $this->k));
6194                         $this->_out($this->linestyleWidth);
6195                 }
6196
6197                 /**
6198                 * Returns the current the line width.
6199                 * @return int Line width
6200                 * @since 2.1.000 (2008-01-07)
6201                 * @see Line(), SetLineWidth()
6202                 */
6203                 function GetLineWidth() {
6204                         return $this->LineWidth;
6205                 }
6206
6207                 /**
6208                 * Set line style.
6209                 * @param array $style Line style. Array with keys among the following:
6210                 * <ul>
6211                 *        <li>width (float): Width of the line in user units.</li>
6212                 *        <li>cap (string): Type of cap to put on the line. Possible values are:
6213                 * butt, round, square. The difference between "square" and "butt" is that
6214                 * "square" projects a flat end past the end of the line.</li>
6215                 *        <li>join (string): Type of join. Possible values are: miter, round,
6216                 * bevel.</li>
6217                 *        <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
6218                 * series of length values, which are the lengths of the on and off dashes.
6219                 * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
6220                 * 1 off, 2 on, 1 off, ...</li>
6221                 *        <li>phase (integer): Modifier on the dash pattern which is used to shift
6222                 * the point at which the pattern starts.</li>
6223                 *        <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
6224                 * </ul>
6225                 * @access public
6226                 * @since 2.1.000 (2008-01-08)
6227                 */
6228                 function SetLineStyle($style) {
6229                         extract($style);
6230                         if (isset($width)) {
6231                                 $width_prev = $this->LineWidth;
6232                                 $this->SetLineWidth($width);
6233                                 $this->LineWidth = $width_prev;
6234                         }
6235                         if (isset($cap)) {
6236                                 $ca = array("butt" => 0, "round"=> 1, "square" => 2);
6237                                 if (isset($ca[$cap])) {
6238                                         $this->linestyleCap = $ca[$cap]." J";
6239                                         $this->_out($this->linestyleCap);
6240                                 }
6241                         }
6242                         if (isset($join)) {
6243                                 $ja = array("miter" => 0, "round" => 1, "bevel" => 2);
6244                                 if (isset($ja[$join])) {
6245                                         $this->linestyleJoin = $ja[$join]." j";
6246                                         $this->_out($this->linestyleJoin);
6247                                 }
6248                         }
6249                         if (isset($dash)) {
6250                                 $dash_string = "";
6251                                 if ($dash) {
6252                                         if (ereg("^.+,", $dash)) {
6253                                                 $tab = explode(",", $dash);
6254                                         } else {
6255                                                 $tab = array($dash);
6256                                         }
6257                                         $dash_string = "";
6258                                         foreach ($tab as $i => $v) {
6259                                                 if ($i) {
6260                                                         $dash_string .= " ";
6261                                                 }
6262                                                 $dash_string .= sprintf("%.2f", $v);
6263                                         }
6264                                 }
6265                                 if (!isset($phase) OR !$dash) {
6266                                         $phase = 0;
6267                                 }
6268                                 $this->linestyleDash = sprintf("[%s] %.2f d", $dash_string, $phase);
6269                                 $this->_out($this->linestyleDash);
6270                         }
6271                         if (isset($color)) {
6272                                 $this->SetDrawColorArray($color);
6273                         }
6274                 }
6275
6276                 /*
6277                 * Set a draw point.
6278                 * @param float $x Abscissa of point.
6279                 * @param float $y Ordinate of point.
6280                 * @access protected
6281                 * @since 2.1.000 (2008-01-08)
6282                 */
6283                 function _outPoint($x, $y) {
6284                         if ($this->rtl) {
6285                                 $x = $this->w - $x;
6286                         }
6287                         $this->_out(sprintf("%.2f %.2f m", $x * $this->k, ($this->h - $y) * $this->k));
6288                 }
6289
6290                 /*
6291                 * Draws a line from last draw point.
6292                 * @param float $x Abscissa of end point.
6293                 * @param float $y Ordinate of end point.
6294                 * @access protected
6295                 * @since 2.1.000 (2008-01-08)
6296                 */
6297                 function _outLine($x, $y) {
6298                         if ($this->rtl) {
6299                                 $x = $this->w - $x;
6300                         }
6301                         $this->_out(sprintf("%.2f %.2f l", $x * $this->k, ($this->h - $y) * $this->k));
6302                 }
6303
6304                 /**
6305                 * Draws a rectangle.
6306                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6307                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6308                 * @param float $w Width.
6309                 * @param float $h Height.
6310                 * @param string $op options
6311                 * @access protected
6312                 * @since 2.1.000 (2008-01-08)
6313                 */
6314                 function _outRect($x, $y, $w, $h, $op) {
6315                         if ($this->rtl) {
6316                                 $x = $this->w - $x - $w;
6317                         }
6318                         $this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k, $op));
6319                 }
6320
6321                 /*
6322                 * Draws a Bezier curve from last draw point.
6323                 * The Bezier curve is a tangent to the line between the control points at either end of the curve.
6324                 * @param float $x1 Abscissa of control point 1.
6325                 * @param float $y1 Ordinate of control point 1.
6326                 * @param float $x2 Abscissa of control point 2.
6327                 * @param float $y2 Ordinate of control point 2.
6328                 * @param float $x3 Abscissa of end point.
6329                 * @param float $y3 Ordinate of end point.
6330                 * @access protected
6331                 * @since 2.1.000 (2008-01-08)
6332                 */
6333                 function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
6334                         if ($this->rtl) {
6335                                 $x1 = $this->w - $x1;
6336                                 $x2 = $this->w - $x2;
6337                                 $x3 = $this->w - $x3;
6338                         }
6339                         $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));
6340                 }
6341
6342                 /**
6343                 * Draws a line between two points.
6344                 * @param float $x1 Abscissa of first point.
6345                 * @param float $y1 Ordinate of first point.
6346                 * @param float $x2 Abscissa of second point.
6347                 * @param float $y2 Ordinate of second point.
6348                 * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6349                 * @access public
6350                 * @since 1.0
6351                 * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
6352                 */
6353                 function Line($x1, $y1, $x2, $y2, $style=array()) {
6354                         if ($style) {
6355                                 $this->SetLineStyle($style);
6356                         }
6357                         $this->_outPoint($x1, $y1);
6358                         $this->_outLine($x2, $y2);
6359                         $this->_out(" S");
6360                 }
6361
6362                 /**
6363                 * Draws a rectangle.
6364                 * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
6365                 * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
6366                 * @param float $w Width.
6367                 * @param float $h Height.
6368                 * @param string $style Style of rendering. Possible values are:
6369                 * <ul>
6370                 *        <li>D or empty string: Draw (default).</li>
6371                 *        <li>F: Fill.</li>
6372                 *        <li>DF or FD: Draw and fill.</li>
6373                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6374                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6375                 * </ul>
6376                 * @param array $border_style Border style of rectangle. Array with keys among the following:
6377                 * <ul>
6378                 *        <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
6379                 *        <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
6380                 * </ul>
6381                 * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
6382                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6383                 * @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).
6384                 * @access public
6385                 * @since 1.0
6386                 * @see SetLineStyle()
6387                 */
6388                 function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
6389                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6390                                 $this->SetFillColorArray($fill_color);
6391                         }
6392                         switch ($style) {
6393                                 case "F": {
6394                                         $op = 'f';
6395                                         $border_style = array();
6396                                         $this->_outRect($x, $y, $w, $h, $op);
6397                                         break;
6398                                 }
6399                                 case "DF":
6400                                 case "FD": {
6401                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6402                                                 $op = 'B';
6403                                                 if (isset($border_style["all"])) {
6404                                                         $this->SetLineStyle($border_style["all"]);
6405                                                         $border_style = array();
6406                                                 }
6407                                         } else {
6408                                                 $op = 'f';
6409                                         }
6410                                         $this->_outRect($x, $y, $w, $h, $op);
6411                                         break;
6412                                 }
6413                                 case "CNZ": {
6414                                         $op = "W n";
6415                                         break;
6416                                 }
6417                                 case "CEO": {
6418                                         $op = "W* n";
6419                                         break;
6420                                 }
6421                                 default: {
6422                                         $op = 'S';
6423                                         if ((!$border_style) OR (isset($border_style["all"]))) {
6424                                                 if (isset($border_style["all"]) AND $border_style["all"]) {
6425                                                         $this->SetLineStyle($border_style["all"]);
6426                                                         $border_style = array();
6427                                                 }
6428                                                 $this->_outRect($x, $y, $w, $h, $op);
6429                                         }
6430                                         break;
6431                                 }
6432                         }
6433                         if ($border_style) {
6434                                 $border_style2 = array();
6435                                 foreach ($border_style as $line => $value) {
6436                                         $lenght = strlen($line);
6437                                         for ($i = 0; $i < $lenght; $i++) {
6438                                                 $border_style2[$line[$i]] = $value;
6439                                         }
6440                                 }
6441                                 $border_style = $border_style2;
6442                                 if (isset($border_style["L"]) AND $border_style["L"]) {
6443                                         $this->Line($x, $y, $x, $y + $h, $border_style["L"]);
6444                                 }
6445                                 if (isset($border_style["T"]) AND $border_style["T"]) {
6446                                         $this->Line($x, $y, $x + $w, $y, $border_style["T"]);
6447                                 }
6448                                 if (isset($border_style["R"]) AND $border_style["R"]) {
6449                                         $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style["R"]);
6450                                 }
6451                                 if (isset($border_style["B"]) AND $border_style["B"]) {
6452                                         $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style["B"]);
6453                                 }
6454                         }
6455                 }
6456
6457
6458                 /**
6459                 * Draws a Bezier curve.
6460                 * The Bezier curve is a tangent to the line between the control points at
6461                 * either end of the curve.
6462                 * @param float $x0 Abscissa of start point.
6463                 * @param float $y0 Ordinate of start point.
6464                 * @param float $x1 Abscissa of control point 1.
6465                 * @param float $y1 Ordinate of control point 1.
6466                 * @param float $x2 Abscissa of control point 2.
6467                 * @param float $y2 Ordinate of control point 2.
6468                 * @param float $x3 Abscissa of end point.
6469                 * @param float $y3 Ordinate of end point.
6470                 * @param string $style Style of rendering. Possible values are:
6471                 * <ul>
6472                 *        <li>D or empty string: Draw (default).</li>
6473                 *        <li>F: Fill.</li>
6474                 *        <li>DF or FD: Draw and fill.</li>
6475                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6476                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6477                 * </ul>
6478                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6479                 * @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).
6480                 * @access public
6481                 * @see SetLineStyle()
6482                 * @since 2.1.000 (2008-01-08)
6483                 */
6484                 function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style="", $line_style=array(), $fill_color=array()) {
6485                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6486                                 $this->SetFillColorArray($fill_color);
6487                         }
6488                         switch ($style) {
6489                                 case "F": {
6490                                         $op = "f";
6491                                         $line_style = array();
6492                                         break;
6493                                 }
6494                                 case "FD":
6495                                 case "DF": {
6496                                         $op = "B";
6497                                         break;
6498                                 }
6499                                 case "CNZ": {
6500                                         $op = "W n";
6501                                         break;
6502                                 }
6503                                 case "CEO": {
6504                                         $op = "W* n";
6505                                         break;
6506                                 }
6507                                 default: {
6508                                         $op = "S";
6509                                         break;
6510                                 }
6511                         }
6512                         if ($line_style) {
6513                                 $this->SetLineStyle($line_style);
6514                         }
6515                         $this->_outPoint($x0, $y0);
6516                         $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6517                         $this->_out($op);
6518                 }
6519
6520                 /**
6521                 * Draws a poly-Bezier curve.
6522                 * Each Bezier curve segment is a tangent to the line between the control points at
6523                 * either end of the curve.
6524                 * @param float $x0 Abscissa of start point.
6525                 * @param float $y0 Ordinate of start point.
6526                 * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
6527                 * @param string $style Style of rendering. Possible values are:
6528                 * <ul>
6529                 *        <li>D or empty string: Draw (default).</li>
6530                 *        <li>F: Fill.</li>
6531                 *        <li>DF or FD: Draw and fill.</li>
6532                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6533                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6534                 * </ul>
6535                 * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6536                 * @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).
6537                 * @access public
6538                 * @see SetLineStyle()
6539                 * @since 3.0008 (2008-05-12)
6540                 */
6541                 function Polycurve($x0, $y0, $segments, $style="", $line_style=array(), $fill_color=array()) {
6542                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6543                                 $this->SetFillColorArray($fill_color);
6544                         }
6545                         switch ($style) {
6546                                 case "F": {
6547                                         $op = "f";
6548                                         $line_style = array();
6549                                         break;
6550                                 }
6551                                 case "FD":
6552                                 case "DF": {
6553                                         $op = "B";
6554                                         break;
6555                                 }
6556                                 case "CNZ": {
6557                                         $op = "W n";
6558                                         break;
6559                                 }
6560                                 case "CEO": {
6561                                         $op = "W* n";
6562                                         break;
6563                                 }
6564                                 default: {
6565                                         $op = "S";
6566                                         break;
6567                                 }
6568                         }
6569                         if ($line_style) {
6570                                 $this->SetLineStyle($line_style);
6571                         }
6572                         $this->_outPoint($x0, $y0);
6573                         foreach ($segments as $segment) {
6574                                 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
6575                                 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
6576                         }
6577                         $this->_out($op);
6578                 }
6579
6580                 /**
6581                 * Draws an ellipse.
6582                 * An ellipse is formed from n Bezier curves.
6583                 * @param float $x0 Abscissa of center point.
6584                 * @param float $y0 Ordinate of center point.
6585                 * @param float $rx Horizontal radius.
6586                 * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
6587                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6588                 * @param float $astart: Angle start of draw line. Default value: 0.
6589                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6590                 * @param string $style Style of rendering. Possible values are:
6591                 * <ul>
6592                 *        <li>D or empty string: Draw (default).</li>
6593                 *        <li>F: Fill.</li>
6594                 *        <li>DF or FD: Draw and fill.</li>
6595                 *        <li>C: Draw close.</li>
6596                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6597                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6598                 * </ul>
6599                 * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6600                 * @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).
6601                 * @param integer $nc Number of curves used in ellipse. Default value: 8.
6602                 * @access public
6603                 * @since 2.1.000 (2008-01-08)
6604                 */
6605                 function Ellipse($x0, $y0, $rx, $ry=0, $angle=0, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6606                         if ($angle) {
6607                                 $this->StartTransform();
6608                                 $this->Rotate($angle, $x0, $y0);
6609                                 $this->Ellipse($x0, $y0, $rx, $ry, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6610                                 $this->StopTransform();
6611                                 return;
6612                         }
6613                         if ($rx) {
6614                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6615                                         $this->SetFillColorArray($fill_color);
6616                                 }
6617                                 switch ($style) {
6618                                         case "F": {
6619                                                 $op = "f";
6620                                                 $line_style = array();
6621                                                 break;
6622                                         }
6623                                         case "FD":
6624                                         case "DF": {
6625                                                 $op = "B";
6626                                                 break;
6627                                         }
6628                                         case "C": {
6629                                                 $op = "s"; // Small "s" signifies closing the path as well
6630                                                 break;
6631                                         }
6632                                         case "CNZ": {
6633                                                 $op = "W n";
6634                                                 break;
6635                                         }
6636                                         case "CEO": {
6637                                                 $op = "W* n";
6638                                                 break;
6639                                         }
6640                                         default: {
6641                                                 $op = "S";
6642                                                 break;
6643                                         }
6644                                 }
6645                                 if ($line_style) {
6646                                         $this->SetLineStyle($line_style);
6647                                 }
6648                                 if (!$ry) {
6649                                         $ry = $rx;
6650                                 }
6651                                 $rx *= $this->k;
6652                                 $ry *= $this->k;
6653                                 if ($nc < 2){
6654                                         $nc = 2;
6655                                 }
6656                                 $astart = deg2rad((float) $astart);
6657                                 $afinish = deg2rad((float) $afinish);
6658                                 $total_angle = $afinish - $astart;
6659                                 $dt = $total_angle / $nc;
6660                                 $dtm = $dt / 3;
6661                                 $x0 *= $this->k;
6662                                 $y0 = ($this->h - $y0) * $this->k;
6663                                 $t1 = $astart;
6664                                 $a0 = $x0 + ($rx * cos($t1));
6665                                 $b0 = $y0 + ($ry * sin($t1));
6666                                 $c0 = -$rx * sin($t1);
6667                                 $d0 = $ry * cos($t1);
6668                                 $this->_outPoint($a0 / $this->k, $this->h - ($b0 / $this->k));
6669                                 for ($i = 1; $i <= $nc; $i++) {
6670                                         // Draw this bit of the total curve
6671                                         $t1 = ($i * $dt) + $astart;
6672                                         $a1 = $x0 + ($rx * cos($t1));
6673                                         $b1 = $y0 + ($ry * sin($t1));
6674                                         $c1 = -$rx * sin($t1);
6675                                         $d1 = $ry * cos($t1);
6676                                         $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));
6677                                         $a0 = $a1;
6678                                         $b0 = $b1;
6679                                         $c0 = $c1;
6680                                         $d0 = $d1;
6681                                 }
6682                                 $this->_out($op);
6683                         }
6684                 }
6685
6686                 /**
6687                 * Draws a circle.
6688                 * A circle is formed from n Bezier curves.
6689                 * @param float $x0 Abscissa of center point.
6690                 * @param float $y0 Ordinate of center point.
6691                 * @param float $r Radius.
6692                 * @param float $astart: Angle start of draw line. Default value: 0.
6693                 * @param float $afinish: Angle finish of draw line. Default value: 360.
6694                 * @param string $style Style of rendering. Possible values are:
6695                 * <ul>
6696                 *        <li>D or empty string: Draw (default).</li>
6697                 *        <li>F: Fill.</li>
6698                 *        <li>DF or FD: Draw and fill.</li>
6699                 *        <li>C: Draw close.</li>
6700                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6701                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6702                 * </ul>
6703                 * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6704                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6705                 * @param integer $nc Number of curves used in circle. Default value: 8.
6706                 * @access public
6707                 * @since 2.1.000 (2008-01-08)
6708                 */
6709                 function Circle($x0, $y0, $r, $astart=0, $afinish=360, $style="", $line_style=array(), $fill_color=array(), $nc=8) {
6710                         $this->Ellipse($x0, $y0, $r, 0, 0, $astart, $afinish, $style, $line_style, $fill_color, $nc);
6711                 }
6712
6713                 /**
6714                 * Draws a polygon.
6715                 * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
6716                 * @param string $style Style of rendering. Possible values are:
6717                 * <ul>
6718                 *        <li>D or empty string: Draw (default).</li>
6719                 *        <li>F: Fill.</li>
6720                 *        <li>DF or FD: Draw and fill.</li>
6721                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6722                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6723                 * </ul>
6724                 * @param array $line_style Line style of polygon. Array with keys among the following:
6725                 * <ul>
6726                 *        <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
6727                 *        <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
6728                 * </ul>
6729                 * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
6730                 * @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).
6731                 * @access public
6732                 * @since 2.1.000 (2008-01-08)
6733                 */
6734                 function Polygon($p, $style="", $line_style=array(), $fill_color=array()) {
6735                         $np = count($p) / 2;
6736                         if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6737                                 $this->SetFillColorArray($fill_color);
6738                         }
6739                         switch ($style) {
6740                                 case "F": {
6741                                         $line_style = array();
6742                                         $op = "f";
6743                                         break;
6744                                 }
6745                                 case "FD":
6746                                 case "DF": {
6747                                         $op = "B";
6748                                         break;
6749                                 }
6750                                 case "CNZ": {
6751                                         $op = "W n";
6752                                         break;
6753                                 }
6754                                 case "CEO": {
6755                                         $op = "W* n";
6756                                         break;
6757                                 }
6758                                 default: {
6759                                         $op = "S";
6760                                         break;
6761                                 }
6762                         }
6763                         $draw = true;
6764                         if ($line_style) {
6765                                 if (isset($line_style["all"])) {
6766                                         $this->SetLineStyle($line_style["all"]);
6767                                 } else { // 0 .. (np - 1), op = {B, S}
6768                                         $draw = false;
6769                                         if ("B" == $op) {
6770                                                 $op = "f";
6771                                                 $this->_outPoint($p[0], $p[1]);
6772                                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6773                                                         $this->_outLine($p[$i], $p[$i + 1]);
6774                                                 }
6775                                                 $this->_outLine($p[0], $p[1]);
6776                                                 $this->_out($op);
6777                                         }
6778                                         $p[($np * 2)] = $p[0];
6779                                         $p[(($np * 2) + 1)] = $p[1];
6780                                         for ($i = 0; $i < $np; $i++) {
6781                                                 if (isset($line_style[$i]) AND ($line_style[$i] != 0)) {
6782                                                         $this->Line($p[($i * 2)], $p[(($i * 2) + 1)], $p[(($i * 2) + 2)], $p[(($i * 2) + 3)], $line_style[$i]);
6783                                                 }
6784                                         }
6785                                 }
6786                         }
6787                         if ($draw) {
6788                                 $this->_outPoint($p[0], $p[1]);
6789                                 for ($i = 2; $i < ($np * 2); $i = $i + 2) {
6790                                         $this->_outLine($p[$i], $p[$i + 1]);
6791                                 }
6792                                 $this->_outLine($p[0], $p[1]);
6793                                 $this->_out($op);
6794                         }
6795                 }
6796
6797                 /**
6798                 * Draws a regular polygon.
6799                 * @param float $x0 Abscissa of center point.
6800                 * @param float $y0 Ordinate of center point.
6801                 * @param float $r: Radius of inscribed circle.
6802                 * @param integer $ns Number of sides.
6803                 * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
6804                 * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
6805                 * @param string $style Style of rendering. Possible values are:
6806                 * <ul>
6807                 *        <li>D or empty string: Draw (default).</li>
6808                 *        <li>F: Fill.</li>
6809                 *        <li>DF or FD: Draw and fill.</li>
6810                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6811                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6812                 * </ul>
6813                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6814                 * <ul>
6815                 *        <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
6816                 *        <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6817                 * </ul>
6818                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6819                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6820                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6821                 * <ul>
6822                 *        <li>D or empty string: Draw (default).</li>
6823                 *        <li>F: Fill.</li>
6824                 *        <li>DF or FD: Draw and fill.</li>
6825                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6826                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6827                 * </ul>
6828                 * @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).
6829                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6830                 * @access public
6831                 * @since 2.1.000 (2008-01-08)
6832                 */
6833                 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()) {
6834                         if (3 > $ns) {
6835                                 $ns = 3;
6836                         }
6837                         if ($draw_circle) {
6838                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6839                         }
6840                         $p = array();
6841                         for ($i = 0; $i < $ns; $i++) {
6842                                 $a = $angle + ($i * 360 / $ns);
6843                                 $a_rad = deg2rad((float) $a);
6844                                 $p[] = $x0 + ($r * sin($a_rad));
6845                                 $p[] = $y0 + ($r * cos($a_rad));
6846                         }
6847                         $this->Polygon($p, $style, $line_style, $fill_color);
6848                 }
6849
6850                 /**
6851                 * Draws a star polygon
6852                 * @param float $x0 Abscissa of center point.
6853                 * @param float $y0 Ordinate of center point.
6854                 * @param float $r Radius of inscribed circle.
6855                 * @param integer $nv Number of vertices.
6856                 * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
6857                 * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
6858                 * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
6859                 * @param string $style Style of rendering. Possible values are:
6860                 * <ul>
6861                 *        <li>D or empty string: Draw (default).</li>
6862                 *        <li>F: Fill.</li>
6863                 *        <li>DF or FD: Draw and fill.</li>
6864                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6865                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6866                 * </ul>
6867                 * @param array $line_style Line style of polygon sides. Array with keys among the following:
6868                 * <ul>
6869                 *        <li>all: Line style of all sides. Array like for
6870                 * {@link SetLineStyle SetLineStyle}.</li>
6871                 *        <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
6872                 * </ul>
6873                 * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
6874                 * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
6875                 * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
6876                 * <ul>
6877                 *        <li>D or empty string: Draw (default).</li>
6878                 *        <li>F: Fill.</li>
6879                 *        <li>DF or FD: Draw and fill.</li>
6880                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6881                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6882                 * </ul>
6883                 * @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).
6884                 * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
6885                 * @access public
6886                 * @since 2.1.000 (2008-01-08)
6887                 */
6888                 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()) {
6889                         if (2 > $nv) {
6890                                 $nv = 2;
6891                         }
6892                         if ($draw_circle) {
6893                                 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
6894                         }
6895                         $p2 = array();
6896                         $visited = array();
6897                         for ($i = 0; $i < $nv; $i++) {
6898                                 $a = $angle + ($i * 360 / $nv);
6899                                 $a_rad = deg2rad((float) $a);
6900                                 $p2[] = $x0 + ($r * sin($a_rad));
6901                                 $p2[] = $y0 + ($r * cos($a_rad));
6902                                 $visited[] = false;
6903                         }
6904                         $p = array();
6905                         $i = 0;
6906                         do {
6907                                 $p[] = $p2[$i * 2];
6908                                 $p[] = $p2[($i * 2) + 1];
6909                                 $visited[$i] = true;
6910                                 $i += $ng;
6911                                 $i %= $nv;
6912                         } while (!$visited[$i]);
6913                         $this->Polygon($p, $style, $line_style, $fill_color);
6914                 }
6915
6916                 /**
6917                 * Draws a rounded rectangle.
6918                 * @param float $x Abscissa of upper-left corner.
6919                 * @param float $y Ordinate of upper-left corner.
6920                 * @param float $w Width.
6921                 * @param float $h Height.
6922                 * @param float $r Radius of the rounded corners.
6923                 * @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").
6924                 * @param string $style Style of rendering. Possible values are:
6925                 * <ul>
6926                 *        <li>D or empty string: Draw (default).</li>
6927                 *        <li>F: Fill.</li>
6928                 *        <li>DF or FD: Draw and fill.</li>
6929                 *        <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
6930                 *        <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
6931                 * </ul>
6932                 * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
6933                 * @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).
6934                 * @access public
6935                 * @since 2.1.000 (2008-01-08)
6936                 */
6937                 function RoundedRect($x, $y, $w, $h, $r, $round_corner="1111", $style="", $border_style=array(), $fill_color=array()) {
6938                         if ("0000" == $round_corner) { // Not rounded
6939                                 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
6940                         } else { // Rounded
6941                                 if (!(false === strpos($style, "F")) AND isset($fill_color)) {
6942                                         $this->SetFillColorArray($fill_color);
6943                                 }
6944                                 switch ($style) {
6945                                         case "F": {
6946                                                 $border_style = array();
6947                                                 $op = "f";
6948                                                 break;
6949                                         }
6950                                         case "FD":
6951                                         case "DF": {
6952                                                 $op = "B";
6953                                                 break;
6954                                         }
6955                                         case "CNZ": {
6956                                                 $op = "W n";
6957                                                 break;
6958                                         }
6959                                         case "CEO": {
6960                                                 $op = "W* n";
6961                                                 break;
6962                                         }
6963                                         default: {
6964                                                 $op = "S";
6965                                                 break;
6966                                         }
6967                                 }
6968                                 if ($border_style) {
6969                                         $this->SetLineStyle($border_style);
6970                                 }
6971                                 $MyArc = 4 / 3 * (sqrt(2) - 1);
6972                                 $this->_outPoint($x + $r, $y);
6973                                 $xc = $x + $w - $r;
6974                                 $yc = $y + $r;
6975                                 $this->_outLine($xc, $y);
6976                                 if ($round_corner[0]) {
6977                                         $this->_outCurve($xc + ($r * $MyArc), $yc - $r, $xc + $r, $yc - ($r * $MyArc), $xc + $r, $yc);
6978                                 } else {
6979                                         $this->_outLine($x + $w, $y);
6980                                 }
6981                                 $xc = $x + $w - $r;
6982                                 $yc = $y + $h - $r;
6983                                 $this->_outLine($x + $w, $yc);
6984                                 if ($round_corner[1]) {
6985                                         $this->_outCurve($xc + $r, $yc + ($r * $MyArc), $xc + ($r * $MyArc), $yc + $r, $xc, $yc + $r);
6986                                 } else {
6987                                         $this->_outLine($x + $w, $y + $h);
6988                                 }
6989                                 $xc = $x + $r;
6990                                 $yc = $y + $h - $r;
6991                                 $this->_outLine($xc, $y + $h);
6992                                 if ($round_corner[2]) {
6993                                         $this->_outCurve($xc - ($r * $MyArc), $yc + $r, $xc - $r, $yc + ($r * $MyArc), $xc - $r, $yc);
6994                                 } else {
6995                                         $this->_outLine($x, $y + $h);
6996                                 }
6997                                 $xc = $x + $r;
6998                                 $yc = $y + $r;
6999                                 $this->_outLine($x, $yc);
7000                                 if ($round_corner[3]) {
7001                                         $this->_outCurve($xc - $r, $yc - ($r * $MyArc), $xc - ($r * $MyArc), $yc - $r, $xc, $yc - $r);
7002                                 } else {
7003                                         $this->_outLine($x, $y);
7004                                         $this->_outLine($x + $r, $y);
7005                                 }
7006                                 $this->_out($op);
7007                         }
7008                 }
7009
7010                 // END GRAPHIC FUNCTIONS SECTION -----------------------
7011
7012                 // BIDIRECTIONAL TEXT SECTION --------------------------
7013                 /**
7014                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7015                  * @param string $str string to manipulate.
7016                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7017                  * @return string
7018                  * @author Nicola Asuni
7019                  * @since 2.1.000 (2008-01-08)
7020                 */
7021                 function utf8StrRev($str, $setbom=false, $forcertl=false) {
7022                         return $this->arrUTF8ToUTF16BE($this->utf8Bidi($this->UTF8StringToArray($str), $forcertl), $setbom);
7023                 }
7024
7025                 /**
7026                  * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
7027                  * @param array $ta array of characters composing the string.
7028                  * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
7029                  * @return string
7030                  * @author Nicola Asuni
7031                  * @since 2.4.000 (2008-03-06)
7032                 */
7033                 function utf8Bidi($ta, $forcertl=false) {
7034                         global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
7035                         // paragraph embedding level
7036                         $pel = 0;
7037                         // max level
7038                         $maxlevel = 0;
7039                         // create string from array
7040                         $str = $this->UTF8ArrSubString($ta);
7041                         // check if string contains arabic text
7042                         if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
7043                                 $arabic = true;
7044                         } else {
7045                                 $arabic = false;
7046                         }
7047                         // check if string contains RTL text
7048                         if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
7049                                 return $ta;
7050                         }
7051                         
7052                         // get number of chars
7053                         $numchars = count($ta);
7054                         
7055                         if ($forcertl == 'R') {
7056                                         $pel = 1;
7057                         } elseif ($forcertl == 'L') {
7058                                         $pel = 0;
7059                         } else {
7060                                 // P2. In each paragraph, find the first character of type L, AL, or R.
7061                                 // 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.
7062                                 for ($i=0; $i < $numchars; $i++) {
7063                                         $type = $unicode[$ta[$i]];
7064                                         if ($type == 'L') {
7065                                                 $pel = 0;
7066                                                 break;
7067                                         } elseif (($type == 'AL') OR ($type == 'R')) {
7068                                                 $pel = 1;
7069                                                 break;
7070                                         }
7071                                 }
7072                         }
7073                         
7074                         // Current Embedding Level
7075                         $cel = $pel;
7076                         // directional override status
7077                         $dos = 'N';
7078                         $remember = array();
7079                         // start-of-level-run
7080                         $sor = $pel % 2 ? 'R' : 'L';
7081                         $eor = $sor;
7082
7083                         //$levels = array(array('level' => $cel, 'sor' => $sor, 'eor' => '', 'chars' => array()));
7084                         //$current_level = &$levels[count( $levels )-1];
7085
7086                         // Array of characters data
7087                         $chardata = Array();
7088
7089                         // 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.
7090                         //      In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
7091                         for ($i=0; $i < $numchars; $i++) {
7092                                 if ($ta[$i] == K_RLE) {
7093                                         // X2. With each RLE, compute the least greater odd embedding level.
7094                                         //      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.
7095                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7096                                         $next_level = $cel + ($cel % 2) + 1;
7097                                         if ($next_level < 62) {
7098                                                 $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
7099                                                 $cel = $next_level;
7100                                                 $dos = 'N';
7101                                                 $sor = $eor;
7102                                                 $eor = $cel % 2 ? 'R' : 'L';
7103                                         }
7104                                 } elseif ($ta[$i] == K_LRE) {
7105                                         // X3. With each LRE, compute the least greater even embedding level.
7106                                         //      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.
7107                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7108                                         $next_level = $cel + 2 - ($cel % 2);
7109                                         if ( $next_level < 62 ) {
7110                                                 $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
7111                                                 $cel = $next_level;
7112                                                 $dos = 'N';
7113                                                 $sor = $eor;
7114                                                 $eor = $cel % 2 ? 'R' : 'L';
7115                                         }
7116                                 } elseif ($ta[$i] == K_RLO) {
7117                                         // X4. With each RLO, compute the least greater odd embedding level.
7118                                         //      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.
7119                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7120                                         $next_level = $cel + ($cel % 2) + 1;
7121                                         if ($next_level < 62) {
7122                                                 $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
7123                                                 $cel = $next_level;
7124                                                 $dos = 'R';
7125                                                 $sor = $eor;
7126                                                 $eor = $cel % 2 ? 'R' : 'L';
7127                                         }
7128                                 } elseif ($ta[$i] == K_LRO) {
7129                                         // X5. With each LRO, compute the least greater even embedding level.
7130                                         //      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.
7131                                         //      b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
7132                                         $next_level = $cel + 2 - ($cel % 2);
7133                                         if ( $next_level < 62 ) {
7134                                                 $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
7135                                                 $cel = $next_level;
7136                                                 $dos = 'L';
7137                                                 $sor = $eor;
7138                                                 $eor = $cel % 2 ? 'R' : 'L';
7139                                         }
7140                                 } elseif ($ta[$i] == K_PDF) {
7141                                         // 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.
7142                                         if (count($remember)) {
7143                                                 $last = count($remember ) - 1;
7144                                                 if (($remember[$last]['num'] == K_RLE) OR
7145                                                           ($remember[$last]['num'] == K_LRE) OR
7146                                                           ($remember[$last]['num'] == K_RLO) OR
7147                                                           ($remember[$last]['num'] == K_LRO)) {
7148                                                         $match = array_pop($remember);
7149                                                         $cel = $match['cel'];
7150                                                         $dos = $match['dos'];
7151                                                         $sor = $eor;
7152                                                         $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
7153                                                 }
7154                                         }
7155                                 } elseif (($ta[$i] != K_RLE) AND
7156                                                                  ($ta[$i] != K_LRE) AND
7157                                                                  ($ta[$i] != K_RLO) AND
7158                                                                  ($ta[$i] != K_LRO) AND
7159                                                                  ($ta[$i] != K_PDF)) {
7160                                         // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
7161                                         //      a. Set the level of the current character to the current embedding level.
7162                                         //      b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
7163                                         if ($dos != 'N') {
7164                                                 $chardir = $dos;
7165                                         } else {
7166                                                 $chardir = $unicode[$ta[$i]];
7167                                         }
7168                                         // stores string characters and other information
7169                                         $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
7170                                 }
7171                         } // end for each char
7172
7173                         // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
7174                         // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
7175                         // X10. The remaining rules are applied to each run of characters at the same level. For each run, determine the start-of-level-run (sor) and end-of-level-run (eor) type, either L or R. This depends on the higher of the two levels on either side of the boundary (at the start or end of the paragraph, the level of the 'other' run is the base embedding level). If the higher level is odd, the type is R; otherwise, it is L.
7176
7177                         // 3.3.3 Resolving Weak Types
7178                         // 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.
7179                         // Nonspacing marks are now resolved based on the previous characters.
7180                         $numchars = count($chardata);
7181
7182                         // 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.
7183                         $prevlevel = -1; // track level changes
7184                         $levcount = 0; // counts consecutive chars at the same level
7185                         for ($i=0; $i < $numchars; $i++) {
7186                                 if ($chardata[$i]['type'] == 'NSM') {
7187                                         if ($levcount) {
7188                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7189                                         } elseif ($i > 0) {
7190                                                 $chardata[$i]['type'] = $chardata[($i-1)]['type'];
7191                                         }
7192                                 }
7193                                 if ($chardata[$i]['level'] != $prevlevel) {
7194                                         $levcount = 0;
7195                                 } else {
7196                                         $levcount++;
7197                                 }
7198                                 $prevlevel = $chardata[$i]['level'];
7199                         }
7200
7201                         // 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.
7202                         $prevlevel = -1;
7203                         $levcount = 0;
7204                         for ($i=0; $i < $numchars; $i++) {
7205                                 if ($chardata[$i]['char'] == 'EN') {
7206                                         for ($j=$levcount; $j >= 0; $j--) {
7207                                                 if ($chardata[$j]['type'] == 'AL') {
7208                                                         $chardata[$i]['type'] = 'AN';
7209                                                 } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
7210                                                         break;
7211                                                 }
7212                                         }
7213                                 }
7214                                 if ($chardata[$i]['level'] != $prevlevel) {
7215                                         $levcount = 0;
7216                                 } else {
7217                                         $levcount++;
7218                                 }
7219                                 $prevlevel = $chardata[$i]['level'];
7220                         }
7221
7222                         // W3. Change all ALs to R.
7223                         for ($i=0; $i < $numchars; $i++) {
7224                                 if ($chardata[$i]['type'] == 'AL') {
7225                                         $chardata[$i]['type'] = 'R';
7226                                 }
7227                         }
7228
7229                         // 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.
7230                         $prevlevel = -1;
7231                         $levcount = 0;
7232                         for ($i=0; $i < $numchars; $i++) {
7233                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7234                                         if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7235                                                 $chardata[$i]['type'] = 'EN';
7236                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
7237                                                 $chardata[$i]['type'] = 'EN';
7238                                         } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
7239                                                 $chardata[$i]['type'] = 'AN';
7240                                         }
7241                                 }
7242                                 if ($chardata[$i]['level'] != $prevlevel) {
7243                                         $levcount = 0;
7244                                 } else {
7245                                         $levcount++;
7246                                 }
7247                                 $prevlevel = $chardata[$i]['level'];
7248                         }
7249
7250                         // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
7251                         $prevlevel = -1;
7252                         $levcount = 0;
7253                         for ($i=0; $i < $numchars; $i++) {
7254                                 if ($chardata[$i]['type'] == 'ET') {
7255                                         if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
7256                                                 $chardata[$i]['type'] = 'EN';
7257                                         } else {
7258                                                 $j = $i+1;
7259                                                 while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
7260                                                         if ($chardata[$j]['type'] == 'EN') {
7261                                                                 $chardata[$i]['type'] = 'EN';
7262                                                                 break;
7263                                                         } elseif ($chardata[$j]['type'] != 'ET') {
7264                                                                 break;
7265                                                         }
7266                                                         $j++;
7267                                                 }
7268                                         }
7269                                 }
7270                                 if ($chardata[$i]['level'] != $prevlevel) {
7271                                         $levcount = 0;
7272                                 } else {
7273                                         $levcount++;
7274                                 }
7275                                 $prevlevel = $chardata[$i]['level'];
7276                         }
7277
7278                         // W6. Otherwise, separators and terminators change to Other Neutral.
7279                         $prevlevel = -1;
7280                         $levcount = 0;
7281                         for ($i=0; $i < $numchars; $i++) {
7282                                 if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
7283                                         $chardata[$i]['type'] = 'ON';
7284                                 }
7285                                 if ($chardata[$i]['level'] != $prevlevel) {
7286                                         $levcount = 0;
7287                                 } else {
7288                                         $levcount++;
7289                                 }
7290                                 $prevlevel = $chardata[$i]['level'];
7291                         }
7292
7293                         //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.
7294                         $prevlevel = -1;
7295                         $levcount = 0;
7296                         for ($i=0; $i < $numchars; $i++) {
7297                                 if ($chardata[$i]['char'] == 'EN') {
7298                                         for ($j=$levcount; $j >= 0; $j--) {
7299                                                 if ($chardata[$j]['type'] == 'L') {
7300                                                         $chardata[$i]['type'] = 'L';
7301                                                 } elseif ($chardata[$j]['type'] == 'R') {
7302                                                         break;
7303                                                 }
7304                                         }
7305                                 }
7306                                 if ($chardata[$i]['level'] != $prevlevel) {
7307                                         $levcount = 0;
7308                                 } else {
7309                                         $levcount++;
7310                                 }
7311                                 $prevlevel = $chardata[$i]['level'];
7312                         }
7313
7314                         // 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.
7315                         $prevlevel = -1;
7316                         $levcount = 0;
7317                         for ($i=0; $i < $numchars; $i++) {
7318                                 if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7319                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7320                                                 $chardata[$i]['type'] = 'L';
7321                                         } elseif (($chardata[$i]['type'] == 'N') AND
7322                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7323                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7324                                                 $chardata[$i]['type'] = 'R';
7325                                         } elseif ($chardata[$i]['type'] == 'N') {
7326                                                 // N2. Any remaining neutrals take the embedding direction
7327                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7328                                         }
7329                                 } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
7330                                         // first char
7331                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
7332                                                 $chardata[$i]['type'] = 'L';
7333                                         } elseif (($chardata[$i]['type'] == 'N') AND
7334                                          (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
7335                                          (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
7336                                                 $chardata[$i]['type'] = 'R';
7337                                         } elseif ($chardata[$i]['type'] == 'N') {
7338                                                 // N2. Any remaining neutrals take the embedding direction
7339                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7340                                         }
7341                                 } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
7342                                         //last char
7343                                         if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
7344                                                 $chardata[$i]['type'] = 'L';
7345                                         } elseif (($chardata[$i]['type'] == 'N') AND
7346                                          (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
7347                                          (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
7348                                                 $chardata[$i]['type'] = 'R';
7349                                         } elseif ($chardata[$i]['type'] == 'N') {
7350                                                 // N2. Any remaining neutrals take the embedding direction
7351                                                 $chardata[$i]['type'] = $chardata[$i]['sor'];
7352                                         }
7353                                 } elseif ($chardata[$i]['type'] == 'N') {
7354                                         // N2. Any remaining neutrals take the embedding direction
7355                                         $chardata[$i]['type'] = $chardata[$i]['sor'];
7356                                 }
7357                                 if ($chardata[$i]['level'] != $prevlevel) {
7358                                         $levcount = 0;
7359                                 } else {
7360                                         $levcount++;
7361                                 }
7362                                 $prevlevel = $chardata[$i]['level'];
7363                         }
7364
7365                         // 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.
7366                         // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
7367                         for ($i=0; $i < $numchars; $i++) {
7368                                 $odd = $chardata[$i]['level'] % 2;
7369                                 if ($odd) {
7370                                         if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7371                                                 $chardata[$i]['level'] += 1;
7372                                         }
7373                                 } else {
7374                                         if ($chardata[$i]['type'] == 'R') {
7375                                                 $chardata[$i]['level'] += 1;
7376                                         } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')){
7377                                                 $chardata[$i]['level'] += 2;
7378                                         }
7379                                 }
7380                                 $maxlevel = max($chardata[$i]['level'],$maxlevel);
7381                         }
7382
7383                         // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
7384                         //      1. Segment separators,
7385                         //      2. Paragraph separators,
7386                         //      3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
7387                         //      4. Any sequence of white space characters at the end of the line.
7388                         for ($i=0; $i < $numchars; $i++) {
7389                                 if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
7390                                         $chardata[$i]['level'] = $pel;
7391                                 } elseif ($chardata[$i]['type'] == 'WS') {
7392                                         $j = $i+1;
7393                                         while ($j < $numchars) {
7394                                                 if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
7395                                                         (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
7396                                                         $chardata[$i]['level'] = $pel;
7397                                                         break;
7398                                                 } elseif ($chardata[$j]['type'] != 'WS') {
7399                                                         break;
7400                                                 }
7401                                                 $j++;
7402                                         }
7403                                 }
7404                         }
7405
7406                         // Arabic Shaping
7407                         // 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.
7408                         if ($arabic) {
7409                                 $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
7410                                 $alfletter = array(1570,1571,1573,1575);
7411                                 $chardata2 = $chardata;
7412                                 $laaletter = false;
7413                                 $charAL = array();
7414                                 $x = 0;
7415                                 for ($i=0; $i < $numchars; $i++) {
7416                                         if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
7417                                                 $charAL[$x] = $chardata[$i];
7418                                                 $charAL[$x]['i'] = $i;
7419                                                 $chardata[$i]['x'] = $x;
7420                                                 $x++;
7421                                         }
7422                                 }
7423                                 $numAL = $x;
7424                                 for ($i=0; $i < $numchars; $i++) {
7425                                         $thischar = $chardata[$i];
7426                                         if ($i > 0) {
7427                                                 $prevchar = $chardata[($i-1)];
7428                                         } else {
7429                                                 $prevchar = false;
7430                                         }
7431                                         if (($i+1) < $numchars) {
7432                                                 $nextchar = $chardata[($i+1)];
7433                                         } else {
7434                                                 $nextchar = false;
7435                                         }
7436                                         if ($unicode[$thischar['char']] == 'AL') {
7437                                                 $x = $thischar['x'];
7438                                                 if ($x > 0) {
7439                                                         $prevchar = $charAL[($x-1)];
7440                                                 } else {
7441                                                         $prevchar = false;
7442                                                 }
7443                                                 if (($x+1) < $numAL) {
7444                                                         $nextchar = $charAL[($x+1)];
7445                                                 } else {
7446                                                         $nextchar = false;
7447                                                 }
7448                                                 // if laa letter
7449                                                 if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
7450                                                         $arabicarr = $laa_array;
7451                                                         $laaletter = true;
7452                                                         if ($x > 1) {
7453                                                                 $prevchar = $charAL[($x-2)];
7454                                                         } else {
7455                                                                 $prevchar = false;
7456                                                         }
7457                                                 } else {
7458                                                         $arabicarr = $unicode_arlet;
7459                                                         $laaletter = false;
7460                                                 }
7461                                                 if (($prevchar !== false) AND ($nextchar !== false) AND
7462                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7463                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7464                                                         ($prevchar['type'] == $thischar['type']) AND
7465                                                         ($nextchar['type'] == $thischar['type']) AND
7466                                                         ($nextchar['char'] != 1567)) {
7467                                                         if (in_array($prevchar['char'], $endedletter)) {
7468                                                                 if (isset($arabicarr[$thischar['char']][2])) {
7469                                                                         // initial
7470                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7471                                                                 }
7472                                                         } else {
7473                                                                 if (isset($arabicarr[$thischar['char']][3])) {
7474                                                                         // medial
7475                                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
7476                                                                 }
7477                                                         }
7478                                                 } elseif (($nextchar !== false) AND
7479                                                         (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
7480                                                         ($nextchar['type'] == $thischar['type']) AND
7481                                                         ($nextchar['char'] != 1567)) {
7482                                                         if (isset($arabicarr[$chardata[$i]['char']][2])) {
7483                                                                 // initial
7484                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
7485                                                         }
7486                                                 } elseif ((($prevchar !== false) AND
7487                                                         (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
7488                                                         ($prevchar['type'] == $thischar['type'])) OR
7489                                                         (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
7490                                                         // final
7491                                                         if (($i > 1) AND ($thischar['char'] == 1607) AND
7492                                                                 ($chardata[$i-1]['char'] == 1604) AND
7493                                                                 ($chardata[$i-2]['char'] == 1604)) {
7494                                                                 //Allah Word
7495                                                                 // mark characters to delete with false
7496                                                                 $chardata2[$i-2]['char'] = false;
7497                                                                 $chardata2[$i-1]['char'] = false;
7498                                                                 $chardata2[$i]['char'] = 65010;
7499                                                         } else {
7500                                                                 if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
7501                                                                         if (isset($arabicarr[$thischar['char']][0])) {
7502                                                                                 // isolated
7503                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7504                                                                         }
7505                                                                 } else {
7506                                                                         if (isset($arabicarr[$thischar['char']][1])) {
7507                                                                                 // final
7508                                                                                 $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
7509                                                                         }
7510                                                                 }
7511                                                         }
7512                                                 } elseif (isset($arabicarr[$thischar['char']][0])) {
7513                                                         // isolated
7514                                                         $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
7515                                                 }
7516                                                 // if laa letter
7517                                                 if ($laaletter) {
7518                                                         // mark characters to delete with false
7519                                                         $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
7520                                                 }
7521                                         } // end if AL (Arabic Letter)
7522                                 } // end for each char
7523                                 /*
7524                                  * Combining characters that can occur with Shadda (0651 HEX, 1617 DEC) are placed in UE586-UE594.
7525                                  * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
7526                                  */
7527                                 $cw = &$this->CurrentFont['cw'];
7528                                 for ($i=0; $i < ($numchars-1); $i++) {
7529                                         if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
7530                                                 // check if the subtitution font is defined on current font
7531                                                 if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
7532                                                         $chardata2[$i]['char'] = false;
7533                                                         $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
7534                                                 }
7535                                         }
7536                                 }
7537                                 // remove marked characters
7538                                 foreach($chardata2 as $key => $value) {
7539                                         if ($value['char'] === false) {
7540                                                 unset($chardata2[$key]);
7541                                         }
7542                                 }
7543                                 $chardata = array_values($chardata2);
7544                                 $numchars = count($chardata);
7545                                 unset($chardata2);
7546                                 unset($arabicarr);
7547                                 unset($laaletter);
7548                                 unset($charAL);
7549                         }
7550
7551                         // 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.
7552                         for ($j=$maxlevel; $j > 0; $j--) {
7553                                 $ordarray = Array();
7554                                 $revarr = Array();
7555                                 $onlevel = false;
7556                                 for ($i=0; $i < $numchars; $i++) {
7557                                         if ($chardata[$i]['level'] >= $j) {
7558                                                 $onlevel = true;
7559                                                 if (isset($unicode_mirror[$chardata[$i]['char']])) {
7560                                                         // 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.
7561                                                         $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
7562                                                 }
7563                                                 $revarr[] = $chardata[$i];
7564                                         } else {
7565                                                 if ($onlevel) {
7566                                                         $revarr = array_reverse($revarr);
7567                                                         $ordarray = array_merge($ordarray, $revarr);
7568                                                         $revarr = Array();
7569                                                         $onlevel = false;
7570                                                 }
7571                                                 $ordarray[] = $chardata[$i];
7572                                         }
7573                                 }
7574                                 if ($onlevel) {
7575                                         $revarr = array_reverse($revarr);
7576                                         $ordarray = array_merge($ordarray, $revarr);
7577                                 }
7578                                 $chardata = $ordarray;
7579                         }
7580
7581                         $ordarray = array();
7582                         for ($i=0; $i < $numchars; $i++) {
7583                                 $ordarray[] = $chardata[$i]['char'];
7584                         }
7585
7586                         return $ordarray;
7587                 }
7588
7589                 // END OF BIDIRECTIONAL TEXT SECTION -------------------
7590
7591                 /*
7592                 * Adds a bookmark.
7593                 * @param string $txt bookmark description.
7594                 * @param int $level bookmark level (minimum value is 0).
7595                 * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
7596                 * @access public
7597                 * @author Olivier Plathey, Nicola Asuni
7598                 * @since 2.1.002 (2008-02-12)
7599                 */
7600                 function Bookmark($txt, $level=0, $y=-1) {
7601                         if ($level < 0) {
7602                                 $level = 0;
7603                         }
7604                         if (isset($this->outlines[0])) {
7605                                 $lastoutline = end($this->outlines);
7606                                 $maxlevel = $lastoutline['l'] + 1;
7607                         } else {
7608                                 $maxlevel = 0;
7609                         }
7610                         if ($level > $maxlevel) {
7611                                 $level = $maxlevel;
7612                         }
7613                         if ($y == -1) {
7614                                 $y = $this->GetY();
7615                         }
7616                         $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $this->PageNo());
7617                 }
7618
7619                 /*
7620                 * Create a bookmark PDF string.
7621                 * @access protected
7622                 * @author Olivier Plathey, Nicola Asuni
7623                 * @since 2.1.002 (2008-02-12)
7624                 */
7625                 function _putbookmarks() {
7626                         $nb = count($this->outlines);
7627                         if ($nb == 0) {
7628                                 return;
7629                         }
7630                         $lru = array();
7631                         $level = 0;
7632                         foreach($this->outlines as $i => $o) {
7633                                 if ($o['l'] > 0) {
7634                                         $parent = $lru[($o['l'] - 1)];
7635                                         //Set parent and last pointers
7636                                         $this->outlines[$i]['parent'] = $parent;
7637                                         $this->outlines[$parent]['last'] = $i;
7638                                         if ($o['l'] > $level) {
7639                                                 //Level increasing: set first pointer
7640                                                 $this->outlines[$parent]['first'] = $i;
7641                                         }
7642                                 } else {
7643                                         $this->outlines[$i]['parent'] = $nb;
7644                                 }
7645                                 if (($o['l'] <= $level) AND ($i > 0)) {
7646                                         //Set prev and next pointers
7647                                         $prev = $lru[$o['l']];
7648                                         $this->outlines[$prev]['next'] = $i;
7649                                         $this->outlines[$i]['prev'] = $prev;
7650                                 }
7651                                 $lru[$o['l']] = $i;
7652                                 $level = $o['l'];
7653                         }
7654                         //Outline items
7655                         $n = $this->n + 1;
7656                         foreach($this->outlines as $i => $o) {
7657                                 $this->_newobj();
7658                                 $this->_out('<</Title '.$this->_textstring($o['t']));
7659                                 $this->_out('/Parent '.($n+$o['parent']).' 0 R');
7660                                 if (isset($o['prev']))
7661                                 $this->_out('/Prev '.($n+$o['prev']).' 0 R');
7662                                 if (isset($o['next']))
7663                                 $this->_out('/Next '.($n+$o['next']).' 0 R');
7664                                 if (isset($o['first']))
7665                                 $this->_out('/First '.($n+$o['first']).' 0 R');
7666                                 if (isset($o['last']))
7667                                 $this->_out('/Last '.($n+$o['last']).' 0 R');
7668                                 $this->_out(sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]', 1+2*$o['p'], ($this->h-$o['y'])*$this->k));
7669                                 $this->_out('/Count 0>>');
7670                                 $this->_out('endobj');
7671                         }
7672                         //Outline root
7673                         $this->_newobj();
7674                         $this->OutlineRoot=$this->n;
7675                         $this->_out('<</Type /Outlines /First '.$n.' 0 R');
7676                         $this->_out('/Last '.($n+$lru[0]).' 0 R>>');
7677                         $this->_out('endobj');
7678                 }
7679
7680
7681                 // --- JAVASCRIPT - FORMS ------------------------------
7682
7683                 /*
7684                 * Adds a javascript
7685                 * @access public
7686                 * @author Johannes Güntert, Nicola Asuni
7687                 * @since 2.1.002 (2008-02-12)
7688                 */
7689                 function IncludeJS($script) {
7690                         $this->javascript .= $script;
7691                 }
7692
7693                 /*
7694                 * Create a javascript PDF string.
7695                 * @access protected
7696                 * @author Johannes Güntert, Nicola Asuni
7697                 * @since 2.1.002 (2008-02-12)
7698                 */
7699                 function _putjavascript() {
7700                         if (empty($this->javascript)) {
7701                                 return;
7702                         }
7703                         // the following two lines are uded to avoid form fields duplication after saving
7704                         $js1 = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2f,%.2f,%.2f,%.2f]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
7705                         $js2 = "getField('tcpdfdocsaved').value = 'saved';";
7706                         $this->_newobj();
7707                         $this->n_js = $this->n;
7708                         $this->_out('<<');
7709                         $this->_out('/Names [(EmbeddedJS) '.($this->n + 1).' 0 R ]');
7710                         $this->_out('>>');
7711                         $this->_out('endobj');
7712                         $this->_newobj();
7713                         $this->_out('<<');
7714                         $this->_out('/S /JavaScript');
7715                         $this->_out('/JS '.$this->_textstring($js1."\n".$this->javascript."\n".$js2));
7716                         $this->_out('>>');
7717                         $this->_out('endobj');
7718                 }
7719
7720                 /*
7721                 * Convert color to javascript color.
7722                 * @param string $color color name or #RRGGBB
7723                 * @access protected
7724                 * @author Denis Van Nuffelen, Nicola Asuni
7725                 * @since 2.1.002 (2008-02-12)
7726                 */
7727                 function _JScolor($color) {
7728                         static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
7729                         if (substr($color,0,1) == '#') {
7730                                 return sprintf("['RGB',%.3f,%.3f,%.3f]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
7731                         }
7732                         if (!in_array($color,$aColors)) {
7733                                 $this->Error('Invalid color: '.$color);
7734                         }
7735                         return 'color.'.$color;
7736                 }
7737
7738                 /*
7739                 * Adds a javascript form field.
7740                 * @param string $type field type
7741                 * @param string $name field name
7742                 * @param int $x horizontal position
7743                 * @param int $y vertical position
7744                 * @param int $w width
7745                 * @param int $h height
7746                 * @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>
7747                 * @access protected
7748                 * @author Denis Van Nuffelen, Nicola Asuni
7749                 * @since 2.1.002 (2008-02-12)
7750                 */
7751                 function _addfield($type, $name, $x, $y, $w, $h, $prop) {
7752                         // the followind avoid fields duplication after saving the document
7753                         $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
7754                         $k = $this->k;
7755                         $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";
7756                         $this->javascript .= "f".$name.".textSize=".$this->FontSizePt.";\n";
7757                         while (list($key, $val) = each($prop)) {
7758                                 if (strcmp(substr($key,-5),"Color") == 0) {
7759                                         $val = $this->_JScolor($val);
7760                                 } else {
7761                                         $val = "'".$val."'";
7762                                 }
7763                                 $this->javascript .= "f".$name.".".$key."=".$val.";\n";
7764                         }
7765                         $this->x += $w;
7766                         $this->javascript .= "}";
7767                 }
7768
7769                 /*
7770                 * Creates a text field
7771                 * @param string $name field name
7772                 * @param int $w width
7773                 * @param int $h height
7774                 * @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>
7775                 * @access public
7776                 * @author Denis Van Nuffelen, Nicola Asuni
7777                 * @since 2.1.002 (2008-02-12)
7778                 */
7779                 function TextField($name, $w, $h, $prop=array()) {
7780                         $this->_addfield('text', $name, $this->x, $this->y, $w, $h, $prop);
7781                 }
7782
7783                 /*
7784                 * Creates a RadioButton field
7785                 * @param string $name field name
7786                 * @param int $w width
7787                 * @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>
7788                 * @access public
7789                 * @author Nicola Asuni
7790                 * @since 2.2.003 (2008-03-03)
7791                 */
7792                 function RadioButton($name, $w, $prop=array()) {
7793                         if (!isset($prop['strokeColor'])) {
7794                                 $prop['strokeColor']='black';
7795                         }
7796                         $this->_addfield('radiobutton', $name, $this->x, $this->y, $w, $w, $prop);
7797                 }
7798
7799                 /*
7800                 * Creates a List-box field
7801                 * @param string $name field name
7802                 * @param int $w width
7803                 * @param int $h height
7804                 * @param array $values array containing the list of values.
7805                 * @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>
7806                 * @access public
7807                 * @author Nicola Asuni
7808                 * @since 2.2.003 (2008-03-03)
7809                 */
7810                 function ListBox($name, $w, $h, $values, $prop=array()) {
7811                         if (!isset($prop['strokeColor'])) {
7812                                 $prop['strokeColor'] = 'ltGray';
7813                         }
7814                         $this->_addfield('listbox', $name, $this->x, $this->y, $w, $h, $prop);
7815                         $s = '';
7816                         foreach($values as $value) {
7817                                 $s .= "'".addslashes($value)."',";
7818                         }
7819                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7820                 }
7821
7822                 /*
7823                 * Creates a Combo-box field
7824                 * @param string $name field name
7825                 * @param int $w width
7826                 * @param int $h height
7827                 * @param array $values array containing the list of values.
7828                 * @param string $prop properties. Possible values are (http://www.adobe.com/devnet/acrobat/pdfs/js_developer_guide.pdf): <ul><li>rect: Position and size of field on page.</li><li>borderStyle: Rectangle border appearance.</li><li>strokeColor: Color of bounding rectangle.</li><li>lineWidth: Width of the edge of the surrounding rectangle.</li><li>rotation: Rotation of field in 90-degree increments.</li><li>fillColor: Background color of field (gray, transparent, RGB, or CMYK).</li><li>userName: Short description of field that appears on mouse-over.</li><li>readonly: Whether the user may change the field contents.</li><li>doNotScroll: Whether text fields may scroll.</li><li>display: Whether visible or hidden on screen or in print.</li><li>textFont: Text font.</li><li>textColor: Text color.</li><li>textSize: Text size.</li><li>richText: Rich text.</li><li>richValue: Text.</li><li>comb: Text comb format.</li><li>multiline: Text multiline.</li><li>charLimit: Text limit to number of characters.</li><li>fileSelect: Text file selection format.</li><li>password: Text password format.</li><li>alignment: Text layout in text fields.</li><li>buttonAlignX: X alignment of icon on button face.</li><li>buttonAlignY: Y alignment of icon on button face.</li><li>buttonFitBounds: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleHow: Relative scaling of an icon to fit inside a button face.</li><li>buttonScaleWhen: Relative scaling of an icon to fit inside a button face.</li><li>highlight: Appearance of a button when pushed.</li><li>style: Glyph style for checkbox and radio buttons.</li><li>numItems: Number of items in a combo box or list box.</li><li>editable: Whether the user can type in a combo box.</li><li>multipleSelection: Whether multiple list box items may be selected.</li></ul>
7829                 * @access public
7830                 * @author Denis Van Nuffelen, Nicola Asuni
7831                 * @since 2.1.002 (2008-02-12)
7832                 */
7833                 function ComboBox($name, $w, $h, $values, $prop=array()) {
7834                         $this->_addfield('combobox', $name, $this->x, $this->y, $w, $h, $prop);
7835                         $s = '';
7836                         foreach($values as $value) {
7837                                 $s .= "'".addslashes($value)."',";
7838                         }
7839                         $this->javascript .= "f".$name.".setItems([".substr($s,0,-1)."]);\n";
7840                 }
7841
7842                 /*
7843                 * Creates a CheckBox field
7844                 * @param string $name field name
7845                 * @param int $w width
7846                 * @param boolean $checked define the initial state (default = false).
7847                 * @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>
7848                 * @access public
7849                 * @author Denis Van Nuffelen, Nicola Asuni
7850                 * @since 2.1.002 (2008-02-12)
7851                 */
7852                 function CheckBox($name, $w, $checked=false, $prop=array()) {
7853                         $prop['value'] = ($checked ? 'Yes' : 'Off');
7854                         if (!isset($prop['strokeColor'])) {
7855                                 $prop['strokeColor'] = 'black';
7856                         }
7857                         $this->_addfield('checkbox', $name, $this->x, $this->y, $w, $w, $prop);
7858                 }
7859
7860                 /*
7861                 * Creates a button field
7862                 * @param string $name field name
7863                 * @param int $w width
7864                 * @param int $h height
7865                 * @param string $caption caption.
7866                 * @param string $action action triggered by the button (JavaScript code).
7867                 * @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>
7868                 * @access public
7869                 * @author Denis Van Nuffelen, Nicola Asuni
7870                 * @since 2.1.002 (2008-02-12)
7871                 */
7872                 function Button($name, $w, $h, $caption, $action, $prop=array()) {
7873                         if (!isset($prop['strokeColor'])) {
7874                                 $prop['strokeColor'] = 'black';
7875                         }
7876                         if (!isset($prop['borderStyle'])) {
7877                                 $prop['borderStyle'] = 'beveled';
7878                         }
7879                         $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
7880                         $this->javascript .= "f".$name.".buttonSetCaption('".addslashes($caption)."');\n";
7881                         $this->javascript .= "f".$name.".setAction('MouseUp','".addslashes($action)."');\n";
7882                         $this->javascript .= "f".$name.".highlight='push';\n";
7883                         $this->javascript .= "f".$name.".print=false;\n";
7884                 }
7885
7886                 // END JAVASCRIPT - FORMS ------------------------------
7887
7888                 /*
7889                 * Enable Write permissions for PDF Reader.
7890                 * @access protected
7891                 * @author Nicola Asuni
7892                 * @since 2.9.000 (2008-03-26)
7893                 */
7894                 function _putuserrights() {
7895                         if (!$this->ur) {
7896                                 return;
7897                         }
7898                         $this->_out('/Perms');
7899                         $this->_out('<<');
7900                         $this->_out('/UR3');
7901                         $this->_out('<<');
7902                         //$this->_out('/SubFilter/adbe.pkcs7.detached/Filter/Adobe.PPKLite/Contents');
7903                         //$this->_out('<0>');
7904                         //$this->_out('/ByteRange[0 3]');
7905                         $this->_out('/M '.$this->_datestring('D:'.date('YmdHis')));
7906                         $this->_out('/Name(TCPDF)');
7907                         $this->_out('/Reference[');
7908                         $this->_out('<<');
7909                         $this->_out('/TransformParams');
7910                         $this->_out('<<');
7911                         $this->_out('/Type/TransformParams');
7912                         $this->_out('/V/2.2');
7913                         if (!empty($this->ur_document)) {
7914                                 $this->_out('/Document['.$this->ur_document.']');
7915                         }
7916                         if (!empty($this->ur_annots)) {
7917                                 $this->_out('/Annots['.$this->ur_annots.']');
7918                         }
7919                         if (!empty($this->ur_form)) {
7920                                 $this->_out('/Form['.$this->ur_form.']');
7921                         }
7922                         if (!empty($this->ur_signature)) {
7923                                 $this->_out('/Signature['.$this->ur_signature.']');
7924                         }
7925                         $this->_out('>>');
7926                         $this->_out('/TransformMethod/UR3');
7927                         $this->_out('/Type/SigRef');
7928                         $this->_out('>>');
7929                         $this->_out(']');
7930                         $this->_out('/Type/Sig');
7931                         $this->_out('>>');
7932                         $this->_out('>>');
7933                 }
7934
7935                 /*
7936                 * Set User's Rights for PDF Reader
7937                 * Check the PDF Reference 8.7.1 Transform Methods,
7938                 * Table 8.105 Entries in the UR transform parameters dictionary
7939                 * @param boolean $enable if true enable user's rights on PDF reader
7940                 * @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.
7941                 * @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.
7942                 * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
7943                 * @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.
7944                 * @access public
7945                 * @author Nicola Asuni
7946                 * @since 2.9.000 (2008-03-26)
7947                 */
7948                 function setUserRights(
7949                                 $enable=true,
7950                                 $document="/FullSave",
7951                                 $annots="/Create/Delete/Modify/Copy/Import/Export",
7952                                 $form="/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate",
7953                                 $signature="/Modify") {
7954                         $this->ur = $enable;
7955                         $this->ur_document = $document;
7956                         $this->ur_annots = $annots;
7957                         $this->ur_form = $form;
7958                         $this->ur_signature = $signature;
7959                 }
7960
7961                 /*
7962                 * Create a new page group.
7963                 * NOTE: call this function before calling AddPage()
7964                 * @access public
7965                 * @since 3.0.000 (2008-03-27)
7966                 */
7967                 function startPageGroup() {
7968                         $this->newpagegroup = true;
7969                 }
7970
7971                 /*
7972                 * Return the current page in the group.
7973                 * @return current page in the group
7974                 * @access public
7975                 * @since 3.0.000 (2008-03-27)
7976                 */
7977                 function getGroupPageNo() {
7978                         return $this->pagegroups[$this->currpagegroup];
7979                 }
7980
7981                 /*
7982                 * Return the alias of the current page group
7983         * If the current font is unicode type, the returned string is surrounded by additional curly braces.
7984                 * (will be replaced by the total number of pages in this group).
7985                 * @return alias of the current page group
7986                 * @access public
7987                 * @since 3.0.000 (2008-03-27)
7988                 */
7989                 function getPageGroupAlias() {
7990                         if (strpos(strtolower($this->CurrentFont['type']), 'unicode')) {
7991                                 return "{".$this->currpagegroup."}";
7992             }
7993                         return $this->currpagegroup;
7994                 }
7995
7996                 /*
7997                 * Put visibility settings.
7998                 * @access protected
7999                 * @since 3.0.000 (2008-03-27)
8000                 */
8001                 function _putocg() {
8002                         $this->_newobj();
8003                         $this->n_ocg_print = $this->n;
8004                         $this->_out('<</Type /OCG /Name '.$this->_textstring('print'));
8005                         $this->_out('/Usage <</Print <</PrintState /ON>> /View <</ViewState /OFF>>>>>>');
8006                         $this->_out('endobj');
8007                         $this->_newobj();
8008                         $this->n_ocg_view=$this->n;
8009                         $this->_out('<</Type /OCG /Name '.$this->_textstring('view'));
8010                         $this->_out('/Usage <</Print <</PrintState /OFF>> /View <</ViewState /ON>>>>>>');
8011                         $this->_out('endobj');
8012                 }
8013
8014                 /*
8015                 * Set the visibility of the successive elements.
8016                 * This can be useful, for instance, to put a background
8017                 * image or color that will show on screen but won't print.
8018                 * @param string $v visibility mode. Legal values are: all, print, screen.
8019                 * @access public
8020                 * @since 3.0.000 (2008-03-27)
8021                 */
8022                 function setVisibility($v) {
8023                         if ($this->openMarkedContent) {
8024                                 // close existing open marked-content
8025                                 $this->_out('EMC');
8026                                 $this->openMarkedContent = false;
8027                         }
8028                         switch($v) {
8029                                 case "print": {
8030                                         $this->_out('/OC /OC1 BDC');
8031                                         $this->openMarkedContent = true;
8032                                         break;
8033                                 }
8034                                 case "screen": {
8035                                         $this->_out('/OC /OC2 BDC');
8036                                         $this->openMarkedContent = true;
8037                                         break;
8038                                 }
8039                                 case "all": {
8040                                         $this->_out('');
8041                                         break;
8042                                 }
8043                                 default: {
8044                                         $this->Error('Incorrect visibility: '.$v);
8045                                         break;
8046                                 }
8047                         }
8048                         $this->visibility = $v;
8049                 }
8050
8051                 /*
8052                 * Add transparency parameters to the current extgstate
8053                 * @param array $params parameters
8054                 * @return the number of extgstates
8055                 * @access protected
8056                 * @since 3.0.000 (2008-03-27)
8057                 */
8058                 function addExtGState($parms) {
8059                         $n = count($this->extgstates) + 1;
8060                         $this->extgstates[$n]['parms'] = $parms;
8061                         return $n;
8062                 }
8063
8064                 /*
8065                 * Add an extgstate
8066                 * @param array $gs extgstate
8067                 * @access protected
8068                 * @since 3.0.000 (2008-03-27)
8069                 */
8070                 function setExtGState($gs) {
8071                         $this->_out(sprintf('/GS%d gs', $gs));
8072                 }
8073
8074                 /*
8075                 * Put extgstates for object transparency
8076                 * @param array $gs extgstate
8077                 * @access protected
8078                 * @since 3.0.000 (2008-03-27)
8079                 */
8080                 function _putextgstates() {
8081                         $ne = count($this->extgstates);
8082                         for ($i = 1; $i <= $ne; $i++) {
8083                                 $this->_newobj();
8084                                 $this->extgstates[$i]['n'] = $this->n;
8085                                 $this->_out('<</Type /ExtGState');
8086                                 foreach ($this->extgstates[$i]['parms'] as $k => $v) {
8087                                         $this->_out('/'.$k.' '.$v);
8088                                 }
8089                                 $this->_out('>>');
8090                                 $this->_out('endobj');
8091                         }
8092                 }
8093
8094                 /*
8095                 * Set alpha for stroking (CA) and non-stroking (ca) operations.
8096                 * @param float $alpha real value from 0 (transparent) to 1 (opaque)
8097                 * @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
8098                 * @access public
8099                 * @since 3.0.000 (2008-03-27)
8100                 */
8101                 function setAlpha($alpha, $bm='Normal') {
8102                         $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm));
8103                         $this->setExtGState($gs);
8104                 }
8105
8106                 /*
8107                 * Set the default JPEG compression quality (1-100)
8108                 * @param int $quality JPEG quality, integer between 1 and 100
8109                 * @access public
8110                 * @since 3.0.000 (2008-03-27)
8111                 */
8112                 function setJPEGQuality($quality) {
8113                         if (($quality < 1) OR ($quality > 100)) {
8114                                 $quality = 75;
8115                         }
8116                         $this->jpeg_quality = intval($quality);
8117                 }
8118
8119                 /*
8120                 * Set the default number of columns in a row for HTML tables.
8121                 * @param int $cols number of columns
8122                 * @access public
8123                 * @since 3.0.014 (2008-06-04)
8124                 */
8125                 function setDefaultTableColumns($cols=4) {
8126                         $this->default_table_columns = intval($cols);
8127                 }
8128
8129                 /*
8130                 * Set the height of cell repect font height.
8131                 * @param int $h cell proportion respect font height (typical value = 1.25).
8132                 * @access public
8133                 * @since 3.0.014 (2008-06-04)
8134                 */
8135                 function setCellHeightRatio($h) {
8136                         $this->cell_height_ratio = $h;
8137                 }
8138
8139                 /*
8140                 * return the height of cell repect font height.
8141                 * @access public
8142                 * @since 4.0.012 (2008-07-24)
8143                 */
8144                 function getCellHeightRatio() {
8145                         return $this->cell_height_ratio;
8146                 }
8147
8148                 /*
8149                 * Set the PDF version (check PDF reference for valid values).
8150                 * Default value is 1.t
8151                 * @access public
8152                 * @since 3.1.000 (2008-06-09)
8153                 */
8154                 function setPDFVersion($version="1.7") {
8155                         $this->PDFVersion = $version;
8156                 }
8157
8158                 /*
8159                 * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
8160                 * (see Section 8.1 of PDF reference, "Viewer Preferences").
8161                 * <ul>
8162                 * <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>
8163                 * <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>
8164                 * <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>
8165                 * <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>
8166                 * <li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li>
8167                 * <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>
8168                 * <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>
8169                 * <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>
8170                 * <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>
8171                 * <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>
8172                 * <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>
8173                 * <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>
8174                 * <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>
8175                 * <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>
8176                 * <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>
8177                 * <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>
8178                 * </ul>
8179                 * @param array $preferences array of options.
8180                 * @author Nicola Asuni
8181                 * @access public
8182                 * @since 3.1.000 (2008-06-09)
8183                 */
8184                 function setViewerPreferences($preferences) {
8185                         $this->viewer_preferences = $preferences;
8186                 }
8187
8188                 /**
8189                 * Paints a linear colour gradient.
8190                 * @param float $x abscissa of the top left corner of the rectangle.
8191                 * @param float $y ordinate of the top left corner of the rectangle.
8192                 * @param float $w width of the rectangle.
8193                 * @param float $h height of the rectangle.
8194                 * @param array $col1 first color (RGB components).
8195                 * @param array $col2 second color (RGB components).
8196                 * @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).
8197                 * @author Andreas Würmser, Nicola Asuni
8198                 * @since 3.1.000 (2008-06-09)
8199                 * @access public
8200                 */
8201                 function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
8202                         $this->Clip($x, $y, $w, $h);
8203                         $this->Gradient(2, $col1, $col2, $coords);
8204                 }
8205
8206                 /**
8207                 * Paints a radial colour gradient.
8208                 * @param float $x abscissa of the top left corner of the rectangle.
8209                 * @param float $y ordinate of the top left corner of the rectangle.
8210                 * @param float $w width of the rectangle.
8211                 * @param float $h height of the rectangle.
8212                 * @param array $col1 first color (RGB components).
8213                 * @param array $col2 second color (RGB components).
8214                 * @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.
8215                 * @author Andreas Würmser, Nicola Asuni
8216                 * @since 3.1.000 (2008-06-09)
8217                 * @access public
8218                 */
8219                 function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
8220                         $this->Clip($x, $y, $w, $h);
8221                         $this->Gradient(3, $col1, $col2, $coords);
8222                 }
8223
8224                 /**
8225                 * Paints a coons patch mesh.
8226                 * @param float $x abscissa of the top left corner of the rectangle.
8227                 * @param float $y ordinate of the top left corner of the rectangle.
8228                 * @param float $w width of the rectangle.
8229                 * @param float $h height of the rectangle.
8230                 * @param array $col1 first color (lower left corner) (RGB components).
8231                 * @param array $col2 second color (lower right corner) (RGB components).
8232                 * @param array $col3 third color (upper right corner) (RGB components).
8233                 * @param array $col4 fourth color (upper left corner) (RGB components).
8234                 * @param array $coords <ul><li>for one patch mesh: array(float x1, float y1, .... float x12, float y12): 12 pairs of coordinates (normally from 0 to 1) which specify the Bezier control points that define the patch. First pair is the lower left edge point, next is its right control point (control point 2). Then the other points are defined in the order: control point 1, edge point, control point 2 going counter-clockwise around the patch. Last (x12, y12) is the first edge point's left control point (control point 1).</li><li>for two or more patch meshes: array[number of patches]: arrays with the following keys for each patch: f: where to put that patch (0 = first patch, 1, 2, 3 = right, top and left of precedent patch - I didn't figure this out completely - just try and error ;-) points: 12 pairs of coordinates of the Bezier control points as above for the first patch, 8 pairs of coordinates for the following patches, ignoring the coordinates already defined by the precedent patch (I also didn't figure out the order of these - also: try and see what's happening) colors: must be 4 colors for the first patch, 2 colors for the following patches</li></ul>
8235                 * @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
8236                 * @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
8237                 * @author Andreas Würmser, Nicola Asuni
8238                 * @since 3.1.000 (2008-06-09)
8239                 * @access public
8240                 */
8241                 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) {
8242                         $this->Clip($x, $y, $w, $h);
8243                         $n = count($this->gradients) + 1;
8244                         $this->gradients[$n]['type'] = 6; //coons patch mesh
8245                         //check the coords array if it is the simple array or the multi patch array
8246                         if (!isset($coords[0]['f'])){
8247                                 //simple array -> convert to multi patch array
8248                                 if (!isset($col1[1])) {
8249                                         $col1[1] = $col1[2] = $col1[0];
8250                                 }
8251                                 if (!isset($col2[1])) {
8252                                         $col2[1] = $col2[2] = $col2[0];
8253                                 }
8254                                 if (!isset($col3[1])) {
8255                                         $col3[1] = $col3[2] = $col3[0];
8256                                 }
8257                                 if (!isset($col4[1])) {
8258                                         $col4[1] = $col4[2] = $col4[0];
8259                                 }
8260                                 $patch_array[0]['f'] = 0;
8261                                 $patch_array[0]['points'] = $coords;
8262                                 $patch_array[0]['colors'][0]['r'] = $col1[0];
8263                                 $patch_array[0]['colors'][0]['g'] = $col1[1];
8264                                 $patch_array[0]['colors'][0]['b'] = $col1[2];
8265                                 $patch_array[0]['colors'][1]['r'] = $col2[0];
8266                                 $patch_array[0]['colors'][1]['g'] = $col2[1];
8267                                 $patch_array[0]['colors'][1]['b'] = $col2[2];
8268                                 $patch_array[0]['colors'][2]['r'] = $col3[0];
8269                                 $patch_array[0]['colors'][2]['g'] = $col3[1];
8270                                 $patch_array[0]['colors'][2]['b'] = $col3[2];
8271                                 $patch_array[0]['colors'][3]['r'] = $col4[0];
8272                                 $patch_array[0]['colors'][3]['g'] = $col4[1];
8273                                 $patch_array[0]['colors'][3]['b'] = $col4[2];
8274                         } else {
8275                                 //multi patch array
8276                                 $patch_array = $coords;
8277                         }
8278                         $bpcd = 65535; //16 BitsPerCoordinate
8279                         //build the data stream
8280                         $this->gradients[$n]['stream'] = "";
8281                         for($i=0; $i < count($patch_array); $i++) {
8282                                 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
8283                                 for($j=0; $j < count($patch_array[$i]['points']); $j++) {
8284                                         //each point as 16 bit
8285                                         $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j]-$coords_min)/($coords_max-$coords_min))*$bpcd;
8286                                         if ($patch_array[$i]['points'][$j] < 0) {
8287                                                 $patch_array[$i]['points'][$j] = 0;
8288                                         }
8289                                         if ($patch_array[$i]['points'][$j] > $bpcd) {
8290                                                 $patch_array[$i]['points'][$j] = $bpcd;
8291                                         }
8292                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]/256));
8293                                         $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j]%256));
8294                                 }
8295                                 for($j=0; $j < count($patch_array[$i]['colors']); $j++) {
8296                                         //each color component as 8 bit
8297                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
8298                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
8299                                         $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
8300                                 }
8301                         }
8302                         //paint the gradient
8303                         $this->_out('/Sh'.$n.' sh');
8304                         //restore previous Graphic State
8305                         $this->_out('Q');
8306                 }
8307
8308                 /**
8309                 * Set a rectangular clipping area.
8310                 * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
8311                 * @param float $y ordinate of the top left corner of the rectangle.
8312                 * @param float $w width of the rectangle.
8313                 * @param float $h height of the rectangle.
8314                 * @author Andreas Würmser, Nicola Asuni
8315                 * @since 3.1.000 (2008-06-09)
8316                 * @access protected
8317                 */
8318                 function Clip($x, $y, $w, $h){
8319                         if ($this->rtl) {
8320                                 $x = $this->w - $x - $w;
8321                         }
8322                         //save current Graphic State
8323                         $s = 'q';
8324                         //set clipping area
8325                         $s .= sprintf(' %.2f %.2f %.2f %.2f re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
8326                         //set up transformation matrix for gradient
8327                         $s .= sprintf(' %.3f 0 0 %.3f %.3f %.3f cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
8328                         $this->_out($s);
8329                 }
8330
8331                 /**
8332                 * Output gradient.
8333                 * @param int $type type of gradient.
8334                 * @param array $col1 first color (RGB components).
8335                 * @param array $col2 second color (RGB components).
8336                 * @param array $coords array of coordinates.
8337                 * @author Andreas Würmser, Nicola Asuni
8338                 * @since 3.1.000 (2008-06-09)
8339                 * @access protected
8340                 */
8341                 function Gradient($type, $col1, $col2, $coords){
8342                         $n = count($this->gradients) + 1;
8343                         $this->gradients[$n]['type'] = $type;
8344                         if (!isset($col1[1])) {
8345                                 $col1[1]=$col1[2]=$col1[0];
8346                         }
8347                         $this->gradients[$n]['col1'] = sprintf('%.3f %.3f %.3f', ($col1[0]/255), ($col1[1]/255), ($col1[2]/255));
8348                         if (!isset($col2[1])) {
8349                                 $col2[1] = $col2[2] = $col2[0];
8350                         }
8351                         $this->gradients[$n]['col2'] = sprintf('%.3f %.3f %.3f', ($col2[0]/255), ($col2[1]/255), ($col2[2]/255));
8352                         $this->gradients[$n]['coords'] = $coords;
8353                         //paint the gradient
8354                         $this->_out('/Sh'.$n.' sh');
8355                         //restore previous Graphic State
8356                         $this->_out('Q');
8357                 }
8358
8359                 /**
8360                 * Output shaders.
8361                 * @author Andreas Würmser, Nicola Asuni
8362                 * @since 3.1.000 (2008-06-09)
8363                 * @access protected
8364                 */
8365                 function _putshaders() {
8366                         foreach($this->gradients as $id => $grad) {
8367                                 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
8368                                         $this->_newobj();
8369                                         $this->_out('<<');
8370                                         $this->_out('/FunctionType 2');
8371                                         $this->_out('/Domain [0.0 1.0]');
8372                                         $this->_out('/C0 ['.$grad['col1'].']');
8373                                         $this->_out('/C1 ['.$grad['col2'].']');
8374                                         $this->_out('/N 1');
8375                                         $this->_out('>>');
8376                                         $this->_out('endobj');
8377                                         $f1 = $this->n;
8378                                 }
8379                                 $this->_newobj();
8380                                 $this->_out('<<');
8381                                 $this->_out('/ShadingType '.$grad['type']);
8382                                 $this->_out('/ColorSpace /DeviceRGB');
8383                                 if ($grad['type'] == 2) {
8384                                         $this->_out(sprintf('/Coords [%.3f %.3f %.3f %.3f]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]));
8385                                         $this->_out('/Function '.$f1.' 0 R');
8386                                         $this->_out('/Extend [true true] ');
8387                                         $this->_out('>>');
8388                                 } elseif ($grad['type'] == 3) {
8389                                         //x0, y0, r0, x1, y1, r1
8390                                         //at this this time radius of inner circle is 0
8391                                         $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]));
8392                                         $this->_out('/Function '.$f1.' 0 R');
8393                                         $this->_out('/Extend [true true] ');
8394                                         $this->_out('>>');
8395                                 } elseif ($grad['type'] == 6) {
8396                                         $this->_out('/BitsPerCoordinate 16');
8397                                         $this->_out('/BitsPerComponent 8');
8398                                         $this->_out('/Decode[0 1 0 1 0 1 0 1 0 1]');
8399                                         $this->_out('/BitsPerFlag 8');
8400                                         $this->_out('/Length '.strlen($grad['stream']));
8401                                         $this->_out('>>');
8402                                         $this->_putstream($grad['stream']);
8403                                 }
8404                                 $this->_out('endobj');
8405                                 $this->gradients[$id]['id'] = $this->n;
8406                         }
8407                 }
8408
8409                 /**
8410                 * Output an arc
8411                 * @author Maxime Delorme, Nicola Asuni
8412                 * @since 3.1.000 (2008-06-09)
8413                 * @access protected
8414                 */
8415                 function _outarc($x1, $y1, $x2, $y2, $x3, $y3 ) {
8416                         $h = $this->h;
8417                         $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));
8418                 }
8419
8420                 /**
8421                 * Draw the sector of a circle.
8422                 * It can be used for instance to render pie charts.
8423                 * @param float $xc abscissa of the center.
8424                 * @param float $yc ordinate of the center.
8425                 * @param float $r radius.
8426                 * @param float $a start angle (in degrees).
8427                 * @param float $b end angle (in degrees).
8428                 * @param string $style: D, F, FD or DF (draw, fill, fill and draw). Default: FD.
8429                 * @param float $cw: indicates whether to go clockwise (default: true).
8430                 * @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.
8431                 * @author Maxime Delorme, Nicola Asuni
8432                 * @since 3.1.000 (2008-06-09)
8433                 * @access public
8434                 */
8435                 function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
8436                         if ($this->rtl) {
8437                                 $xc = $this->w - $xc - $w;
8438                         }
8439                         if ($cw) {
8440                                 $d = $b;
8441                                 $b = $o - $a;
8442                                 $a = $o - $d;
8443                         } else {
8444                                 $b += $o;
8445                                 $a += $o;
8446                         }
8447                         $a = ($a % 360) + 360;
8448                         $b = ($b % 360) + 360;
8449                         if ($a > $b) {
8450                                 $b +=360;
8451                         }
8452                         $b = $b / 360 * 2 * M_PI;
8453                         $a = $a / 360 * 2 * M_PI;
8454                         $d = $b - $a;
8455                         if ($d == 0 ) {
8456                                 $d = 2 * M_PI;
8457                         }
8458                         $k = $this->k;
8459                         $hp = $this->h;
8460                         if ($style=='F') {
8461                                 $op = 'f';
8462                         } elseif ($style=='FD' or $style=='DF') {
8463                                 $op = 'b';
8464                         } else {
8465                                 $op = 's';
8466                         }
8467                         if (sin($d/2)) {
8468                                 $MyArc = 4/3 * (1 - cos($d/2)) / sin($d/2) * $r;
8469                         }
8470                         //first put the center
8471                         $this->_out(sprintf('%.2f %.2f m', ($xc)*$k, ($hp-$yc)*$k));
8472                         //put the first point
8473                         $this->_out(sprintf('%.2f %.2f l', ($xc+$r*cos($a))*$k, (($hp-($yc-$r*sin($a)))*$k)));
8474                         //draw the arc
8475                         if ($d < (M_PI/2)) {
8476                                 $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));
8477                         } else {
8478                                 $b = $a + $d/4;
8479                                 $MyArc = 4/3*(1-cos($d/8))/sin($d/8)*$r;
8480                                 $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));
8481                                 $a = $b;
8482                                 $b = $a + $d/4;
8483                                 $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));
8484                                 $a = $b;
8485                                 $b = $a + $d/4;
8486                                 $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) );
8487                                 $a = $b;
8488                                 $b = $a + $d/4;
8489                                 $this->_outarc($xc+$r*cos($a)+$MyArc*cos(M_PI/2+$a), $yc-$r*sin($a)-$MyArc*sin(M_PI/2+$a), $xc+$r*cos($b)+$MyArc*cos($b-M_PI/2), $yc-$r*sin($b)-$MyArc*sin($b-M_PI/2), $xc+$r*cos($b), $yc-$r*sin($b));
8490                         }
8491                         //terminate drawing
8492                         $this->_out($op);
8493                 }
8494
8495                 /**
8496                 * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
8497                 * Only vector drawing is supported, not text or bitmap.
8498                 * 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).
8499                 * @param string $file Name of the file containing the image.
8500                 * @param float $x Abscissa of the upper-left corner.
8501                 * @param float $y Ordinate of the upper-left corner.
8502                 * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
8503                 * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
8504                 * @param mixed $link URL or identifier returned by AddLink().
8505                 * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
8506                 * @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>
8507                 * @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>
8508                 * @author Valentin Schmidt, Nicola Asuni
8509                 * @since 3.1.000 (2008-06-09)
8510                 * @access public
8511                 */
8512                 function ImageEps($file, $x, $y, $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='') {
8513                         $data = file_get_contents($file);
8514                         if ($data === false) {
8515                                 $this->Error('EPS file not found: '.$file);
8516                         }
8517                         $regs = array();
8518                         // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
8519                         preg_match ('/%%Creator:([^\r\n]+)/', $data, $regs); # find Creator
8520                         if (count($regs) > 1) {
8521                                 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
8522                                 if (strpos($version_str, 'Adobe Illustrator') !== false) {
8523                                         $versexp = explode(' ', $version_str);
8524                                         $version = (float)array_pop($versexp);
8525                                         if ($version >= 9) {
8526                                                 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
8527                                         }
8528                                 }
8529                         }
8530                         // strip binary bytes in front of PS-header
8531                         $start = strpos($data, '%!PS-Adobe');
8532                         if ($start > 0) {
8533                                 $data = substr($data, $start);
8534                         }
8535                         // find BoundingBox params
8536                         preg_match ("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
8537                         if (count($regs) > 1) {
8538                                 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
8539                         } else {
8540                                 $this->Error('No BoundingBox found in EPS file: '.$file);
8541                         }
8542                         $start = strpos($data, '%%EndSetup');
8543                         if ($start === false) {
8544                                 $start = strpos($data, '%%EndProlog');
8545                         }
8546                         if ($start === false) {
8547                                 $start = strpos($data, '%%BoundingBox');
8548                         }
8549                         $data = substr($data, $start);
8550                         $end = strpos($data, '%%PageTrailer');
8551                         if ($end===false) {
8552                                 $end = strpos($data, 'showpage');
8553                         }
8554                         if ($end) {
8555                                 $data = substr($data, 0, $end);
8556                         }
8557                         $k = $this->k;
8558                         if ($w > 0) {
8559                                 $scale_x = $w/(($x2-$x1)/$k);
8560                                 if ($h > 0) {
8561                                         $scale_y = $h/(($y2-$y1)/$k);
8562                                 } else {
8563                                         $scale_y = $scale_x;
8564                                         $h = ($y2-$y1)/$k * $scale_y;
8565                                 }
8566                         } else {
8567                                 if ($h > 0) {
8568                                         $scale_y = $h/(($y2-$y1)/$k);
8569                                         $scale_x = $scale_y;
8570                                         $w = ($x2-$x1)/$k * $scale_x;
8571                                 } else {
8572                                         $w = ($x2 - $x1) / $k;
8573                                         $h = ($y2 - $y1) / $k;
8574                                 }
8575                         }
8576                         // Check whether we need a new page first as this does not fit
8577                         if ((($this->y + $h) > $this->PageBreakTrigger) AND empty($this->InFooter) AND $this->AcceptPageBreak()) {
8578                                 // Automatic page break
8579                                 $this->AddPage($this->CurOrientation);
8580                                 // Reset coordinates to top fo next page
8581                                 //$x = $this->GetX();
8582                                 $y = $this->GetY() + $this->cMargin;
8583                         }
8584                         // set bottomcoordinates
8585                         $this->img_rb_y = $y + $h;
8586                         // set alignment
8587                         if ($this->rtl) {
8588                                 if ($palign == 'L') {
8589                                         $ximg = $this->lMargin;
8590                                         // set right side coordinate
8591                                         $this->img_rb_x = $ximg + $w;
8592                                 } elseif ($palign == 'C') {
8593                                         $ximg = ($this->w - $x - $w) / 2;
8594                                         // set right side coordinate
8595                                         $this->img_rb_x = $ximg + $w;
8596                                 } else {
8597                                         $ximg = $this->w - $x - $w;
8598                                         // set left side coordinate
8599                                         $this->img_rb_x = $ximg;
8600                                 }
8601                         } else {
8602                                 if ($palign == 'R') {
8603                                         $ximg = $this->w - $this->rMargin - $w;
8604                                         // set left side coordinate
8605                                         $this->img_rb_x = $ximg;
8606                                 } elseif ($palign == 'C') {
8607                                         $ximg = ($this->w - $x - $w) / 2;
8608                                         // set right side coordinate
8609                                         $this->img_rb_x = $ximg + $w;
8610                                 } else {
8611                                         $ximg = $x;
8612                                         // set right side coordinate
8613                                         $this->img_rb_x = $ximg + $w;
8614                                 }
8615                         }
8616                         if ($useBoundingBox){
8617                                 $dx = $ximg * $k - $x1;
8618                                 $dy = $y * $k - $y1;
8619                         } else {
8620                                 $dx = $ximg * $k;
8621                                 $dy = $y * $k;
8622                         }
8623                         // save the current graphic state
8624                         $this->_out('q');
8625                         // translate
8626                         $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy+($this->hPt - 2*$y*$k - ($y2-$y1))));
8627                         // scale
8628                         if (isset($scale_x)) {
8629                                 $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1*(1-$scale_x), $y2*(1-$scale_y)));
8630                         }
8631                         // handle pc/unix/mac line endings
8632                         $lines = split("\r\n|[\r\n]", $data);
8633                         $u=0;
8634                         $cnt = count($lines);
8635                         for ($i=0; $i < $cnt; $i++) {
8636                                 $line = $lines[$i];
8637                                 if (($line == '') OR ($line{0} == '%')) {
8638                                         continue;
8639                                 }
8640                                 $len = strlen($line);
8641                                 $chunks = explode(' ', $line);
8642                                 $cmd = array_pop($chunks);
8643                                 // RGB
8644                                 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
8645                                         $b = array_pop($chunks);
8646                                         $g = array_pop($chunks);
8647                                         $r = array_pop($chunks);
8648                                         $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!
8649                                         continue;
8650                                 }
8651                                 switch ($cmd) {
8652                                         case 'm':
8653                                         case 'l':
8654                                         case 'v':
8655                                         case 'y':
8656                                         case 'c':
8657                                         case 'k':
8658                                         case 'K':
8659                                         case 'g':
8660                                         case 'G':
8661                                         case 's':
8662                                         case 'S':
8663                                         case 'J':
8664                                         case 'j':
8665                                         case 'w':
8666                                         case 'M':
8667                                         case 'd':
8668                                         case 'n':
8669                                         case 'v': {
8670                                                 $this->_out($line);
8671                                                 break;
8672                                         }
8673                                         case 'x': {// custom fill color
8674                                                 list($c,$m,$y,$k) = $chunks;
8675                                                 $this->_out("$c $m $y $k k");
8676                                                 break;
8677                                         }
8678                                         case 'X': { // custom stroke color
8679                                                 list($c,$m,$y,$k) = $chunks;
8680                                                 $this->_out("$c $m $y $k K");
8681                                                 break;
8682                                         }
8683                                         case 'Y':
8684                                         case 'N':
8685                                         case 'V':
8686                                         case 'L':
8687                                         case 'C': {
8688                                                 $line{$len-1} = strtolower($cmd);
8689                                                 $this->_out($line);
8690                                                 break;
8691                                         }
8692                                         case 'b':
8693                                         case 'B': {
8694                                                 $this->_out($cmd . '*');
8695                                                 break;
8696                                         }
8697                                         case 'f':
8698                                         case 'F': {
8699                                                 if ($u > 0) {
8700                                                         $isU = false;
8701                                                         $max = min($i+5, $cnt);
8702                                                         for ($j=$i+1; $j < $max; $j++)
8703                                                           $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
8704                                                         if ($isU) {
8705                                                                 $this->_out("f*");
8706                                                         }
8707                                                 } else {
8708                                                         $this->_out("f*");
8709                                                 }
8710                                                 break;
8711                                         }
8712                                         case '*u': {
8713                                                 $u++;
8714                                                 break;
8715                                         }
8716                                         case '*U': {
8717                                                 $u--;
8718                                                 break;
8719                                         }
8720                                 }
8721                         }
8722                         // restore previous graphic state
8723                         $this->_out('Q');
8724                         if ($link) {
8725                                 $this->Link($ximg, $y, $w, $h, $link);
8726                         }
8727                         // set pointer to align the successive text/objects
8728                         switch($align) {
8729                                 case 'T':{
8730                                         $this->y = $y;
8731                                         $this->x = $this->img_rb_x;
8732                                         break;
8733                                 }
8734                                 case 'M':{
8735                                         $this->y = $y + round($h/2);
8736                                         $this->x = $this->img_rb_x;
8737                                         break;
8738                                 }
8739                                 case 'B':{
8740                                         $this->y = $this->img_rb_y;
8741                                         $this->x = $this->img_rb_x;
8742                                         break;
8743                                 }
8744                                 case 'N':{
8745                                         $this->SetY($this->img_rb_y);
8746                                         break;
8747                                 }
8748                                 default:{
8749                                         break;
8750                                 }
8751                         }
8752                         $this->endlinex = $this->img_rb_x;
8753                 }
8754
8755                 /**
8756                  * Set document barcode.
8757                  * @param string $bc barcode
8758                  */
8759                 function setBarcode($bc="") {
8760                         $this->barcode = $bc;
8761                 }
8762
8763                 /**
8764                  * Get current barcode.
8765                  * @return string
8766                  * @since 4.0.012 (2008-07-24)
8767                  */
8768                 function getBarcode() {
8769                         return $this->barcode;
8770                 }
8771
8772                 /**
8773                  * Print Barcode.
8774                  * @param string $code code to print
8775                  * @param string $type type of barcode.
8776                  * @param int $x x position in user units
8777                  * @param int $y y position in user units
8778                  * @param int $w width in user units
8779                  * @param int $h height position in user units
8780                  * @param float $xres width of the smallest bar in user units
8781                  * @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>
8782                  * @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>
8783                  * @author Nicola Asuni
8784                  * @since 3.1.000 (2008-06-09)
8785                  * @access public
8786                  */
8787                 function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
8788                         if (empty($code)) {
8789                                 return;
8790                         }
8791                         $barcodeobj = new TCPDFbarcode($code, $type);
8792                         $arrcode = $barcodeobj->getBarcodeArray();
8793                         if ($arrcode === false) {
8794                                 $this->Error('Error in barcode string');
8795                         }
8796                         // set default values
8797                         if (!isset($style["position"])) {
8798                                 if ($this->rtl) {
8799                                         $style["position"] = "R";
8800                                 } else {
8801                                         $style["position"] = "L";
8802                                 }
8803                         }
8804                         if (!isset($style["padding"])) {
8805                                 $style["padding"] = 0;
8806                         }
8807                         if (!isset($style["fgcolor"])) {
8808                                 $style["fgcolor"] = array(0,0,0); // default black
8809                         }
8810                         if (!isset($style["bgcolor"])) {
8811                                 $style["bgcolor"] = false; // default transparent
8812                         }
8813                         if (!isset($style["border"])) {
8814                                 $style["border"] = false;
8815                         }
8816                         if (!isset($style["text"])) {
8817                                 $style["text"] = false;
8818                                 $fontsize = 0;
8819                         }
8820                         if ($style["text"] AND isset($style["font"])) {
8821                                 $prevFontFamily = $this->FontFamily;
8822                                 $prevFontStyle = $this->FontStyle;
8823                                 $prevFontSizePt = $this->FontSizePt;
8824                                 if (isset($style["fontsize"])) {
8825                                         $fontsize = $style["fontsize"];
8826                                 } else {
8827                                         $fontsize = 0;
8828                                 }
8829                                 $this->SetFont($style["font"], '', $fontsize);
8830                         }
8831                         if (!isset($style["stretchtext"])) {
8832                                 $style["stretchtext"] = 4;
8833                         }
8834                         // set foreground color
8835                         $prevDrawColor = $this->DrawColor;
8836                         $prevTextColor = $this->TextColor;
8837                         $this->SetDrawColorArray($style["fgcolor"]);
8838                         $this->SetTextColorArray($style["fgcolor"]);
8839                         if (empty($w) OR ($w <= 0)) {
8840                                 if ($this->rtl) {
8841                                         $w = $this->x - $this->lMargin;
8842                                 } else {
8843                                         $w = $this->w - $this->rMargin - $this->x;
8844                                 }
8845                         }
8846                         if (empty($x)) {
8847                                 $x = $this->GetX();
8848                         }
8849                         if ($this->rtl) {
8850                                 $x = $this->w - $x;
8851                         }
8852                         if (empty($y)) {
8853                                 $y = $this->GetY();
8854                         }
8855                         if (empty($xres)) {
8856                                 $xres = 0.4;
8857                         }
8858                         $fbw = ($arrcode["maxw"] * $xres) + (2 * $style["padding"]);
8859                         $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style["padding"]);
8860                         if (empty($h)) {
8861                                 $h = 10 + $extraspace;
8862                         }
8863                         if ((($y + $h) > $this->PageBreakTrigger) AND (empty($this->InFooter)) AND ($this->AcceptPageBreak())) {
8864                                 //Automatic page break
8865                                 $x = $this->x;
8866                                 $ws = $this->ws;
8867                                 if ($ws > 0) {
8868                                         $this->ws = 0;
8869                                         $this->_out('0 Tw');
8870                                 }
8871                                 $this->AddPage($this->CurOrientation);
8872                                 if ($ws > 0) {
8873                                         $this->ws = $ws;
8874                                         $this->_out(sprintf('%.3f Tw',$ws * $k));
8875                                 }
8876                                 $this->x = $x;
8877                                 $y = $this->y;
8878                         }
8879                         // maximum bar heigth
8880                         $barh = $h - $extraspace;
8881                         switch ($style["position"]) {
8882                                 case "L": { // left
8883                                         if ($this->rtl) {
8884                                                 $xpos = $x - $w;
8885                                         } else {
8886                                                 $xpos = $x;
8887                                         }
8888                                         break;
8889                                 }
8890                                 case "C": { // center
8891                                         $xdiff = (($w - $fbw) / 2);
8892                                         if ($this->rtl) {
8893                                                 $xpos = $x - $w + $xdiff;
8894                                         } else {
8895                                                 $xpos = $x + $xdiff;
8896                                         }
8897                                         break;
8898                                 }
8899                                 case "R": { // right
8900                                         if ($this->rtl) {
8901                                                 $xpos = $x - $fbw;
8902                                         } else {
8903                                                 $xpos = $x + $w - $fbw;
8904                                         }
8905                                         break;
8906                                 }
8907                                 case "S": { // stretch
8908                                         $fbw = $w;
8909                                         $xres = ($w - (2 * $style["padding"])) / $arrcode["maxw"];
8910                                         if ($this->rtl) {
8911                                                 $xpos = $x - $w;
8912                                         } else {
8913                                                 $xpos = $x;
8914                                         }
8915                                         break;
8916                                 }
8917                         }
8918                         $xpos_rect = $xpos;
8919                         $xpos = $xpos_rect + $style["padding"];
8920                         $xpos_text = $xpos;
8921                         // barcode is always printed in LTR direction
8922                         $tempRTL = $this->rtl;
8923                         $this->rtl = false;
8924                         // print background color
8925                         if ($style["bgcolor"]) {
8926                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'DF', '', $style["bgcolor"]);
8927                         } elseif ($style["border"]) {
8928                                 $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
8929                         }
8930                         // print bars
8931                         if ($arrcode !== false) {
8932                                 foreach ($arrcode["bcode"] as $k => $v) {
8933                                         $bw = ($v["w"] * $xres);
8934                                         if ($v["t"]) {
8935                                                 // braw a vertical bar
8936                                                 $ypos = $y + $style["padding"] + ($v["p"] * $barh / $arrcode["maxh"]);
8937                                                 $this->Rect($xpos, $ypos, $bw, ($v["h"] * $barh  / $arrcode["maxh"]), 'DF', array("L"=>0,"T"=>0,"R"=>0,"B"=>0), $style["fgcolor"]);
8938                                         }
8939                                         $xpos += $bw;
8940                                 }
8941                         }
8942                         // print text
8943                         if ($style["text"]) {
8944                                 // print text
8945                                 $this->x = $xpos_text;
8946                                 $this->y = $y + $style["padding"] + $barh;
8947                                 $this->Cell(($arrcode["maxw"] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style["stretchtext"]);
8948                         }
8949                         // restore original direction
8950                         $this->rtl = $tempRTL;
8951                         // restore previous font
8952                         if ($style["text"] AND isset($style["font"])) {
8953                                 $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
8954                         }
8955                         // restore colors
8956                         $this->DrawColor = $prevDrawColor;
8957                         $this->TextColor = $prevTextColor;
8958                         // set bottomcoordinates
8959                         $this->img_rb_y = $y + $h;
8960                         if ($this->rtl) {
8961                                 // set left side coordinate
8962                                 $this->img_rb_x = ($this->w - $x - $w);
8963                         } else {
8964                                 // set right side coordinate
8965                                 $this->img_rb_x = $x + $w;
8966                         }
8967                         // set pointer to align the successive text/objects
8968                         switch($align) {
8969                                 case 'T':{
8970                                         $this->y = $y;
8971                                         $this->x = $this->img_rb_x;
8972                                         break;
8973                                 }
8974                                 case 'M':{
8975                                         $this->y = $y + round($h/2);
8976                                         $this->x = $this->img_rb_x;
8977                                         break;
8978                                 }
8979                                 case 'B':{
8980                                         $this->y = $this->img_rb_y;
8981                                         $this->x = $this->img_rb_x;
8982                                         break;
8983                                 }
8984                                 case 'N':{
8985                                         $this->SetY($this->img_rb_y);
8986                                         break;
8987                                 }
8988                                 default:{
8989                                         break;
8990                                 }
8991                         }
8992                 }
8993
8994                 /**
8995                  * This function is DEPRECATED, please use the new write1DBarcode() function.
8996                  * @param int $x x position in user units
8997                  * @param int $y y position in user units
8998                  * @param int $w width in user units
8999                  * @param int $h height position in user units
9000                  * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
9001                  * @param string $style barcode style
9002                  * @param string $font font for text
9003                  * @param int $xres x resolution
9004                  * @param string $code code to print
9005                  * @deprecated deprecated since version 3.1.000 (2008-06-10)
9006                  * @see write1DBarcode()
9007                  */
9008                 function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
9009                         // convert old settings for the new write1DBarcode() function.
9010                         $xres = 1 / $xres;
9011                         $newstyle = array(
9012                                 "position" => "L",
9013                                 "border" => false,
9014                                 "padding" => 0,
9015                                 "fgcolor" => array(0,0,0),
9016                                 "bgcolor" => false,
9017                                 "text" => true,
9018                                 "font" => $font,
9019                                 "fontsize" => 8,
9020                                 "stretchtext" => 4
9021                         );
9022                         if ($style & 1) {
9023                                 $newstyle["border"] = true;
9024                         }
9025                         if ($style & 2) {
9026                                 $newstyle["bgcolor"] = false;
9027                         }
9028                         if ($style & 4) {
9029                                 $newstyle["position"] = "C";
9030                         } elseif ($style & 8) {
9031                                 $newstyle["position"] = "L";
9032                         } elseif ($style & 16) {
9033                                 $newstyle["position"] = "R";
9034                         }
9035                         if ($style & 128) {
9036                                 $newstyle["text"] = true;
9037                         }
9038                         if ($style & 256) {
9039                                 $newstyle["stretchtext"] = 4;
9040                         }
9041                         $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
9042                 }
9043
9044                 /**
9045                  * Returns an array containing current margins:
9046                  * <ul>
9047                                 <li>$ret['left'] = left  margin</li>
9048                                 <li>$ret['right'] = right margin</li>
9049                                 <li>$ret['top'] = top margin</li>
9050                                 <li>$ret['bottom'] = bottom margin</li>
9051                                 <li>$ret['header'] = header margin</li>
9052                                 <li>$ret['footer'] = footer margin</li>
9053                                 <li>$ret['cell'] = cell margin</li>
9054                  * </ul>
9055                  * @return array containing all margins measures
9056                  * @since 3.2.000 (2008-06-23)
9057                  */
9058                 function getMargins() {
9059                         $ret = array(
9060                                 'left' => $this->lMargin,
9061                                 'right' => $this->rMargin,
9062                                 'top' => $this->tMargin,
9063                                 'bottom' => $this->bMargin,
9064                                 'header' => $this->header_margin,
9065                                 'footer' => $this->footer_margin,
9066                                 'cell' => $this->cMargin,
9067                         );
9068                         return $ret;
9069                 }
9070
9071                 /**
9072                  * Returns an array containing original margins:
9073                  * <ul>
9074                                 <li>$ret['left'] = left  margin</li>
9075                                 <li>$ret['right'] = right margin</li>
9076                  * </ul>
9077                  * @return array containing all margins measures
9078                  * @since 4.0.012 (2008-07-24)
9079                  */
9080                 function getOriginalMargins() {
9081                         $ret = array(
9082                                 'left' => $this->original_lMargin,
9083                                 'right' => $this->original_rMargin
9084                         );
9085                         return $ret;
9086                 }
9087
9088                 /**
9089                  * Returns the current font size.
9090                  * @return current font size
9091                  * @since 3.2.000 (2008-06-23)
9092                  */
9093                 function getFontSize() {
9094                         return $this->FontSize;
9095                 }
9096
9097                 /**
9098                  * Returns the current font size in points unit.
9099                  * @return current font size in points unit
9100                  * @since 3.2.000 (2008-06-23)
9101                  */
9102                 function getFontSizePt() {
9103                         return $this->FontSizePt;
9104                 }
9105
9106                 /**
9107                  * Prints a cell (rectangular area) with optional borders, background color and html text string.
9108                  * 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 />
9109                  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
9110                  * @param float $w Cell width. If 0, the cell extends up to the right margin.
9111                  * @param float $h Cell minimum height. The cell extends automatically if needed.
9112                  * @param float $x upper-left corner X coordinate
9113                  * @param float $y upper-left corner Y coordinate
9114                  * @param string $html html text to print. Default value: empty string.
9115                  * @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>
9116                  * @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>
9117         Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
9118                  * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
9119                  * @param boolean $reseth if true reset the last cell height (default true).
9120                  * @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>
9121                  * @uses MultiCell()
9122                  * @see Multicell(), writeHTML()
9123                  */
9124                 function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='') {
9125                         return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true);
9126                 }
9127
9128                 /**
9129                  * Returns the HTML DOM array.
9130                  * <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>
9131                  * @param string $html html code
9132                  * @return array
9133                  * @since 3.2.000 (2008-06-20)
9134                  */
9135                 function getHtmlDomArray($html) {
9136                         // remove all unsupported tags (the line below lists all supported tags)
9137                         $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>");
9138                         //replace carriage returns, newlines and tabs
9139                         $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ", "\\" => "\\\\");
9140                         $html = strtr($html, $repTable);
9141                         // remove extra spaces from tables
9142                         $html = preg_replace('/[\s]*<\/table>[\s]*/', '</table>', $html);
9143                         $html = preg_replace('/[\s]*<\/tr>[\s]*/', '</tr>', $html);
9144                         $html = preg_replace('/[\s]*<tr/', '<tr', $html);
9145                         $html = preg_replace('/[\s]*<\/th>[\s]*/', '</th>', $html);
9146                         $html = preg_replace('/[\s]*<th/', '<th', $html);
9147                         $html = preg_replace('/[\s]*<\/td>[\s]*/', '</td>', $html);
9148                         $html = preg_replace('/[\s]*<td/', '<td', $html);
9149                         $html = preg_replace('/<\/th>/', '<span></span></th>', $html);
9150                         $html = preg_replace('/<\/td>/', '<span></span></td>', $html);
9151                         // pattern for generic tag
9152                         $tagpattern = '/(<[^>]+>)/Uu';
9153                         // explodes the string
9154                         $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
9155                         // count elements
9156                         $maxel = count($a);
9157                         $key = 0;
9158                         // create an array of elements
9159                         $dom = array();
9160                         $dom[$key] = array();
9161                         // set first void element
9162                         $dom[$key]['tag'] = false;
9163                         $dom[$key]['value'] = "";
9164                         $dom[$key]['parent'] = 0;
9165                         $dom[$key]['fontname'] = $this->FontFamily;
9166                         $dom[$key]['fontstyle'] = $this->FontStyle;
9167                         $dom[$key]['fontsize'] = $this->FontSizePt;
9168                         $dom[$key]['bgcolor'] = false;
9169                         $dom[$key]['fgcolor'] = $this->fgcolor;
9170                         $dom[$key]['align'] = '';
9171                         $key++;
9172                         $level = array();
9173                         array_push($level, 0); // root
9174                         while ($key <= $maxel) {
9175                                 if ($key > 0) {
9176                                         $dom[$key] = array();
9177                                 }
9178                                 $element = $a[($key-1)];
9179                                 if (preg_match($tagpattern, $element)) {
9180                                         // html tag
9181                                         $dom[$key]['tag'] = true;
9182                                         $element = substr($element, 1, -1);
9183                                         // get tag name
9184                                         preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
9185                                         $dom[$key]['value'] = strtolower($tag[1]);
9186                                         if ($element{0} == '/') {
9187                                                 // closing html tag
9188                                                 $dom[$key]['opening'] = false;
9189                                                 $dom[$key]['parent'] = end($level);
9190                                                 array_pop($level);
9191                                                 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
9192                                                 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
9193                                                 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
9194                                                 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
9195                                                 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
9196                                                 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
9197                                                 // set the number of columns in table tag
9198                                                 if (($dom[$key]['value'] == "tr") AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
9199                                                         $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
9200                                                 }
9201                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9202                                                         $dom[($dom[$key]['parent'])]['content'] = "";
9203                                                         for ($i = ($dom[$key]['parent'] + 1); $i < $key; $i++) {
9204                                                                 $dom[($dom[$key]['parent'])]['content'] .= $a[($i-1)];
9205                                                         }
9206                                                         $key = $i;
9207                                                 }
9208                                         } else {
9209                                                 // opening html tag
9210                                                 $dom[$key]['opening'] = true;
9211                                                 $dom[$key]['parent'] = end($level);
9212                                                 if (substr($element, -1, 1) != '/') {
9213                                                         // not self-closing tag
9214                                                         array_push($level, $key);
9215                                                         $dom[$key]['self'] = false;
9216                                                 } else {
9217                                                         $dom[$key]['self'] = true;
9218                                                 }
9219                                                 // copy some values from parent
9220                                                 if ($key > 0) {
9221                                                         $dom[$key]['fontname'] = $dom[($dom[$key]['parent'])]['fontname'];
9222                                                         $dom[$key]['fontstyle'] = $dom[($dom[$key]['parent'])]['fontstyle'];
9223                                                         $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'];
9224                                                         $dom[$key]['bgcolor'] = $dom[($dom[$key]['parent'])]['bgcolor'];
9225                                                         $dom[$key]['fgcolor'] = $dom[($dom[$key]['parent'])]['fgcolor'];
9226                                                         $dom[$key]['align'] = $dom[($dom[$key]['parent'])]['align'];
9227                                                 }
9228                                                 // get attributes
9229                                                 preg_match_all('/([^=\s]*)=["\']?([^"\']*)["\']?/', $element, $attr_array, PREG_PATTERN_ORDER);
9230                                                 $dom[$key]['attribute'] = array(); // reset attribute array
9231                                                 while (list($id, $name) = each($attr_array[1])) {
9232                                                         $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
9233                                                 }
9234                                                 // split style attributes
9235                                                 if (isset($dom[$key]['attribute']['style'])) {
9236                                                         // get style attributes
9237                                                         preg_match_all('/([^:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
9238                                                         $dom[$key]['style'] = array(); // reset style attribute array
9239                                                         while (list($id, $name) = each($style_array[1])) {
9240                                                                 $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
9241                                                         }
9242                                                         // --- get some style attributes ---
9243                                                         if (isset($dom[$key]['style']['font-family'])) {
9244                                                                 // font family
9245                                                                 if (isset($dom[$key]['style']['font-family'])) {
9246                                                                         $fontslist = split(",", strtolower($dom[$key]['style']['font-family']));
9247                                                                         foreach($fontslist as $font) {
9248                                                                                 $font = trim(strtolower($font));
9249                                                                                 if (in_array($font, $this->fontlist)){
9250                                                                                         $dom[$key]['fontname'] = $font;
9251                                                                                         break;
9252                                                                                 }
9253                                                                         }
9254                                                                 }
9255                                                         }
9256                                                         // font size
9257                                                         if (isset($dom[$key]['style']['font-size'])) {
9258                                                                 $fsize = trim($dom[$key]['style']['font-size']);
9259                                                                 switch ($fsize) {
9260                                                                         case 'xx-small': {
9261                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
9262                                                                                 break;
9263                                                                         }
9264                                                                         case 'x-small': {
9265                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
9266                                                                                 break;
9267                                                                         }
9268                                                                         case 'small': {
9269                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
9270                                                                                 break;
9271                                                                         }
9272                                                                         case 'medium': {
9273                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'];
9274                                                                                 break;
9275                                                                         }
9276                                                                         case 'large': {
9277                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
9278                                                                                 break;
9279                                                                         }
9280                                                                         case 'x-large': {
9281                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
9282                                                                                 break;
9283                                                                         }
9284                                                                         case 'xx-large': {
9285                                                                                 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
9286                                                                                 break;
9287                                                                         }
9288                                                                         default: {
9289                                                                                 $dom[$key]['fontsize'] = intval($fsize);
9290                                                                         }
9291                                                                 }
9292                                                         }
9293                                                         // font style
9294                                                         $dom[$key]['fontstyle'] = "";
9295                                                         if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == "b")) {
9296                                                                 $dom[$key]['fontstyle'] .= "B";
9297                                                         }
9298                                                         if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == "i")) {
9299                                                                 $dom[$key]['fontstyle'] .= "I";
9300                                                         }
9301                                                         // font color
9302                                                         if (isset($dom[$key]['style']['color']) AND (!empty($dom[$key]['style']['color']))) {
9303                                                                 $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
9304                                                         }
9305                                                         // background color
9306                                                         if (isset($dom[$key]['style']['background-color']) AND (!empty($dom[$key]['style']['background-color']))) {
9307                                                                 $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
9308                                                         }
9309                                                         // text-decoration
9310                                                         if (isset($dom[$key]['style']['text-decoration'])) {
9311                                                                 $decors = explode(" ", strtolower($dom[$key]['style']['text-decoration']));
9312                                                                 foreach ($decors as $dec) {
9313                                                                         $dec = trim($dec);
9314                                                                         if ($dec{0} == "u") {
9315                                                                                 $dom[$key]['fontstyle'] .= "U";
9316                                                                         } elseif ($dec{0} == "l") {
9317                                                                                 $dom[$key]['fontstyle'] .= "D";
9318                                                                         }
9319                                                                 }
9320                                                         }
9321                                                         // check for width attribute
9322                                                         if (isset($dom[$key]['style']['width'])) {
9323                                                                 $dom[$key]['width'] = intval($dom[$key]['style']['width']);
9324                                                         }
9325                                                         // check for height attribute
9326                                                         if (isset($dom[$key]['style']['height'])) {
9327                                                                 $dom[$key]['height'] = intval($dom[$key]['style']['height']);
9328                                                         }
9329                                                         // check for text alignment
9330                                                         if (isset($dom[$key]['style']['text-align'])) {
9331                                                                 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
9332                                                         }
9333                                                 }
9334                                                 // check for font tag
9335                                                 if ($dom[$key]['value'] == "font") {
9336                                                         // font family
9337                                                         if (isset($dom[$key]['attribute']['face'])) {
9338                                                                 $fontslist = split(",", strtolower($dom[$key]['attribute']['face']));
9339                                                                 foreach($fontslist as $font) {
9340                                                                         $font = trim(strtolower($font));
9341                                                                         if (in_array($font, $this->fontlist)){
9342                                                                                 $dom[$key]['fontname'] = $font;
9343                                                                                 break;
9344                                                                         }
9345                                                                 }
9346                                                         }
9347                                                         // font size
9348                                                         if (isset($dom[$key]['attribute']['size'])) {
9349                                                                 if ($key > 0) {
9350                                                                         if ($dom[$key]['attribute']['size']{0} == "+") {
9351                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
9352                                                                         } elseif ($dom[$key]['attribute']['size']{0} == "-") {
9353                                                                                 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
9354                                                                         } else {
9355                                                                                 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9356                                                                         }
9357                                                                 } else {
9358                                                                         $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
9359                                                                 }
9360                                                         }
9361                                                 }
9362                                                 if (($dom[$key]['value'] == "ul") OR ($dom[$key]['value'] == "ol") OR ($dom[$key]['value'] == "dl")) {
9363                                                         // force natural alignment for lists
9364                                                         if ($this->rtl) {
9365                                                                 $dom[$key]['align'] = "R";
9366                                                         } else {
9367                                                                 $dom[$key]['align'] = "L";
9368                                                         }
9369                                                 }
9370                                                 if (($dom[$key]['value'] == "small") OR ($dom[$key]['value'] == "sup") OR ($dom[$key]['value'] == "sub")) {
9371                                                         $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
9372                                                 }
9373                                                 if (($dom[$key]['value'] == "strong") OR ($dom[$key]['value'] == "b")) {
9374                                                         $dom[$key]['fontstyle'] .= "B";
9375                                                 }
9376                                                 if (($dom[$key]['value'] == "em") OR ($dom[$key]['value'] == "i")) {
9377                                                         $dom[$key]['fontstyle'] .= "I";
9378                                                 }
9379                                                 if (($dom[$key]['value']{0} == "h") AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
9380                                                         $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
9381                                                         $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
9382                                                         $dom[$key]['fontstyle'] .= "B";
9383                                                 }
9384                                                 if (($dom[$key]['value'] == "table")) {
9385                                                         $dom[$key]['rows'] = 0; // number of rows
9386                                                         $dom[$key]['trids'] = array(); // IDs of TR elements
9387                                                 }
9388                                                 if (($dom[$key]['value'] == "tr")) {
9389                                                         $dom[$key]['cols'] = 0;
9390                                                         // store the number of rows on table element
9391                                                         $dom[($dom[$key]['parent'])]['rows']++;
9392                                                         // store the TR elements IDs on table element
9393                                                         array_push($dom[($dom[$key]['parent'])]['trids'], $key);
9394                                                 }
9395                                                 if (($dom[$key]['value'] == "th") OR ($dom[$key]['value'] == "td")) {
9396                                                         if (isset($dom[$key]['attribute']['colspan'])) {
9397                                                                 $colspan = intval($dom[$key]['attribute']['colspan']);
9398                                                         } else {
9399                                                                 $colspan = 1;
9400                                                         }
9401                                                         $dom[$key]['attribute']['colspan'] = $colspan;
9402                                                         $dom[($dom[$key]['parent'])]['cols'] += $colspan;
9403                                                 }
9404                                                 // set foreground color attribute
9405                                                 if (isset($dom[$key]['attribute']['color']) AND (!empty($dom[$key]['attribute']['color']))) {
9406                                                         $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
9407                                                 }
9408                                                 // set background color attribute
9409                                                 if (isset($dom[$key]['attribute']['bgcolor']) AND (!empty($dom[$key]['attribute']['bgcolor']))) {
9410                                                         $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
9411                                                 }
9412                                                 // check for width attribute
9413                                                 if (isset($dom[$key]['attribute']['width'])) {
9414                                                         $dom[$key]['width'] = intval($dom[$key]['attribute']['width']);
9415                                                 }
9416                                                 // check for height attribute
9417                                                 if (isset($dom[$key]['attribute']['height'])) {
9418                                                         $dom[$key]['height'] = intval($dom[$key]['attribute']['height']);
9419                                                 }
9420                                                 // check for text alignment
9421                                                 if (isset($dom[$key]['attribute']['align']) AND (!empty($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
9422                                                         $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
9423                                                 }
9424                                         } // end opening tag
9425                                 } else {
9426                                         // text
9427                                         $dom[$key]['tag'] = false;
9428                                         $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
9429                                         $dom[$key]['parent'] = end($level);
9430                                         // calculate text width
9431                                         //$dom[$key]['width'] = $this->GetStringWidth($dom[$key]['value'], $dom[($dom[$key]['parent'])]['fontname'], $dom[($dom[$key]['parent'])]['fontstyle'], $dom[($dom[$key]['parent'])]['fontsize']);
9432                                 }
9433                                 $key++;
9434                         }
9435                         return $dom;
9436                 }
9437
9438                 /**
9439                  * Allows to preserve some HTML formatting (limited support).<br />
9440                  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
9441                  * 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,
9442                  * @param string $html text to display
9443                  * @param boolean $ln if true add a new line after text (default = true)
9444                  * @param int $fill Indicates if the background must be painted (true) or transparent (false).
9445                  * @param boolean $reseth if true reset the last cell height (default false).
9446                  * @param boolean $cell if true add the default cMargin space to each Write (default false).
9447                  * @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>
9448                  */
9449                 function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
9450                         // store current values
9451                         $prevlMargin = $this->lMargin;
9452                         $prevrMargin = $this->rMargin;
9453                         $prevcMargin = $this->cMargin;
9454                         $prevFontFamily = $this->FontFamily;
9455                         $prevFontStyle = $this->FontStyle;
9456                         $prevFontSizePt = $this->FontSizePt;
9457                         $curfontname = $prevFontFamily;
9458                         $curfontstyle = $prevFontStyle;
9459                         $curfontsize = $prevFontSizePt;
9460                         $prevbgcolor = $this->bgcolor;
9461                         $prevfgcolor = $this->fgcolor;
9462                         $this->newline = true;
9463                         $minstartliney = $this->y;
9464                         $yshift = 0;
9465                         $startlinepage = $this->page;
9466                         $newline = true;
9467                         if (isset($this->footerlen[$this->page])) {
9468                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9469                         } else {
9470                                 $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9471                         }
9472                         $startlinepos = $this->footerpos[$this->page];
9473                         $lalign = $align;
9474                         $plalign = $align;
9475                         if ($this->rtl) {
9476                                 $w = $this->x - $this->lMargin;
9477                         } else {
9478                                 $w = $this->w - $this->rMargin - $this->x;
9479                         }
9480                         $w -= (2 * $this->cMargin);
9481                         if ($cell) {
9482                                 if ($this->rtl) {
9483                                         $this->x -= $this->cMargin;
9484                                 } else {
9485                                         $this->x += $this->cMargin;
9486                                 }
9487                         }
9488                         $this->listindent = $this->GetStringWidth("0000");
9489                         $this->listnum = 0;
9490                         if ((empty($this->lasth))OR ($reseth)) {
9491                                 //set row height
9492                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9493                         }
9494                         $dom = $this->getHtmlDomArray($html);
9495                         $maxel = count($dom);
9496                         $key = 0;
9497                         while ($key < $maxel) {
9498                                 if ($dom[$key]['tag'] OR ($key == 0)) {
9499                                         if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
9500                                                 $dom[$key]['align'] = ($this->rtl)?'R':'L';
9501                                         }
9502                                         // vertically align image in line
9503                                         if ((!$this->newline) AND ($dom[$key]['value'] == 'img') 
9504                                                 AND (isset($dom[$key]['attribute']['height']))
9505                                                 AND ($dom[$key]['attribute']['height'] > 0)
9506                                                 ) {
9507                                                 $this->y += (($curfontsize / $this->k) - $this->pixelsToUnits($dom[$key]['attribute']['height']));
9508                                                 $minstartliney = min($this->y, $minstartliney);
9509                                         } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
9510                                                 // account for different font size
9511                                                 $pfontname = $curfontname;
9512                                                 $pfontstyle = $curfontstyle;
9513                                                 $pfontsize = $curfontsize;
9514                                                 $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
9515                                                 $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
9516                                                 $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
9517                                                 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
9518                                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9519                                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
9520                                                         if (is_numeric($fontsize) AND ($fontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($fontsize != $curfontsize) AND (!$this->newline)) {
9521                                                                 $this->y += (($curfontsize - $fontsize) / $this->k);
9522                                                                 $minstartliney = min($this->y, $minstartliney);
9523                                                         }
9524                                                         $curfontname = $fontname;
9525                                                         $curfontstyle = $fontstyle;
9526                                                         $curfontsize = $fontsize;
9527                                                 }
9528                                         }
9529                                         if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
9530                                                 $this->SetFillColorArray($dom[$key]['bgcolor']);
9531                                                 $wfill = true;
9532                                         } else {
9533                                                 $wfill = $fill | false;
9534                                         }
9535                                         if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
9536                                                 $this->SetTextColorArray($dom[$key]['fgcolor']);
9537                                         }
9538                                         if (isset($dom[$key]['align'])) {
9539                                                 $lalign = $dom[$key]['align'];
9540                                         }
9541                                         if (empty($lalign)) {
9542                                                 $lalign = $align;
9543                                         }
9544                                 }
9545                                 // align lines
9546                                 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
9547                                         $newline = true;
9548                                         // we are at the beginning of a new line
9549                                         if (isset($startlinex)) {
9550                                                 $yshift = $minstartliney - $startliney;
9551                                                 if ($yshift > 0) {
9552                                                         $yshift = 0;
9553                                                 }
9554                                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9555                                                         // the last line must be shifted to be aligned as requested
9556                                                         $linew = abs($this->endlinex - $startlinex);
9557                                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9558                                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9559                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9560                                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9561                                                         } elseif (isset($opentagpos)) {
9562                                                                 $midpos = $opentagpos;
9563                                                         } elseif (isset($this->footerlen[$startlinepage])) {
9564                                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9565                                                                 $midpos = $this->footerpos[$startlinepage];
9566                                                         } else {
9567                                                                 $midpos = 0;
9568                                                         }
9569                                                         if ($midpos > 0) {
9570                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9571                                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9572                                                         } else {
9573                                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9574                                                                 $pend = "";
9575                                                         }
9576                                                         // calculate shifting amount
9577                                                         $mdiff = abs($w - $linew);
9578                                                         if ($plalign == "C") {
9579                                                                 if ($this->rtl) {
9580                                                                         $t_x = -($mdiff / 2);
9581                                                                 } else {
9582                                                                         $t_x = ($mdiff / 2);
9583                                                                 }
9584                                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9585                                                                 // right alignment on LTR document
9586                                                                 $t_x = $mdiff;
9587                                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9588                                                                 // left alignment on RTL document
9589                                                                 $t_x = -$mdiff;
9590                                                         } else {
9591                                                                 $t_x = 0;
9592                                                         }
9593                                                         if (($t_x != 0) OR ($yshift < 0)) {
9594                                                                 // shift the line
9595                                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9596                                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9597                                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9598                                                                 // shift the annotations and links
9599                                                                 if (isset($this->PageAnnots[$this->page])) {
9600                                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9601                                                                                 if ($pac['y'] >= $minstartliney) {
9602                                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9603                                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9604                                                                                 }
9605                                                                         }
9606                                                                 }
9607                                                                 $this->y -= $yshift;
9608                                                         }
9609                                                 }
9610                                         }
9611                                         $this->checkPageBreak($this->lasth);
9612                                         $this->SetFont($fontname, $fontstyle, $fontsize);
9613                                         if ($wfill) {
9614                                                 $this->SetFillColorArray($this->bgcolor);
9615                                         }
9616                                         $startlinex = $this->x;
9617                                         $startliney = $this->y;
9618                                         $minstartliney = $this->y;
9619                                         $startlinepage = $this->page;
9620                                         if (isset($endlinepos)) {
9621                                                 $startlinepos = $endlinepos;
9622                                                 unset($endlinepos);
9623                                         } else {
9624                                                 if (isset($this->footerlen[$this->page])) {
9625                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9626                                                 } else {
9627                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9628                                                 }
9629                                                 $startlinepos = $this->footerpos[$this->page];
9630                                         }
9631                                         $plalign = $lalign;
9632                                         $this->newline = false;
9633                                 }
9634                                 if (isset($opentagpos)) {
9635                                         unset($opentagpos);
9636                                 }
9637                                 if ($dom[$key]['tag']) {
9638                                         if ($dom[$key]['opening']) {
9639                                                 // table content is handled in a special way
9640                                                 if (($dom[$key]['value'] == "td") OR ($dom[$key]['value'] == "th")) {
9641                                                         $trid = $dom[$key]['parent'];
9642                                                         $table_el = $dom[$trid]['parent'];
9643                                                         if (!isset($dom[$table_el]['cols'])) {
9644                                                                 $dom[$table_el]['cols'] = $trid['cols'];
9645                                                         }
9646                                                         // calculate cell width
9647                                                         if (isset($dom[($dom[$key]['parent'])]['width'])) {
9648                                                                 $table_width = $this->pixelsToUnits($dom[($dom[$key]['parent'])]['width']);
9649                                                         } else {
9650                                                                 $table_width = $w;
9651                                                         }
9652                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
9653                                                                 $currentcmargin = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding']);
9654                                                         } else {
9655                                                                 $currentcmargin = 0;
9656                                                         }
9657                                                         $this->cMargin = $currentcmargin;
9658                                                         if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
9659                                                                 $cellspacing = $this->pixelsToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing']);
9660                                                         } else {
9661                                                                 $cellspacing = 0;
9662                                                         }
9663                                                         if ($this->rtl) {
9664                                                                 $cellspacingx = -$cellspacing;
9665                                                         } else {
9666                                                                 $cellspacingx = $cellspacing;
9667                                                         }
9668                                                         $colspan = $dom[$key]['attribute']['colspan'];
9669                                                         if (isset($dom[$key]['width'])) {
9670                                                                 $cellw = $this->pixelsToUnits($dom[$key]['width']);
9671                                                         } else {
9672                                                                 $cellw = ($colspan * ($table_width / $dom[$table_el]['cols']));
9673                                                         }
9674                                                         $cellw -= $cellspacing;
9675                                                         if (isset($dom[$key]['content'])) {
9676                                                                 $cell_content = $dom[$key]['content'];
9677                                                         } else {
9678                                                                 $cell_content = "&nbsp;";
9679                                                         }
9680                                                         $tagtype = $dom[$key]['value'];
9681                                                         $parentid = $key;
9682                                                         while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
9683                                                                 // move $key index forward
9684                                                                 $key++;
9685                                                         }
9686                                                         if (!isset($dom[$trid]['startpage'])) {
9687                                                                 $dom[$trid]['startpage'] = $this->page;
9688                                                         } else {
9689                                                                 $this->setPage($dom[$trid]['startpage']);
9690                                                         }
9691                                                         if (!isset($dom[$trid]['starty'])) {
9692                                                                 $dom[$trid]['starty'] = $this->y;
9693                                                         } else {
9694                                                                 $this->y = $dom[$trid]['starty'];
9695                                                         }
9696                                                         if (!isset($dom[$trid]['startx'])) {
9697                                                                 $dom[$trid]['startx'] = $this->x;
9698                                                         }
9699                                                         $this->x += ($cellspacingx / 2);
9700                                                         if (isset($dom[$parentid]['attribute']['rowspan'])) {
9701                                                                 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
9702                                                         }       else {
9703                                                                 $rowspan = 1;
9704                                                         }
9705                                                         // skip row-spanned cells started on the previous rows
9706                                                         if (isset($dom[$table_el]['rowspans'])) {
9707                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9708                                                                         if  (($trwsp['startx'] == $this->x) AND (($trwsp['starty'] < $this->y) OR ($trwsp['startpage'] < $this->page)) AND ($trwsp['rowspan'] > 0)) {
9709                                                                                 $this->x = $trwsp['endx'] + $cellspacingx;
9710                                                                         }
9711                                                                 }
9712                                                         }
9713                                                         // add rowspan information to table element
9714                                                         if ($rowspan > 1) {
9715                                                                 if (isset($this->footerlen[$this->page])) {
9716                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9717                                                                 } else {
9718                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9719                                                                 }
9720                                                                 $trintmrkpos = $this->footerpos[$this->page];
9721                                                                 $trsid = array_push($dom[$table_el]['rowspans'], array('rowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y, 'intmrkpos' => $trintmrkpos));
9722                                                         }
9723                                                         $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
9724                                                         if ($rowspan > 1) {
9725                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
9726                                                         }
9727                                                         // push background colors
9728                                                         if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
9729                                                                 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
9730                                                         }
9731
9732                                                         // write the cell content
9733                                                         $this->MultiCell($cellw, 0, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
9734
9735                                                         $this->cMargin = $currentcmargin;
9736                                                         $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
9737                                                         
9738                                                         // update the end of row position
9739                                                         if ($rowspan <= 1) {
9740                                                                 if (isset($dom[$trid]['endy'])) {
9741                                                                         if ($this->page == $dom[$trid]['endpage']) {
9742                                                                                 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
9743                                                                         } elseif ($this->page > $dom[$trid]['endpage']) {
9744                                                                                 $dom[$trid]['endy'] = $this->y;
9745                                                                         }
9746                                                                 } else {
9747                                                                         $dom[$trid]['endy'] = $this->y;
9748                                                                 }
9749                                                                 if (isset($dom[$trid]['endpage'])) {
9750                                                                         $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
9751                                                                 } else {
9752                                                                         $dom[$trid]['endpage'] = $this->page;
9753                                                                 }
9754                                                         } else {
9755                                                         // account for row-spanned cells
9756                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
9757                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
9758                                                                 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
9759                                                         }
9760                                                         if (isset($dom[$table_el]['rowspans'])) {
9761                                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
9762                                                                         if ($trwsp['rowspan'] > 0) {
9763                                                                                 if (isset($dom[$trid]['endpage'])) {
9764                                                                                         if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
9765                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
9766                                                                                         } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[$trid]['endpage']) {
9767                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $trwsp['endy'];
9768                                                                                         } else {
9769                                                                                                 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
9770                                                                                                 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
9771                                                                                         }
9772                                                                                 }
9773                                                                         }
9774                                                                 }
9775                                                         }
9776                                                         $this->x += ($cellspacingx / 2);
9777                                                 } else {
9778                                                         // opening tag (or self-closing tag)
9779                                                         if (!isset($opentagpos)) {
9780                                                                 if (isset($this->footerlen[$this->page])) {
9781                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]) - $this->footerlen[$this->page];
9782                                                                 } else {
9783                                                                         $this->footerpos[$this->page] = strlen($this->pages[$this->page]);
9784                                                                 }
9785                                                                 $opentagpos = $this->footerpos[$this->page];
9786                                                         }
9787                                                         $this->openHTMLTagHandler($dom, $key, $cell);
9788                                                 }
9789                                         } else {
9790                                                 // closing tag
9791                                                 $this->closeHTMLTagHandler($dom, $key, $cell);
9792                                         }
9793                                 } elseif (strlen($dom[$key]['value']) > 0) {
9794                                         // print list-item
9795                                         if (!empty($this->lispacer)) {
9796                                                 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
9797                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9798                                                 $minstartliney = $this->y;
9799                                                 $tmpx = $this->x;
9800                                                 $lspace = $this->GetStringWidth($this->lispacer."  ");
9801                                                 if ($this->rtl) {
9802                                                         $this->x += $lspace;
9803                                                 } else {
9804                                                         $this->x -= $lspace;
9805                                                 }
9806                                                 $this->Write($this->lasth, $this->lispacer, '', false, '', false, 0, false);
9807                                                 $this->x = $tmpx;
9808                                                 $this->lispacer = "";
9809                                                 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
9810                                                 $this->lasth = $this->FontSize * $this->cell_height_ratio;
9811                                                 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
9812                                                         $this->y += (($pfontsize - $curfontsize) / $this->k);
9813                                                         $minstartliney = min($this->y, $minstartliney);
9814                                                 }
9815                                         }
9816                                         // text
9817                                         $this->htmlvspace = 0;
9818                                         if ($newline) {
9819                                                 if ($this->rtl OR $this->tmprtl) {
9820                                                         $dom[$key]['value'] = rtrim($dom[$key]['value']);
9821                                                 } else {
9822                                                         $dom[$key]['value'] = ltrim($dom[$key]['value']);
9823                                                 }
9824                                                 $newline = false;
9825                                         }
9826                                         if ($this->HREF) {
9827                                                 // HTML <a> Link
9828                                                 $strrest = $this->addHtmlLink($this->HREF, $dom[$key]['value'], $wfill, true);
9829                                         } else {
9830                                                 $ctmpmargin = $this->cMargin;
9831                                                 $this->cMargin = 0;
9832                                                 // write only until the end of the line and get the rest
9833                                                 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, "", false, 0, true);
9834                                                 $this->cMargin = $ctmpmargin;
9835                                         }
9836                                         if (strlen($strrest) > 0) {
9837                                                 // store the remaining string on the previous $key position
9838                                                 $this->newline = true;
9839                                                 if ($cell) {
9840                                                         if ($this->rtl) {
9841                                                                 $this->x -= $this->cMargin;
9842                                                         } else {
9843                                                                 $this->x += $this->cMargin;
9844                                                         }
9845                                                 }
9846                                                 $dom[$key]['value'] = ltrim($strrest);
9847                                                 $key--;
9848                                         }
9849                                 }
9850                                 $key++;
9851                         } // end for each $key
9852                         // align the last line
9853                         if (isset($startlinex)) {
9854                                 $yshift = $minstartliney - $startliney;
9855                                 if ($yshift > 0) {
9856                                         $yshift = 0;
9857                                 }
9858                                 if ((isset($plalign) AND ((($plalign == "C") OR (($plalign == "R") AND (!$this->rtl)) OR (($plalign == "L") AND ($this->rtl))))) OR ($yshift < 0)){
9859                                         // the last line must be shifted to be aligned as requested
9860                                         $linew = abs($this->endlinex - $startlinex);
9861                                         $pstart = substr($this->pages[$startlinepage], 0, $startlinepos);
9862                                         if (isset($opentagpos) AND isset($this->footerlen[$startlinepage])) {
9863                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9864                                                 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
9865                                         } elseif (isset($opentagpos)) {
9866                                                 $midpos = $opentagpos;
9867                                         } elseif (isset($this->footerlen[$startlinepage])) {
9868                                                 $this->footerpos[$startlinepage] = strlen($this->pages[$startlinepage]) - $this->footerlen[$startlinepage];
9869                                                 $midpos = $this->footerpos[$startlinepage];
9870                                         } else {
9871                                                 $midpos = 0;
9872                                         }
9873                                         if ($midpos > 0) {
9874                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos, ($midpos - $startlinepos));
9875                                                 $pend = substr($this->pages[$startlinepage], $midpos);
9876                                         } else {
9877                                                 $pmid = substr($this->pages[$startlinepage], $startlinepos);
9878                                                 $pend = "";
9879                                         }
9880                                         // calculate shifting amount
9881                                         $mdiff = abs($w - $linew);
9882                                         if ($plalign == "C") {
9883                                                 if ($this->rtl) {
9884                                                         $t_x = -($mdiff / 2);
9885                                                 } else {
9886                                                         $t_x = ($mdiff / 2);
9887                                                 }
9888                                         }       elseif (($plalign == "R") AND (!$this->rtl)) {
9889                                                 // right alignment on LTR document
9890                                                 $t_x = $mdiff;
9891                                         }       elseif (($plalign == "L") AND ($this->rtl)) {
9892                                                 // left alignment on RTL document
9893                                                 $t_x = -$mdiff;
9894                                         } else {
9895                                                 $t_x = 0;
9896                                         }
9897                                         if (($t_x != 0) OR ($yshift < 0)) {
9898                                                 // shift the line
9899                                                 $trx = sprintf('1 0 0 1 %.3f %.3f cm', ($t_x * $this->k), ($yshift * $this->k));
9900                                                 $this->pages[$startlinepage] = $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend;
9901                                                 $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
9902                                                 // shift the annotations and links
9903                                                 if (isset($this->PageAnnots[$this->page])) {
9904                                                         foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
9905                                                                 if ($pac['y'] >= $minstartliney) {
9906                                                                         $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
9907                                                                         $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
9908                                                                 }
9909                                                         }
9910                                                 }
9911                                                 $this->y -= $yshift;
9912                                         }
9913                                 }
9914                         }
9915                         if ($ln AND (!($cell AND ($dom[$key-1]['value'] == "table")))) {
9916                                 $this->Ln($this->lasth);
9917                         }
9918                         // restore previous values
9919                         $this->SetFont($prevFontFamily, $prevFontStyle, $prevFontSizePt);
9920                         $this->SetFillColorArray($prevbgcolor);
9921                         $this->SetTextColorArray($prevfgcolor);
9922                         $this->lMargin = $prevlMargin;
9923                         $this->rMargin = $prevrMargin;
9924                         $this->cMargin = $prevcMargin;
9925                         unset($dom);
9926                 }
9927
9928                 /**
9929                  * Process opening tags.
9930                  * @param array $dom html dom array
9931                  * @param int $key current element id
9932                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
9933                  * @access protected
9934                  */
9935                 function openHTMLTagHandler(&$dom, $key, $cell=false) {
9936                         $tag = $dom[$key];
9937                         $parent = $dom[($dom[$key]['parent'])];
9938                         // check for text direction attribute
9939                         if (isset($tag['attribute']['dir'])) {
9940                                 $this->tmprtl = $tag['attribute']['dir'] == 'rtl' ? 'R' : 'L';
9941                         } else {
9942                                 $this->tmprtl = false;
9943                         }
9944                         //Opening tag
9945                         switch($tag['value']) {
9946                                 case 'table': {
9947                                         $dom[$key]['rowspans'] = array();
9948                                         if (isset($tag['attribute']['cellpadding'])) {
9949                                                 $this->oldcMargin = $this->cMargin;
9950                                                 $this->cMargin = $this->pixelsToUnits($tag['attribute']['cellpadding']);
9951                                         }
9952                                         break;
9953                                 }
9954                                 case 'tr': {
9955                                         // array of columns positions
9956                                         $dom[$key]['cellpos'] = array();
9957                                         break;
9958                                 }
9959                                 case 'td':
9960                                 case 'th': {
9961                                         break;
9962                                 }
9963                                 case 'hr': {
9964                                         $this->addHTMLVertSpace(1, $cell);
9965                                         $this->htmlvspace = 0;
9966                                         if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
9967                                                 $hrWidth = $this->pixelsToUnits($tag['attribute']['width']);
9968                                         } else {
9969                                                 $hrWidth = $this->w - $this->lMargin - $this->rMargin;
9970                                         }
9971                                         $x = $this->GetX();
9972                                         $y = $this->GetY();
9973                                         $prevlinewidth = $this->GetLineWidth();
9974                                         $this->Line($x, $y, $x + $hrWidth, $y);
9975                                         $this->SetLineWidth($prevlinewidth);
9976                                         $this->addHTMLVertSpace(1, $cell);
9977                                         break;
9978                                 }
9979                                 case 'b': {
9980                                         $this->setStyle('b', true);
9981                                         break;
9982                                 }
9983                                 case 'i': {
9984                                         $this->setStyle('i', true);
9985                                         break;
9986                                 }
9987                                 case 'u': {
9988                                         $this->setStyle('u', true);
9989                                         break;
9990                                 }
9991                                 case 'del': {
9992                                         $this->setStyle('d', true);
9993                                         break;
9994                                 }
9995                                 case 'a': {
9996                                         if (array_key_exists('href', $tag['attribute'])) {
9997                                                 $this->HREF = $tag['attribute']['href'];
9998                                         }
9999                                         break;
10000                                 }
10001                                 case 'img': {
10002                                         if (isset($tag['attribute']['src'])) {
10003                                                 // replace relative path with real server path
10004                                                 if ($tag['attribute']['src'][0] == '/') {
10005                                                         $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
10006                                                 }
10007                                                 $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
10008                                                 if (!isset($tag['attribute']['width'])) {
10009                                                         $tag['attribute']['width'] = 0;
10010                                                 }
10011                                                 if (!isset($tag['attribute']['height'])) {
10012                                                         $tag['attribute']['height'] = 0;
10013                                                 }
10014                                                 //if (!isset($tag['attribute']['align'])) {
10015                                                         // the only alignment supported is "bottom"
10016                                                         // further development is required for other modes.
10017                                                         $tag['attribute']['align'] = 'bottom';
10018                                                 //} 
10019                                                 switch($tag['attribute']['align']) {
10020                                                         case 'top': {
10021                                                                 $align = 'T';
10022                                                                 break;
10023                                                         }
10024                                                         case 'middle': {
10025                                                                 $align = 'M';
10026                                                                 break;
10027                                                         }
10028                                                         case 'bottom': {
10029                                                                 $align = 'B';
10030                                                                 break;
10031                                                         }
10032                                                         default: {
10033                                                                 $align = 'B';
10034                                                                 break;
10035                                                         }
10036                                                 }
10037                                                 $fileinfo = pathinfo($tag['attribute']['src']);
10038                                                 if (isset($fileinfo['extension']) AND (!empty($fileinfo['extension']))) {
10039                                                         $type = strtolower($fileinfo['extension']);
10040                                                 }
10041                                                 $prevy = $this->y;
10042                                                 if (($type == "eps") OR ($type == "ai")) {
10043                                                         $this->ImageEps($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', true, $align);
10044                                                 } else {
10045                                                         $this->Image($tag['attribute']['src'], $this->GetX(), $this->GetY(), $this->pixelsToUnits($tag['attribute']['width']), $this->pixelsToUnits($tag['attribute']['height']), '', '', $align);
10046                                                 }
10047                                                 switch($align) {
10048                                                         case 'T': {
10049                                                                 $this->y = $prevy;
10050                                                                 break;
10051                                                         }
10052                                                         case 'M': {
10053                                                                 $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
10054                                                                 break;
10055                                                         }
10056                                                         case 'B': {
10057                                                                 $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
10058                                                                 break;
10059                                                         }
10060                                                 }
10061                                         }
10062                                         break;
10063                                 }
10064                                 case 'dl': {
10065                                         $this->listnum++;
10066                                         break;
10067                                 }
10068                                 case 'dt': {
10069                                         $this->addHTMLVertSpace(1, $cell);
10070                                         break;
10071                                 }
10072                                 case 'dd': {
10073                                         if ($this->rtl) {
10074                                                 $this->rMargin += $this->listindent;
10075                                         } else {
10076                                                 $this->lMargin += $this->listindent;
10077                                         }
10078                                         $this->addHTMLVertSpace(1, $cell);
10079                                         break;
10080                                 }
10081                                 case 'ul':
10082                                 case 'ol': {
10083                                         $this->listnum++;
10084                                         if ($tag['value'] == "ol") {
10085                                                 $this->listordered[$this->listnum] = true;
10086                                         } else {
10087                                                 $this->listordered[$this->listnum] = false;
10088                                         }
10089                                         $this->listcount[$this->listnum] = 0;
10090                                         if ($this->rtl) {
10091                                                 $this->rMargin += $this->listindent;
10092                                         } else {
10093                                                 $this->lMargin += $this->listindent;
10094                                         }
10095                                         break;
10096                                 }
10097                                 case 'li': {
10098                                         $this->Ln('', $cell);
10099                                         if ($tag['value'] == 'li') {
10100                                                 if ($this->listordered[$this->listnum]) {
10101                                                         if (isset($tag['attribute']['value'])) {
10102                                                                 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
10103                                                         }
10104                                                         $this->listcount[$this->listnum]++;
10105                                                         if ($this->rtl) {
10106                                                                 $this->lispacer = ".".($this->listcount[$this->listnum]);
10107                                                         } else {
10108                                                                 $this->lispacer = ($this->listcount[$this->listnum]).".";
10109                                                         }
10110                                                 } else {
10111                                                         //unordered list symbol
10112                                                         $this->lispacer = "-";
10113                                                 }
10114                                         } else {
10115                                                 $this->lispacer = "";
10116                                         }
10117                                         break;
10118                                 }
10119                                 case 'blockquote': {
10120                                         if ($this->rtl) {
10121                                                 $this->rMargin += $this->listindent;
10122                                         } else {
10123                                                 $this->lMargin += $this->listindent;
10124                                         }
10125                                         $this->addHTMLVertSpace(2, $cell);
10126                                         break;
10127                                 }
10128                                 case 'br': {
10129                                         $this->Ln('', $cell);
10130                                         break;
10131                                 }
10132                                 case 'div': {
10133                                         $this->addHTMLVertSpace(2, $cell);
10134                                         break;
10135                                 }
10136                                 case 'p': {
10137                                         $this->addHTMLVertSpace(2, $cell);
10138                                         break;
10139                                 }
10140                                 case 'sup': {
10141                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
10142                                         break;
10143                                 }
10144                                 case 'sub': {
10145                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
10146                                         break;
10147                                 }
10148                                 case 'h1':
10149                                 case 'h2':
10150                                 case 'h3':
10151                                 case 'h4':
10152                                 case 'h5':
10153                                 case 'h6': {
10154                                         $this->addHTMLVertSpace(1, $cell, ($tag['fontsize'] * 1.5) / $this->k);
10155                                         break;
10156                                 }
10157                                 default: {
10158                                         break;
10159                                 }
10160                         }
10161                 }
10162
10163                 /**
10164                  * Process closing tags.
10165                  * @param array $dom html dom array
10166                  * @param int $key current element id
10167                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10168                  * @access protected
10169                  */
10170                 function closeHTMLTagHandler(&$dom, $key, $cell=false) {
10171                         $tag = $dom[$key];
10172                         $parent = $dom[($dom[$key]['parent'])];
10173                         //Closing tag
10174                         switch($tag['value']) {
10175                                 case 'td':
10176                                 case 'th': {
10177                                         break;
10178                                 }
10179                                 case 'tr': {
10180                                         $table_el = $dom[($dom[$key]['parent'])]['parent'];
10181                                         // update row-spanned cells
10182                                         if (isset($dom[$table_el]['rowspans'])) {
10183                                                 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
10184                                                         $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
10185                                                         if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
10186                                                                 if ($dom[$table_el]['rowspans'][$k]['endpage'] == $dom[($dom[$key]['parent'])]['endpage']) {
10187                                                                         $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
10188                                                                 } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $dom[($dom[$key]['parent'])]['endpage']) {
10189                                                                         $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
10190                                                                         $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
10191                                                                 }
10192                                                         }
10193                                                 }
10194                                         }
10195                                         $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
10196                                         $this->y = $dom[($dom[$key]['parent'])]['endy'];
10197                                         if (isset($dom[$table_el]['attribute']['cellspacing'])) {
10198                                                 $cellspacing = $this->pixelsToUnits($dom[$table_el]['attribute']['cellspacing']);
10199                                                 $this->y += $cellspacing;
10200                                         }                               
10201                                         $this->Ln(0, $cell);
10202                                         $this->x = $dom[($dom[$key]['parent'])]['startx'];
10203                                         break;
10204                                 }
10205                                 case 'table': {
10206                                         // draw borders
10207                                         $table_el = $parent;
10208                                         if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
10209                                                 OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
10210                                                         $border = 1;
10211                                         } else {
10212                                                 $border = 0;
10213                                         }
10214                                         // for each row
10215                                         foreach ($table_el['trids'] as $j => $trkey) {
10216                                                 $parent = $dom[$trkey];
10217                                                 $restspace = $this->getPageHeight() - $this->y - $this->getBreakMargin();
10218                                                 // for each cell on the row
10219                                                 foreach ($parent['cellpos'] as $k => $cellpos) {
10220                                                         if (isset($cellpos['rowspanid'])) {
10221                                                                 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
10222                                                                 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
10223                                                                 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
10224                                                                 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
10225                                                                 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
10226                                                         } else {
10227                                                                 $endy = $parent['endy'];
10228                                                                 $startpage = $parent['startpage'];
10229                                                                 $endpage = $parent['endpage'];
10230                                                         }
10231                                                         $this->setPage($startpage);
10232                                                         $this->y = $parent['starty'];
10233                                                         if ($endpage > $startpage) {
10234                                                                 // design borders around HTML cells.
10235                                                                 for ($page=$startpage; $page <= $endpage; $page++) {
10236                                                                         $this->setPage($page);
10237                                                                         if ($page == $startpage) {
10238                                                                                 $this->y = $this->getPageHeight() - $restspace - $this->getBreakMargin();
10239                                                                                 $ch = $restspace;
10240                                                                         } elseif ($page == $endpage) {
10241                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10242                                                                                 $ch = $endy - $this->tMargin;
10243                                                                         } else {
10244                                                                                 $this->y = $this->tMargin; // put cursor at the beginning of text
10245                                                                                 $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
10246                                                                         }
10247
10248                                                                         if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10249                                                                                 $this->SetFillColorArray($cellpos['bgcolor']);
10250                                                                                 $fill = true;
10251                                                                         } else {
10252                                                                                 $fill = false;
10253                                                                         }
10254                                                                         $cw = abs($cellpos['endx'] - $cellpos['startx']);
10255                                                                         $this->x = $cellpos['startx'];
10256                                                                         // design a cell around the text
10257                                                                         $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10258                                                                         $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10259                                                                         $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10260                                                                         $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10261                                                                         $this->intmrk[$this->page] += strlen($ccode."\n");
10262                                                                 }
10263                                                         } else {
10264                                                                 $ch = $endy - $parent['starty'];
10265                                                                 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
10266                                                                         $this->SetFillColorArray($cellpos['bgcolor']);
10267                                                                         $fill = true;
10268                                                                 } else {
10269                                                                         $fill = false;
10270                                                                 }
10271                                                                 $cw = abs($cellpos['endx'] - $cellpos['startx']);
10272                                                                 $this->x = $cellpos['startx'];
10273                                                                 $this->y = $parent['starty'];
10274                                                                 // design a cell around the text
10275                                                                 $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, "", $border, 1, '', $fill);
10276                                                                 $pstart = substr($this->pages[$this->page], 0, $this->intmrk[$this->page]);
10277                                                                 $pend = substr($this->pages[$this->page], $this->intmrk[$this->page]);
10278                                                                 $this->pages[$this->page] = $pstart.$ccode."\n".$pend;
10279                                                                 $this->intmrk[$this->page] += strlen($ccode."\n");
10280                                                         }
10281                                                 }
10282                                                 if (isset($table_el['attribute']['cellspacing'])) {
10283                                                         $cellspacing = $this->pixelsToUnits($table_el['attribute']['cellspacing']);
10284                                                         $this->y += $cellspacing;
10285                                                 }
10286                                                 $this->Ln(0, $cell);
10287                                                 $this->x = $parent['startx'];
10288                                         }
10289                                         if (isset($parent['cellpadding'])) {
10290                                                 $this->cMargin = $this->oldcMargin;
10291                                         }
10292                                         //set row height
10293                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10294                                         break;
10295                                 }
10296                                 case 'b': {
10297                                         $this->setStyle('b', false);
10298                                         break;
10299                                 }
10300                                 case 'i': {
10301                                         $this->setStyle('i', false);
10302                                         break;
10303                                 }
10304                                 case 'u': {
10305                                         $this->setStyle('u', false);
10306                                         break;
10307                                 }
10308                                 case 'del': {
10309                                         $this->setStyle('d', false);
10310                                         break;
10311                                 }
10312                                 case 'a': {
10313                                         $this->HREF = '';
10314                                         break;
10315                                 }
10316                                 case 'sup': {
10317                                         $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
10318                                         break;
10319                                 }
10320                                 case 'sub': {
10321                                         $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
10322                                         break;
10323                                 }
10324                                 case 'div': {
10325                                         $this->addHTMLVertSpace(1, $cell);
10326                                         break;
10327                                 }
10328                                 case 'blockquote': {
10329                                         if ($this->rtl) {
10330                                                 $this->rMargin -= $this->listindent;
10331                                         } else {
10332                                                 $this->lMargin -= $this->listindent;
10333                                         }
10334                                         $this->addHTMLVertSpace(2, $cell);
10335                                         break;
10336                                 }
10337                                 case 'p': {
10338                                         $this->addHTMLVertSpace(2, $cell);
10339                                         break;
10340                                 }
10341                                 case 'dl': {
10342                                         $this->listnum--;
10343                                         if ($this->listnum <= 0) {
10344                                                 $this->listnum = 0;
10345                                                 $this->addHTMLVertSpace(2, $cell);
10346                                         }
10347                                         break;
10348                                 }
10349                                 case 'dt': {
10350                                         $this->lispacer = "";
10351                                         break;
10352                                 }
10353                                 case 'dd': {
10354                                         $this->lispacer = "";
10355                                         if ($this->rtl) {
10356                                                 $this->rMargin -= $this->listindent;
10357                                         } else {
10358                                                 $this->lMargin -= $this->listindent;
10359                                         }
10360                                         break;
10361                                 }
10362                                 case 'ul':
10363                                 case 'ol': {
10364                                         $this->listnum--;
10365                                         $this->lispacer = "";
10366                                         if ($this->rtl) {
10367                                                 $this->rMargin -= $this->listindent;
10368                                         } else {
10369                                                 $this->lMargin -= $this->listindent;
10370                                         }
10371                                         if ($this->listnum <= 0) {
10372                                                 $this->listnum = 0;
10373                                                 $this->addHTMLVertSpace(2, $cell);
10374                                         }
10375                                         $this->lasth = $this->FontSize * $this->cell_height_ratio;
10376                                         break;
10377                                 }
10378                                 case 'li': {
10379                                         $this->lispacer = "";
10380                                         break;
10381                                 }
10382                                 case 'h1':
10383                                 case 'h2':
10384                                 case 'h3':
10385                                 case 'h4':
10386                                 case 'h5':
10387                                 case 'h6': {
10388                                         $this->addHTMLVertSpace(1, $cell, ($parent['fontsize'] * 1.5) / $this->k);
10389                                         break;
10390                                 }
10391                                 default : {
10392                                         break;
10393                                 }
10394                         }
10395                         $this->tmprtl = false;
10396                 }
10397                 
10398                 /**
10399                  * Add vertical spaces if needed.
10400                  * @param int $n number of spaces to add
10401                  * @param boolean $cell if true add the default cMargin space to each new line (default false).
10402                  * @param string $h The height of the break. By default, the value equals the height of the last printed cell.
10403                  * @access protected
10404                  */
10405                 function addHTMLVertSpace($n, $cell=false, $h='') {
10406                         if (is_string($h)) {
10407                                 $vsize = $n * $this->lasth;
10408                         } else {
10409                                 $vsize = $n * $h;
10410                         }
10411                         if ($vsize > $this->htmlvspace) {
10412                                 $this->Ln(($vsize - $this->htmlvspace), $cell);
10413                                 $this->htmlvspace = $vsize;
10414             }
10415         }
10416                 
10417         } // END OF TCPDF CLASS
10418 }
10419 //============================================================+
10420 // END OF FILE
10421 //============================================================+
10422 ?>