[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/se3master/var/www/se3/html2pdf/_tcpdf_5.0.002/ -> tcpdf.php (source)

   1  <?php
   2  //============================================================+
   3  // File name   : tcpdf.php
   4  // Begin       : 2002-08-03
   5  // Last Update : 2010-05-06
   6  // Author      : Nicola Asuni - info@tecnick.com - http://www.tcpdf.org
   7  // Version     : 5.0.002
   8  // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
   9  //     ----------------------------------------------------------------------------
  10  //  Copyright (C) 2002-2010  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  //  * all ISO page formats, custom page formats, custom margins and units of measure;
  39  //  * UTF-8 Unicode and Right-To-Left languages;
  40  //  * TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;
  41  //  * methods to publish some XHTML code, Javascript and Forms;
  42  //  * images, graphic (geometric figures) and transformation methods;
  43  //  * supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)
  44  //  * 1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, QR-Code;
  45  //  * Grayscale, RGB, CMYK, Spot Colors and Transparencies;
  46  //  * automatic page header and footer management;
  47  //  * document encryption and digital signature certifications;
  48  //  * transactions to UNDO commands;
  49  //  * PDF annotations, including links, text and file attachments;
  50  //  * text rendering modes (fill, stroke and clipping);
  51  //  * multiple columns mode;
  52  //  * bookmarks and table of content;
  53  //  * text hyphenation;
  54  //  * automatic page break, line break and text alignments including justification;
  55  //  * automatic page numbering and page groups;
  56  //  * move and delete pages;
  57  //  * page compression (requires php-zlib extension);
  58  //
  59  // -----------------------------------------------------------
  60  // THANKS TO:
  61  //
  62  // Olivier Plathey (http://www.fpdf.org) for original FPDF.
  63  // Efthimios Mavrogeorgiadis (emavro@yahoo.com) for suggestions on RTL language support.
  64  // Klemen Vodopivec (http://www.fpdf.de/downloads/addons/37/) for Encryption algorithm.
  65  // Warren Sherliker (wsherliker@gmail.com) for better image handling.
  66  // dullus for text Justification.
  67  // Bob Vincent (pillarsdotnet@users.sourceforge.net) for <li> value attribute.
  68  // Patrick Benny for text stretch suggestion on Cell().
  69  // Johannes Güntert for JavaScript support.
  70  // Denis Van Nuffelen for Dynamic Form.
  71  // Jacek Czekaj for multibyte justification
  72  // Anthony Ferrara for the reintroduction of legacy image methods.
  73  // Sourceforge user 1707880 (hucste) for line-trough mode.
  74  // Larry Stanbery for page groups.
  75  // Martin Hall-May for transparency.
  76  // Aaron C. Spike for Polycurve method.
  77  // Mohamad Ali Golkar, Saleh AlMatrafe, Charles Abbott for Arabic and Persian support.
  78  // Moritz Wagner and Andreas Wurmser for graphic functions.
  79  // Andrew Whitehead for core fonts support.
  80  // Esteban Joël Marín for OpenType font conversion.
  81  // Teus Hagen for several suggestions and fixes.
  82  // Yukihiro Nakadaira for CID-0 CJK fonts fixes.
  83  // Kosmas Papachristos for some CSS improvements.
  84  // Marcel Partap for some fixes.
  85  // Won Kyu Park for several suggestions, fixes and patches.
  86  // Dominik Dzienia for QR-code support.
  87  // Laurent Minguet for some suggestions.
  88  // Anyone that has reported a bug or sent a suggestion.
  89  //============================================================+
  90  
  91  /**
  92   * This is a PHP class for generating PDF documents without requiring external extensions.<br>
  93   * 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>
  94   * <h3>TCPDF main features are:</h3>
  95   * <ul>
  96   * <li>no external libraries are required for the basic functions;</li>
  97   * <li>all ISO page formats, custom page formats, custom margins and units of measure;</li>
  98   * <li>UTF-8 Unicode and Right-To-Left languages;</li>
  99   * <li>TrueTypeUnicode, OpenTypeUnicode, TrueType, OpenType, Type1 and CID-0 fonts;</li>
 100   * <li>methods to publish some XHTML code, Javascript and Forms;</li>
 101   * <li>images, graphic (geometric figures) and transformation methods;
 102   * <li>supports JPEG, PNG and SVG images natively, all images supported by GD (GD, GD2, GD2PART, GIF, JPEG, PNG, BMP, XBM, XPM) and all images supported via ImagMagick (http://www.imagemagick.org/www/formats.html)</li>
 103   * <li>1D and 2D barcodes: CODE 39, ANSI MH10.8M-1983, USD-3, 3 of 9, CODE 93, USS-93, Standard 2 of 5, Interleaved 2 of 5, CODE 128 A/B/C, 2 and 5 Digits UPC-Based Extention, EAN 8, EAN 13, UPC-A, UPC-E, MSI, POSTNET, PLANET, RMS4CC (Royal Mail 4-state Customer Code), CBC (Customer Bar Code), KIX (Klant index - Customer index), Intelligent Mail Barcode, Onecode, USPS-B-3200, CODABAR, CODE 11, PHARMACODE, PHARMACODE TWO-TRACKS, QR-Code;</li>
 104   * <li>Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
 105   * <li>automatic page header and footer management;</li>
 106   * <li>document encryption and digital signature certifications;</li>
 107   * <li>transactions to UNDO commands;</li>
 108   * <li>PDF annotations, including links, text and file attachments;</li>
 109   * <li>text rendering modes (fill, stroke and clipping);</li>
 110   * <li>multiple columns mode;</li>
 111   * <li>bookmarks and table of content;</li>
 112   * <li>text hyphenation;</li>
 113   * <li>automatic page break, line break and text alignments including justification;</li>
 114   * <li>automatic page numbering and page groups;</li>
 115   * <li>move and delete pages;</li>
 116   * <li>page compression (requires php-zlib extension);</li>
 117   * </ul>
 118   * Tools to encode your unicode fonts are on fonts/utils directory.</p>
 119   * @package com.tecnick.tcpdf
 120   * @abstract Class for generating PDF files on-the-fly without requiring external extensions.
 121   * @author Nicola Asuni
 122   * @copyright 2002-2010 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
 123   * @link http://www.tcpdf.org
 124   * @license http://www.gnu.org/copyleft/lesser.html LGPL
 125   * @version 5.0.002
 126   */
 127  
 128  /**
 129   * main configuration file
 130   */
 131  require_once(dirname(__FILE__).'/config/tcpdf_config.php');
 132  
 133  // includes some support files
 134  
 135  /**
 136   * unicode data
 137   */
 138  require_once(dirname(__FILE__).'/unicode_data.php');
 139  
 140  /**
 141   * html colors table
 142   */
 143  require_once(dirname(__FILE__).'/htmlcolors.php');
 144  
 145  if (!class_exists('TCPDF', false)) {
 146      /**
 147       * define default PDF document producer
 148       */
 149      define('PDF_PRODUCER', 'TCPDF 5.0.002 (http://www.tcpdf.org)');
 150  
 151      /**
 152      * This is a PHP class for generating PDF documents without requiring external extensions.<br>
 153      * 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>
 154      * @name TCPDF
 155      * @package com.tecnick.tcpdf
 156      * @version 5.0.002
 157      * @author Nicola Asuni - info@tecnick.com
 158      * @link http://www.tcpdf.org
 159      * @license http://www.gnu.org/copyleft/lesser.html LGPL
 160      */
 161      class TCPDF {
 162  
 163          // protected or Protected properties
 164  
 165          /**
 166           * @var current page number
 167           * @access protected
 168           */
 169          protected $page;
 170  
 171          /**
 172           * @var current object number
 173           * @access protected
 174           */
 175          protected $n;
 176  
 177          /**
 178           * @var array of object offsets
 179           * @access protected
 180           */
 181          protected $offsets;
 182  
 183          /**
 184           * @var buffer holding in-memory PDF
 185           * @access protected
 186           */
 187          protected $buffer;
 188  
 189          /**
 190           * @var array containing pages
 191           * @access protected
 192           */
 193          protected $pages = array();
 194  
 195          /**
 196           * @var current document state
 197           * @access protected
 198           */
 199          protected $state;
 200  
 201          /**
 202           * @var compression flag
 203           * @access protected
 204           */
 205          protected $compress;
 206  
 207          /**
 208           * @var current page orientation (P = Portrait, L = Landscape)
 209           * @access protected
 210           */
 211          protected $CurOrientation;
 212  
 213          /**
 214           * @var array that stores page dimensions and graphic status.<ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => 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><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>
 215           * @access protected
 216           */
 217          protected $pagedim = array();
 218  
 219          /**
 220           * @var scale factor (number of points in user unit)
 221           * @access protected
 222           */
 223          protected $k;
 224  
 225          /**
 226           * @var width of page format in points
 227           * @access protected
 228           */
 229          protected $fwPt;
 230  
 231          /**
 232           * @var height of page format in points
 233           * @access protected
 234           */
 235          protected $fhPt;
 236  
 237          /**
 238           * @var current width of page in points
 239           * @access protected
 240           */
 241          protected $wPt;
 242  
 243          /**
 244           * @var current height of page in points
 245           * @access protected
 246           */
 247          protected $hPt;
 248  
 249          /**
 250           * @var current width of page in user unit
 251           * @access protected
 252           */
 253          protected $w;
 254  
 255          /**
 256           * @var current height of page in user unit
 257           * @access protected
 258           */
 259          protected $h;
 260  
 261          /**
 262           * @var left margin
 263           * @access protected
 264           */
 265          protected $lMargin;
 266  
 267          /**
 268           * @var top margin
 269           * @access protected
 270           */
 271          protected $tMargin;
 272  
 273          /**
 274           * @var right margin
 275           * @access protected
 276           */
 277          protected $rMargin;
 278  
 279          /**
 280           * @var page break margin
 281           * @access protected
 282           */
 283          protected $bMargin;
 284  
 285          /**
 286           * @var cell internal padding
 287           * @access protected
 288           */
 289          //protected
 290          public $cMargin;
 291  
 292          /**
 293           * @var cell internal padding (previous value)
 294           * @access protected
 295           */
 296          protected $oldcMargin;
 297  
 298          /**
 299           * @var current horizontal position in user unit for cell positioning
 300           * @access protected
 301           */
 302          protected $x;
 303  
 304          /**
 305           * @var current vertical position in user unit for cell positioning
 306           * @access protected
 307           */
 308          protected $y;
 309  
 310          /**
 311           * @var height of last cell printed
 312           * @access protected
 313           */
 314          protected $lasth;
 315  
 316          /**
 317           * @var line width in user unit
 318           * @access protected
 319           */
 320          protected $LineWidth;
 321  
 322          /**
 323           * @var array of standard font names
 324           * @access protected
 325           */
 326          protected $CoreFonts;
 327  
 328          /**
 329           * @var array of used fonts
 330           * @access protected
 331           */
 332          protected $fonts = array();
 333  
 334          /**
 335           * @var array of font files
 336           * @access protected
 337           */
 338          protected $FontFiles = array();
 339  
 340          /**
 341           * @var array of encoding differences
 342           * @access protected
 343           */
 344          protected $diffs = array();
 345  
 346          /**
 347           * @var array of used images
 348           * @access protected
 349           */
 350          protected $images = array();
 351  
 352          /**
 353           * @var array of Annotations in pages
 354           * @access protected
 355           */
 356          protected $PageAnnots = array();
 357  
 358          /**
 359           * @var array of internal links
 360           * @access protected
 361           */
 362          protected $links = array();
 363  
 364          /**
 365           * @var current font family
 366           * @access protected
 367           */
 368          protected $FontFamily;
 369  
 370          /**
 371           * @var current font style
 372           * @access protected
 373           */
 374          protected $FontStyle;
 375  
 376          /**
 377           * @var current font ascent (distance between font top and baseline)
 378           * @access protected
 379           * @since 2.8.000 (2007-03-29)
 380           */
 381          protected $FontAscent;
 382  
 383          /**
 384           * @var current font descent (distance between font bottom and baseline)
 385           * @access protected
 386           * @since 2.8.000 (2007-03-29)
 387           */
 388          protected $FontDescent;
 389  
 390          /**
 391           * @var underlining flag
 392           * @access protected
 393           */
 394          protected $underline;
 395  
 396          /**
 397           * @var overlining flag
 398           * @access protected
 399           */
 400          protected $overline;
 401  
 402          /**
 403           * @var current font info
 404           * @access protected
 405           */
 406          protected $CurrentFont;
 407  
 408          /**
 409           * @var current font size in points
 410           * @access protected
 411           */
 412          protected $FontSizePt;
 413  
 414          /**
 415           * @var current font size in user unit
 416           * @access protected
 417           */
 418          protected $FontSize;
 419  
 420          /**
 421           * @var commands for drawing color
 422           * @access protected
 423           */
 424          protected $DrawColor;
 425  
 426          /**
 427           * @var commands for filling color
 428           * @access protected
 429           */
 430          protected $FillColor;
 431  
 432          /**
 433           * @var commands for text color
 434           * @access protected
 435           */
 436          protected $TextColor;
 437  
 438          /**
 439           * @var indicates whether fill and text colors are different
 440           * @access protected
 441           */
 442          protected $ColorFlag;
 443  
 444          /**
 445           * @var automatic page breaking
 446           * @access protected
 447           */
 448          protected $AutoPageBreak;
 449  
 450          /**
 451           * @var threshold used to trigger page breaks
 452           * @access protected
 453           */
 454          protected $PageBreakTrigger;
 455  
 456          /**
 457           * @var flag set when processing footer
 458           * @access protected
 459           */
 460          protected $InFooter = false;
 461  
 462          /**
 463           * @var zoom display mode
 464           * @access protected
 465           */
 466          protected $ZoomMode;
 467  
 468          /**
 469           * @var layout display mode
 470           * @access protected
 471           */
 472          protected $LayoutMode;
 473  
 474          /**
 475           * @var title
 476           * @access protected
 477           */
 478          protected $title = '';
 479  
 480          /**
 481           * @var subject
 482           * @access protected
 483           */
 484          protected $subject = '';
 485  
 486          /**
 487           * @var author
 488           * @access protected
 489           */
 490          protected $author = '';
 491  
 492          /**
 493           * @var keywords
 494           * @access protected
 495           */
 496          protected $keywords = '';
 497  
 498          /**
 499           * @var creator
 500           * @access protected
 501           */
 502          protected $creator = '';
 503  
 504          /**
 505           * @var alias for total number of pages
 506           * @access protected
 507           */
 508          protected $AliasNbPages = '{nb}';
 509  
 510          /**
 511           * @var alias for page number
 512           * @access protected
 513           */
 514          protected $AliasNumPage = '{pnb}';
 515  
 516          /**
 517           * @var right-bottom corner X coordinate of inserted image
 518           * @since 2002-07-31
 519           * @author Nicola Asuni
 520           * @access protected
 521           */
 522          protected $img_rb_x;
 523  
 524          /**
 525           * @var right-bottom corner Y coordinate of inserted image
 526           * @since 2002-07-31
 527           * @author Nicola Asuni
 528           * @access protected
 529           */
 530          protected $img_rb_y;
 531  
 532          /**
 533           * @var adjusting factor to convert pixels to user units.
 534           * @since 2004-06-14
 535           * @author Nicola Asuni
 536           * @access protected
 537           */
 538          protected $imgscale = 1;
 539  
 540          /**
 541           * @var boolean set to true when the input text is unicode (require unicode fonts)
 542           * @since 2005-01-02
 543           * @author Nicola Asuni
 544           * @access protected
 545           */
 546          protected $isunicode = false;
 547  
 548          /**
 549           * @var PDF version
 550           * @since 1.5.3
 551           * @access protected
 552           */
 553          protected $PDFVersion = '1.7';
 554  
 555          /**
 556           * @var Minimum distance between header and top page margin.
 557           * @access protected
 558           */
 559          protected $header_margin;
 560  
 561          /**
 562           * @var Minimum distance between footer and bottom page margin.
 563           * @access protected
 564           */
 565          protected $footer_margin;
 566  
 567          /**
 568           * @var original left margin value
 569           * @access protected
 570           * @since 1.53.0.TC013
 571           */
 572          protected $original_lMargin;
 573  
 574          /**
 575           * @var original right margin value
 576           * @access protected
 577           * @since 1.53.0.TC013
 578           */
 579          protected $original_rMargin;
 580  
 581          /**
 582           * @var Header font.
 583           * @access protected
 584           */
 585          protected $header_font;
 586  
 587          /**
 588           * @var Footer font.
 589           * @access protected
 590           */
 591          protected $footer_font;
 592  
 593          /**
 594           * @var Language templates.
 595           * @access protected
 596           */
 597          protected $l;
 598  
 599          /**
 600           * @var Barcode to print on page footer (only if set).
 601           * @access protected
 602           */
 603          protected $barcode = false;
 604  
 605          /**
 606           * @var If true prints header
 607           * @access protected
 608           */
 609          protected $print_header = true;
 610  
 611          /**
 612           * @var If true prints footer.
 613           * @access protected
 614           */
 615          protected $print_footer = true;
 616  
 617          /**
 618           * @var Header image logo.
 619           * @access protected
 620           */
 621          protected $header_logo = '';
 622  
 623          /**
 624           * @var Header image logo width in mm.
 625           * @access protected
 626           */
 627          protected $header_logo_width = 30;
 628  
 629          /**
 630           * @var String to print as title on document header.
 631           * @access protected
 632           */
 633          protected $header_title = '';
 634  
 635          /**
 636           * @var String to print on document header.
 637           * @access protected
 638           */
 639          protected $header_string = '';
 640  
 641          /**
 642           * @var Default number of columns for html table.
 643           * @access protected
 644           */
 645          protected $default_table_columns = 4;
 646  
 647  
 648          // variables for html parser
 649  
 650          /**
 651           * @var HTML PARSER: array to store current link and rendering styles.
 652           * @access protected
 653           */
 654          protected $HREF = array();
 655  
 656          /**
 657           * @var store a list of available fonts on filesystem.
 658           * @access protected
 659           */
 660          protected $fontlist = array();
 661  
 662          /**
 663           * @var current foreground color
 664           * @access protected
 665           */
 666          protected $fgcolor;
 667  
 668          /**
 669           * @var HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
 670           * @access protected
 671           */
 672          protected $listordered = array();
 673  
 674          /**
 675           * @var HTML PARSER: array count list items on nested lists.
 676           * @access protected
 677           */
 678          protected $listcount = array();
 679  
 680          /**
 681           * @var HTML PARSER: current list nesting level.
 682           * @access protected
 683           */
 684          protected $listnum = 0;
 685  
 686          /**
 687           * @var HTML PARSER: indent amount for lists.
 688           * @access protected
 689           */
 690          protected $listindent = 0;
 691  
 692          /**
 693           * @var HTML PARSER: current list indententation level.
 694           * @access protected
 695           */
 696          protected $listindentlevel = 0;
 697  
 698          /**
 699           * @var current background color
 700           * @access protected
 701           */
 702          protected $bgcolor;
 703  
 704          /**
 705           * @var Store temporary font size in points.
 706           * @access protected
 707           */
 708          protected $tempfontsize = 10;
 709  
 710          /**
 711           * @var spacer for LI tags.
 712           * @access protected
 713           */
 714          protected $lispacer = '';
 715  
 716          /**
 717           * @var default encoding
 718           * @access protected
 719           * @since 1.53.0.TC010
 720           */
 721          protected $encoding = 'UTF-8';
 722  
 723          /**
 724           * @var PHP internal encoding
 725           * @access protected
 726           * @since 1.53.0.TC016
 727           */
 728          protected $internal_encoding;
 729  
 730          /**
 731           * @var indicates if the document language is Right-To-Left
 732           * @access protected
 733           * @since 2.0.000
 734           */
 735          protected $rtl = false;
 736  
 737          /**
 738           * @var used to force RTL or LTR string inversion
 739           * @access protected
 740           * @since 2.0.000
 741           */
 742          protected $tmprtl = false;
 743  
 744          // --- Variables used for document encryption:
 745  
 746          /**
 747           * Indicates whether document is protected
 748           * @access protected
 749           * @since 2.0.000 (2008-01-02)
 750           */
 751          protected $encrypted;
 752  
 753          /**
 754           * U entry in pdf document
 755           * @access protected
 756           * @since 2.0.000 (2008-01-02)
 757           */
 758          protected $Uvalue;
 759  
 760          /**
 761           * O entry in pdf document
 762           * @access protected
 763           * @since 2.0.000 (2008-01-02)
 764           */
 765          protected $Ovalue;
 766  
 767          /**
 768           * P entry in pdf document
 769           * @access protected
 770           * @since 2.0.000 (2008-01-02)
 771           */
 772          protected $Pvalue;
 773  
 774          /**
 775           * encryption object id
 776           * @access protected
 777           * @since 2.0.000 (2008-01-02)
 778           */
 779          protected $enc_obj_id;
 780  
 781          /**
 782           * last RC4 key encrypted (cached for optimisation)
 783           * @access protected
 784           * @since 2.0.000 (2008-01-02)
 785           */
 786          protected $last_rc4_key;
 787  
 788          /**
 789           * last RC4 computed key
 790           * @access protected
 791           * @since 2.0.000 (2008-01-02)
 792           */
 793          protected $last_rc4_key_c;
 794  
 795          /**
 796           * RC4 padding
 797           * @access protected
 798           */
 799          protected $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";
 800  
 801          /**
 802           * RC4 encryption key
 803           * @access protected
 804           */
 805          protected $encryption_key;
 806  
 807          // --- bookmark ---
 808  
 809          /**
 810           * Outlines for bookmark
 811           * @access protected
 812           * @since 2.1.002 (2008-02-12)
 813           */
 814          protected $outlines = array();
 815  
 816          /**
 817           * Outline root for bookmark
 818           * @access protected
 819           * @since 2.1.002 (2008-02-12)
 820           */
 821          protected $OutlineRoot;
 822  
 823  
 824          // --- javascript and form ---
 825  
 826          /**
 827           * javascript code
 828           * @access protected
 829           * @since 2.1.002 (2008-02-12)
 830           */
 831          protected $javascript = '';
 832  
 833          /**
 834           * javascript counter
 835           * @access protected
 836           * @since 2.1.002 (2008-02-12)
 837           */
 838          protected $n_js;
 839  
 840          /**
 841           * line trough state
 842           * @access protected
 843           * @since 2.8.000 (2008-03-19)
 844           */
 845          protected $linethrough;
 846  
 847          // --- Variables used for User's Rights ---
 848          // See PDF reference chapter 8.7 Digital Signatures
 849  
 850          /**
 851           * If true enables user's rights on PDF reader
 852           * @access protected
 853           * @since 2.9.000 (2008-03-26)
 854           */
 855          protected $ur;
 856  
 857          /**
 858           * Names specifying additional document-wide usage rights for the document.
 859           * @access protected
 860           * @since 2.9.000 (2008-03-26)
 861           */
 862          protected $ur_document;
 863  
 864          /**
 865           * Names specifying additional annotation-related usage rights for the document.
 866           * @access protected
 867           * @since 2.9.000 (2008-03-26)
 868           */
 869          protected $ur_annots;
 870  
 871          /**
 872           * Names specifying additional form-field-related usage rights for the document.
 873           * @access protected
 874           * @since 2.9.000 (2008-03-26)
 875           */
 876          protected $ur_form;
 877  
 878          /**
 879           * Names specifying additional signature-related usage rights for the document.
 880           * @access protected
 881           * @since 2.9.000 (2008-03-26)
 882           */
 883          protected $ur_signature;
 884  
 885          /**
 886           * Dot Per Inch Document Resolution (do not change)
 887           * @access protected
 888           * @since 3.0.000 (2008-03-27)
 889           */
 890          protected $dpi = 72;
 891  
 892          /**
 893           * Array of page numbers were a new page group was started
 894           * @access protected
 895           * @since 3.0.000 (2008-03-27)
 896           */
 897          protected $newpagegroup = array();
 898  
 899          /**
 900           * Contains the number of pages of the groups
 901           * @access protected
 902           * @since 3.0.000 (2008-03-27)
 903           */
 904          protected $pagegroups;
 905  
 906          /**
 907           * Contains the alias of the current page group
 908           * @access protected
 909           * @since 3.0.000 (2008-03-27)
 910           */
 911          protected $currpagegroup;
 912  
 913          /**
 914           * Restrict the rendering of some elements to screen or printout.
 915           * @access protected
 916           * @since 3.0.000 (2008-03-27)
 917           */
 918          protected $visibility = 'all';
 919  
 920          /**
 921           * Print visibility.
 922           * @access protected
 923           * @since 3.0.000 (2008-03-27)
 924           */
 925          protected $n_ocg_print;
 926  
 927          /**
 928           * View visibility.
 929           * @access protected
 930           * @since 3.0.000 (2008-03-27)
 931           */
 932          protected $n_ocg_view;
 933  
 934          /**
 935           * Array of transparency objects and parameters.
 936           * @access protected
 937           * @since 3.0.000 (2008-03-27)
 938           */
 939          protected $extgstates;
 940  
 941          /**
 942           * Set the default JPEG compression quality (1-100)
 943           * @access protected
 944           * @since 3.0.000 (2008-03-27)
 945           */
 946          protected $jpeg_quality;
 947  
 948          /**
 949           * Default cell height ratio.
 950           * @access protected
 951           * @since 3.0.014 (2008-05-23)
 952           */
 953          protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
 954  
 955          /**
 956           * PDF viewer preferences.
 957           * @access protected
 958           * @since 3.1.000 (2008-06-09)
 959           */
 960          protected $viewer_preferences;
 961  
 962          /**
 963           * A name object specifying how the document should be displayed when opened.
 964           * @access protected
 965           * @since 3.1.000 (2008-06-09)
 966           */
 967          protected $PageMode;
 968  
 969          /**
 970           * Array for storing gradient information.
 971           * @access protected
 972           * @since 3.1.000 (2008-06-09)
 973           */
 974          protected $gradients = array();
 975  
 976          /**
 977           * Array used to store positions inside the pages buffer.
 978           * keys are the page numbers
 979           * @access protected
 980           * @since 3.2.000 (2008-06-26)
 981           */
 982          protected $intmrk = array();
 983  
 984          /**
 985           * Array used to store content positions inside the pages buffer.
 986           * keys are the page numbers
 987           * @access protected
 988           * @since 4.6.021 (2009-07-20)
 989           */
 990          protected $cntmrk = array();
 991  
 992          /**
 993           * Array used to store footer positions of each page.
 994           * @access protected
 995           * @since 3.2.000 (2008-07-01)
 996           */
 997          protected $footerpos = array();
 998  
 999  
1000          /**
1001           * Array used to store footer length of each page.
1002           * @access protected
1003           * @since 4.0.014 (2008-07-29)
1004           */
1005          protected $footerlen = array();
1006  
1007          /**
1008           * True if a newline is created.
1009           * @access protected
1010           * @since 3.2.000 (2008-07-01)
1011           */
1012          protected $newline = true;
1013  
1014          /**
1015           * End position of the latest inserted line
1016           * @access protected
1017           * @since 3.2.000 (2008-07-01)
1018           */
1019          protected $endlinex = 0;
1020  
1021          /**
1022           * PDF string for last line width
1023           * @access protected
1024           * @since 4.0.006 (2008-07-16)
1025           */
1026          protected $linestyleWidth = '';
1027  
1028          /**
1029           * PDF string for last line width
1030           * @access protected
1031           * @since 4.0.006 (2008-07-16)
1032           */
1033          protected $linestyleCap = '0 J';
1034  
1035          /**
1036           * PDF string for last line width
1037           * @access protected
1038           * @since 4.0.006 (2008-07-16)
1039           */
1040          protected $linestyleJoin = '0 j';
1041  
1042          /**
1043           * PDF string for last line width
1044           * @access protected
1045           * @since 4.0.006 (2008-07-16)
1046           */
1047          protected $linestyleDash = '[] 0 d';
1048  
1049          /**
1050           * True if marked-content sequence is open
1051           * @access protected
1052           * @since 4.0.013 (2008-07-28)
1053           */
1054          protected $openMarkedContent = false;
1055  
1056          /**
1057           * Count the latest inserted vertical spaces on HTML
1058           * @access protected
1059           * @since 4.0.021 (2008-08-24)
1060           */
1061          protected $htmlvspace = 0;
1062  
1063          /**
1064           * Array of Spot colors
1065           * @access protected
1066           * @since 4.0.024 (2008-09-12)
1067           */
1068          protected $spot_colors = array();
1069  
1070          /**
1071           * Symbol used for HTML unordered list items
1072           * @access protected
1073           * @since 4.0.028 (2008-09-26)
1074           */
1075          protected $lisymbol = '';
1076  
1077          /**
1078           * String used to mark the beginning and end of EPS image blocks
1079           * @access protected
1080           * @since 4.1.000 (2008-10-18)
1081           */
1082          protected $epsmarker = 'x#!#EPS#!#x';
1083  
1084          /**
1085           * Array of transformation matrix
1086           * @access protected
1087           * @since 4.2.000 (2008-10-29)
1088           */
1089          protected $transfmatrix = array();
1090  
1091          /**
1092           * Current key for transformation matrix
1093           * @access protected
1094           * @since 4.8.005 (2009-09-17)
1095           */
1096          protected $transfmatrix_key = 0;
1097  
1098          /**
1099           * Booklet mode for double-sided pages
1100           * @access protected
1101           * @since 4.2.000 (2008-10-29)
1102           */
1103          protected $booklet = false;
1104  
1105          /**
1106           * Epsilon value used for float calculations
1107           * @access protected
1108           * @since 4.2.000 (2008-10-29)
1109           */
1110          protected $feps = 0.005;
1111  
1112          /**
1113           * Array used for custom vertical spaces for HTML tags
1114           * @access protected
1115           * @since 4.2.001 (2008-10-30)
1116           */
1117          protected $tagvspaces = array();
1118  
1119          /**
1120           * @var HTML PARSER: custom indent amount for lists.
1121           * Negative value means disabled.
1122           * @access protected
1123           * @since 4.2.007 (2008-11-12)
1124           */
1125          protected $customlistindent = -1;
1126  
1127          /**
1128           * @var if true keeps the border open for the cell sides that cross the page.
1129           * @access protected
1130           * @since 4.2.010 (2008-11-14)
1131           */
1132          protected $opencell = true;
1133  
1134          /**
1135           * @var array of files to embedd
1136           * @access protected
1137           * @since 4.4.000 (2008-12-07)
1138           */
1139          protected $embeddedfiles = array();
1140  
1141          /**
1142           * @var boolean true when inside html pre tag
1143           * @access protected
1144           * @since 4.4.001 (2008-12-08)
1145           */
1146          protected $premode = false;
1147  
1148          /**
1149           * Array used to store positions of graphics transformation blocks inside the page buffer.
1150           * keys are the page numbers
1151           * @access protected
1152           * @since 4.4.002 (2008-12-09)
1153           */
1154          protected $transfmrk = array();
1155  
1156          /**
1157           * Default color for html links
1158           * @access protected
1159           * @since 4.4.003 (2008-12-09)
1160           */
1161          protected $htmlLinkColorArray = array(0, 0, 255);
1162  
1163          /**
1164           * Default font style to add to html links
1165           * @access protected
1166           * @since 4.4.003 (2008-12-09)
1167           */
1168          protected $htmlLinkFontStyle = 'U';
1169  
1170          /**
1171           * Counts the number of pages.
1172           * @access protected
1173           * @since 4.5.000 (2008-12-31)
1174           */
1175          protected $numpages = 0;
1176  
1177          /**
1178           * Array containing page lengths in bytes.
1179           * @access protected
1180           * @since 4.5.000 (2008-12-31)
1181           */
1182          protected $pagelen = array();
1183  
1184          /**
1185           * Counts the number of pages.
1186           * @access protected
1187           * @since 4.5.000 (2008-12-31)
1188           */
1189          protected $numimages = 0;
1190  
1191          /**
1192           * Store the image keys.
1193           * @access protected
1194           * @since 4.5.000 (2008-12-31)
1195           */
1196          protected $imagekeys = array();
1197  
1198          /**
1199           * Length of the buffer in bytes.
1200           * @access protected
1201           * @since 4.5.000 (2008-12-31)
1202           */
1203          protected $bufferlen = 0;
1204  
1205          /**
1206           * If true enables disk caching.
1207           * @access protected
1208           * @since 4.5.000 (2008-12-31)
1209           */
1210          protected $diskcache = false;
1211  
1212          /**
1213           * Counts the number of fonts.
1214           * @access protected
1215           * @since 4.5.000 (2009-01-02)
1216           */
1217          protected $numfonts = 0;
1218  
1219          /**
1220           * Store the font keys.
1221           * @access protected
1222           * @since 4.5.000 (2009-01-02)
1223           */
1224          protected $fontkeys = array();
1225  
1226          /**
1227           * Store the font object IDs.
1228           * @access protected
1229           * @since 4.8.001 (2009-09-09)
1230           */
1231          protected $font_obj_ids = array();
1232  
1233          /**
1234           * Store the fage status (true when opened, false when closed).
1235           * @access protected
1236           * @since 4.5.000 (2009-01-02)
1237           */
1238          protected $pageopen = array();
1239  
1240          /**
1241           * Default monospaced font
1242           * @access protected
1243           * @since 4.5.025 (2009-03-10)
1244           */
1245          protected $default_monospaced_font = 'courier';
1246  
1247          /**
1248           * Used to store a cloned copy of the current class object
1249           * @access protected
1250           * @since 4.5.029 (2009-03-19)
1251           */
1252          protected $objcopy;
1253  
1254          /**
1255           * Array used to store the lengths of cache files
1256           * @access protected
1257           * @since 4.5.029 (2009-03-19)
1258           */
1259          protected $cache_file_length = array();
1260  
1261          /**
1262           * Table header content to be repeated on each new page
1263           * @access protected
1264           * @since 4.5.030 (2009-03-20)
1265           */
1266          protected $thead = '';
1267  
1268          /**
1269           * Margins used for table header.
1270           * @access protected
1271           * @since 4.5.030 (2009-03-20)
1272           */
1273          protected $theadMargins = array();
1274  
1275          /**
1276           * Cache array for UTF8StringToArray() method.
1277           * @access protected
1278           * @since 4.5.037 (2009-04-07)
1279           */
1280          protected $cache_UTF8StringToArray = array();
1281  
1282          /**
1283           * Maximum size of cache array used for UTF8StringToArray() method.
1284           * @access protected
1285           * @since 4.5.037 (2009-04-07)
1286           */
1287          protected $cache_maxsize_UTF8StringToArray = 8;
1288  
1289          /**
1290           * Current size of cache array used for UTF8StringToArray() method.
1291           * @access protected
1292           * @since 4.5.037 (2009-04-07)
1293           */
1294          protected $cache_size_UTF8StringToArray = 0;
1295  
1296          /**
1297           * If true enables document signing
1298           * @access protected
1299           * @since 4.6.005 (2009-04-24)
1300           */
1301          protected $sign = false;
1302  
1303          /**
1304           * Signature data
1305           * @access protected
1306           * @since 4.6.005 (2009-04-24)
1307           */
1308          protected $signature_data = array();
1309  
1310          /**
1311           * Signature max length
1312           * @access protected
1313           * @since 4.6.005 (2009-04-24)
1314           */
1315          protected $signature_max_length = 11742;
1316  
1317          /**
1318           * Regular expression used to find blank characters used for word-wrapping.
1319           * @access protected
1320           * @since 4.6.006 (2009-04-28)
1321           */
1322          protected $re_spaces = '/[\s]/';
1323  
1324          /**
1325           * Signature object ID
1326           * @access protected
1327           * @since 4.6.022 (2009-06-23)
1328           */
1329          protected $sig_obj_id = 0;
1330  
1331          /**
1332           * ByteRange placemark used during signature process.
1333           * @access protected
1334           * @since 4.6.028 (2009-08-25)
1335           */
1336          protected $byterange_string = '/ByteRange[0 ********** ********** **********]';
1337  
1338          /**
1339           * Placemark used during signature process.
1340           * @access protected
1341           * @since 4.6.028 (2009-08-25)
1342           */
1343          protected $sig_annot_ref = '***SIGANNREF*** 0 R';
1344  
1345          /**
1346           * ID of page objects
1347           * @access protected
1348           * @since 4.7.000 (2009-08-29)
1349           */
1350          protected $page_obj_id = array();
1351  
1352          /**
1353           * Start ID for embedded file objects
1354           * @access protected
1355           * @since 4.7.000 (2009-08-29)
1356           */
1357          protected $embedded_start_obj_id = 100000;
1358  
1359          /**
1360           * Start ID for annotation objects
1361           * @access protected
1362           * @since 4.7.000 (2009-08-29)
1363           */
1364          protected $annots_start_obj_id = 200000;
1365  
1366          /**
1367           * Max ID of annotation object
1368           * @access protected
1369           * @since 4.7.000 (2009-08-29)
1370           */
1371          protected $annot_obj_id = 200000;
1372  
1373          /**
1374           * Current ID of annotation object
1375           * @access protected
1376           * @since 4.8.003 (2009-09-15)
1377           */
1378          protected $curr_annot_obj_id = 200000;
1379  
1380          /**
1381           * List of form annotations IDs
1382           * @access protected
1383           * @since 4.8.000 (2009-09-07)
1384           */
1385          protected $form_obj_id = array();
1386  
1387          /**
1388           * Deafult Javascript field properties. Possible values are described on official Javascript for Acrobat API reference. Annotation options can be directly specified using the 'aopt' entry.
1389           * @access protected
1390           * @since 4.8.000 (2009-09-07)
1391           */
1392          protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1393  
1394          /**
1395           * Javascript objects array
1396           * @access protected
1397           * @since 4.8.000 (2009-09-07)
1398           */
1399          protected $js_objects = array();
1400  
1401          /**
1402           * Start ID for javascript objects
1403           * @access protected
1404           * @since 4.8.000 (2009-09-07)
1405           */
1406          protected $js_start_obj_id = 300000;
1407  
1408          /**
1409           * Current ID of javascript object
1410           * @access protected
1411           * @since 4.8.000 (2009-09-07)
1412           */
1413          protected $js_obj_id = 300000;
1414  
1415          /**
1416           * Current form action (used during XHTML rendering)
1417           * @access protected
1418           * @since 4.8.000 (2009-09-07)
1419           */
1420          protected $form_action = '';
1421  
1422          /**
1423           * Current form encryption type (used during XHTML rendering)
1424           * @access protected
1425           * @since 4.8.000 (2009-09-07)
1426           */
1427          protected $form_enctype = 'application/x-www-form-urlencoded';
1428  
1429          /**
1430           * Current method to submit forms.
1431           * @access protected
1432           * @since 4.8.000 (2009-09-07)
1433           */
1434          protected $form_mode = 'post';
1435  
1436          /**
1437           * Start ID for appearance streams XObjects
1438           * @access protected
1439           * @since 4.8.001 (2009-09-09)
1440           */
1441          protected $apxo_start_obj_id = 400000;
1442  
1443          /**
1444           * Current ID of appearance streams XObjects
1445           * @access protected
1446           * @since 4.8.001 (2009-09-09)
1447           */
1448          protected $apxo_obj_id = 400000;
1449  
1450          /**
1451           * List of fonts used on form fields (fontname => fontkey).
1452           * @access protected
1453           * @since 4.8.001 (2009-09-09)
1454           */
1455          protected $annotation_fonts = array();
1456  
1457          /**
1458           * List of radio buttons parent objects.
1459           * @access protected
1460           * @since 4.8.001 (2009-09-09)
1461           */
1462          protected $radiobutton_groups = array();
1463  
1464          /**
1465           * List of radio group objects IDs
1466           * @access protected
1467           * @since 4.8.001 (2009-09-09)
1468           */
1469          protected $radio_groups = array();
1470  
1471          /**
1472           * Text indentation value (used for text-indent CSS attribute)
1473           * @access protected
1474           * @since 4.8.006 (2009-09-23)
1475           */
1476          protected $textindent = 0;
1477  
1478          /**
1479           * Store page number when startTransaction() is called.
1480           * @access protected
1481           * @since 4.8.006 (2009-09-23)
1482           */
1483          protected $start_transaction_page = 0;
1484  
1485          /**
1486           * Store Y position when startTransaction() is called.
1487           * @access protected
1488           * @since 4.9.001 (2010-03-28)
1489           */
1490          protected $start_transaction_y = 0;
1491  
1492          /**
1493           * True when we are printing the thead section on a new page
1494           * @access protected
1495           * @since 4.8.027 (2010-01-25)
1496           */
1497          protected $inthead = false;
1498  
1499          /**
1500           * Array of column measures (width, space, starting Y position)
1501           * @access protected
1502           * @since 4.9.001 (2010-03-28)
1503           */
1504          protected $columns = array();
1505  
1506          /**
1507           * Number of colums
1508           * @access protected
1509           * @since 4.9.001 (2010-03-28)
1510           */
1511          protected $num_columns = 0;
1512  
1513          /**
1514           * Current column number
1515           * @access protected
1516           * @since 4.9.001 (2010-03-28)
1517           */
1518          protected $current_column = 0;
1519  
1520          /**
1521           * Starting page for columns
1522           * @access protected
1523           * @since 4.9.001 (2010-03-28)
1524           */
1525          protected $column_start_page = 0;
1526  
1527          /**
1528           * Text rendering mode: 0 = Fill text; 1 = Stroke text; 2 = Fill, then stroke text; 3 = Neither fill nor stroke text (invisible); 4 = Fill text and add to path for clipping; 5 = Stroke text and add to path for clipping; 6 = Fill, then stroke text and add to path for clipping; 7 = Add text to path for clipping.
1529           * @access protected
1530           * @since 4.9.008 (2010-04-03)
1531           */
1532          protected $textrendermode = 0;
1533  
1534          /**
1535           * Text stroke width in doc units
1536           * @access protected
1537           * @since 4.9.008 (2010-04-03)
1538           */
1539          protected $textstrokewidth = 0;
1540  
1541          /**
1542           * @var current stroke color
1543           * @access protected
1544           * @since 4.9.008 (2010-04-03)
1545           */
1546          protected $strokecolor;
1547  
1548          /**
1549           * @var default unit of measure for document
1550           * @access protected
1551           * @since 5.0.000 (2010-04-22)
1552           */
1553          protected $pdfunit = 'mm';
1554  
1555          /**
1556           * @var true when we are on TOC (Table Of Content) page
1557           * @access protected
1558           */
1559          protected $tocpage = false;
1560  
1561          /**
1562           * @var If true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1563           * @access protected
1564           * @since 5.0.000 (2010-04-26)
1565           */
1566          protected $rasterize_vector_images = false;
1567  
1568          /**
1569           * @var directory used for the last SVG image
1570           * @access protected
1571           * @since 5.0.000 (2010-05-05)
1572           */
1573          protected $svgdir = '';
1574  
1575          /**
1576           * @var Deafult unit of measure for SVG
1577           * @access protected
1578           * @since 5.0.000 (2010-05-02)
1579           */
1580          protected $svgunit = 'px';
1581  
1582          /**
1583           * @var array of SVG gradients
1584           * @access protected
1585           * @since 5.0.000 (2010-05-02)
1586           */
1587          protected $svggradients = array();
1588  
1589          /**
1590           * @var ID of last SVG gradient
1591           * @access protected
1592           * @since 5.0.000 (2010-05-02)
1593           */
1594          protected $svggradientid = 0;
1595  
1596          /**
1597           * @var true when in SVG defs group
1598           * @access protected
1599           * @since 5.0.000 (2010-05-02)
1600           */
1601          protected $svgdefsmode = false;
1602  
1603          /**
1604           * @var array of SVG defs
1605           * @access protected
1606           * @since 5.0.000 (2010-05-02)
1607           */
1608          protected $svgdefs = array();
1609  
1610          /**
1611           * @var true when in SVG clipPath tag
1612           * @access protected
1613           * @since 5.0.000 (2010-04-26)
1614           */
1615          protected $svgclipmode = false;
1616  
1617          /**
1618           * @var array of SVG clipPath commands
1619           * @access protected
1620           * @since 5.0.000 (2010-05-02)
1621           */
1622          protected $svgclippaths = array();
1623  
1624          /**
1625           * @var ID of last SVG clipPath
1626           * @access protected
1627           * @since 5.0.000 (2010-05-02)
1628           */
1629          protected $svgclipid = 0;
1630  
1631          /**
1632           * @var svg text
1633           * @access protected
1634           * @since 5.0.000 (2010-05-02)
1635           */
1636          protected $svgtext = '';
1637  
1638          /**
1639           * @var array of hinheritable SVG properties
1640           * @access protected
1641           * @since 5.0.000 (2010-05-02)
1642           */
1643          protected $svginheritprop = array('clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cursor', 'direction', 'fill', 'fill-opacity', 'fill-rule', 'font', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'image-rendering', 'kerning', 'letter-spacing', 'marker', 'marker-end', 'marker-mid', 'marker-start', 'pointer-events', 'shape-rendering', 'stroke', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-rendering', 'visibility', 'word-spacing', 'writing-mode');
1644  
1645          /**
1646           * @var array of SVG properties
1647           * @access protected
1648           * @since 5.0.000 (2010-05-02)
1649           */
1650          protected $svgstyles = array(array(
1651              'alignment-baseline' => 'auto',
1652              'baseline-shift' => 'baseline',
1653              'clip' => 'auto',
1654              'clip-path' => 'none',
1655              'clip-rule' => 'nonzero',
1656              'color' => 'black',
1657              'color-interpolation' => 'sRGB',
1658              'color-interpolation-filters' => 'linearRGB',
1659              'color-profile' => 'auto',
1660              'color-rendering' => 'auto',
1661              'cursor' => 'auto',
1662              'direction' => 'ltr',
1663              'display' => 'inline',
1664              'dominant-baseline' => 'auto',
1665              'enable-background' => 'accumulate',
1666              'fill' => 'black',
1667              'fill-opacity' => 1,
1668              'fill-rule' => 'nonzero',
1669              'filter' => 'none',
1670              'flood-color' => 'black',
1671              'flood-opacity' => 1,
1672              'font' => '',
1673              'font-family' => 'helvetica',
1674              'font-size' => 'medium',
1675              'font-size-adjust' => 'none',
1676              'font-stretch' => 'normal',
1677              'font-style' => 'normal',
1678              'font-variant' => 'normal',
1679              'font-weight' => 'normal',
1680              'glyph-orientation-horizontal' => '0deg',
1681              'glyph-orientation-vertical' => 'auto',
1682              'image-rendering' => 'auto',
1683              'kerning' => 'auto',
1684              'letter-spacing' => 'normal',
1685              'lighting-color' => 'white',
1686              'marker' => '',
1687              'marker-end' => 'none',
1688              'marker-mid' => 'none',
1689              'marker-start' => 'none',
1690              'mask' => 'none',
1691              'opacity' => 1,
1692              'overflow' => 'auto',
1693              'pointer-events' => 'visiblePainted',
1694              'shape-rendering' => 'auto',
1695              'stop-color' => 'black',
1696              'stop-opacity' => 1,
1697              'stroke' => 'none',
1698              'stroke-dasharray' => 'none',
1699              'stroke-dashoffset' => 0,
1700              'stroke-linecap' => 'butt',
1701              'stroke-linejoin' => 'miter',
1702              'stroke-miterlimit' => 4,
1703              'stroke-opacity' => 1,
1704              'stroke-width' => 1,
1705              'text-anchor' => 'start',
1706              'text-decoration' => 'none',
1707              'text-rendering' => 'auto',
1708              'unicode-bidi' => 'normal',
1709              'visibility' => 'visible',
1710              'word-spacing' => 'normal',
1711              'writing-mode' => 'lr-tb',
1712              'text-color' => 'black',
1713              'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1714              ));
1715  
1716          //------------------------------------------------------------
1717          // METHODS
1718          //------------------------------------------------------------
1719  
1720          /**
1721           * This is the class constructor.
1722           * It allows to set up the page format, the orientation and
1723           * the measure unit used in all the methods (except for the font sizes).
1724           * @since 1.0
1725           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li></ul>
1726           * @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.
1727           * @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>
1728           * @param boolean $unicode TRUE means that the input text is unicode (default = true)
1729           * @param boolean $diskcache if TRUE reduce the RAM memory usage by caching temporary data on filesystem (slower).
1730           * @param String $encoding charset encoding; default is UTF-8
1731           * @access public
1732           */
1733  		public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false) {
1734              /* Set internal character encoding to ASCII */
1735              if (function_exists('mb_internal_encoding') AND mb_internal_encoding()) {
1736                  $this->internal_encoding = mb_internal_encoding();
1737                  mb_internal_encoding('ASCII');
1738              }
1739              // set disk caching
1740              $this->diskcache = $diskcache ? true : false;
1741              // set language direction
1742              $this->rtl = false;
1743              $this->tmprtl = false;
1744              //Some checks
1745              $this->_dochecks();
1746              //Initialization of properties
1747              $this->isunicode = $unicode;
1748              $this->page = 0;
1749              $this->transfmrk[0] = array();
1750              $this->pagedim = array();
1751              $this->n = 2;
1752              $this->buffer = '';
1753              $this->pages = array();
1754              $this->state = 0;
1755              $this->fonts = array();
1756              $this->FontFiles = array();
1757              $this->diffs = array();
1758              $this->images = array();
1759              $this->links = array();
1760              $this->gradients = array();
1761              $this->InFooter = false;
1762              $this->lasth = 0;
1763              $this->FontFamily = 'helvetica';
1764              $this->FontStyle = '';
1765              $this->FontSizePt = 12;
1766              $this->underline = false;
1767              $this->overline = false;
1768              $this->linethrough = false;
1769              $this->DrawColor = '0 G';
1770              $this->FillColor = '0 g';
1771              $this->TextColor = '0 g';
1772              $this->ColorFlag = false;
1773              // encryption values
1774              $this->encrypted = false;
1775              $this->last_rc4_key = '';
1776              $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";
1777              //Standard Unicode fonts
1778              $this->CoreFonts = array(
1779                  'courier'=>'Courier',
1780                  'courierB'=>'Courier-Bold',
1781                  'courierI'=>'Courier-Oblique',
1782                  'courierBI'=>'Courier-BoldOblique',
1783                  'helvetica'=>'Helvetica',
1784                  'helveticaB'=>'Helvetica-Bold',
1785                  'helveticaI'=>'Helvetica-Oblique',
1786                  'helveticaBI'=>'Helvetica-BoldOblique',
1787                  'times'=>'Times-Roman',
1788                  'timesB'=>'Times-Bold',
1789                  'timesI'=>'Times-Italic',
1790                  'timesBI'=>'Times-BoldItalic',
1791                  'symbol'=>'Symbol',
1792                  'zapfdingbats'=>'ZapfDingbats'
1793              );
1794              //Set scale factor
1795              $this->setPageUnit($unit);
1796              // set page format and orientation
1797              $this->setPageFormat($format, $orientation);
1798              //Page margins (1 cm)
1799              $margin = 28.35 / $this->k;
1800              $this->SetMargins($margin, $margin);
1801              //Interior cell margin
1802              $this->cMargin = $margin / 10;
1803              //Line width (0.2 mm)
1804              $this->LineWidth = 0.57 / $this->k;
1805              $this->linestyleWidth = sprintf('%.2F w', ($this->LineWidth * $this->k));
1806              $this->linestyleCap = '0 J';
1807              $this->linestyleJoin = '0 j';
1808              $this->linestyleDash = '[] 0 d';
1809              //Automatic page break
1810              $this->SetAutoPageBreak(true, (2 * $margin));
1811              //Full width display mode
1812              $this->SetDisplayMode('fullwidth');
1813              //Compression
1814              $this->SetCompression(true);
1815              //Set default PDF version number
1816              $this->PDFVersion = '1.7';
1817              $this->encoding = $encoding;
1818              $this->HREF = array();
1819              $this->getFontsList();
1820              $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1821              $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1822              $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1823              $this->extgstates = array();
1824              // user's rights
1825              $this->sign = false;
1826              $this->ur = false;
1827              $this->ur_document = '/FullSave';
1828              $this->ur_annots = '/Create/Delete/Modify/Copy/Import/Export';
1829              $this->ur_form = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1830              $this->ur_signature = '/Modify';
1831              // set default JPEG quality
1832              $this->jpeg_quality = 75;
1833              // initialize some settings
1834              $this->utf8Bidi(array(''), '');
1835              // set default font
1836              $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1837              // check if PCRE Unicode support is enabled
1838              if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1839                  // PCRE unicode support is turned ON
1840                  // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
1841                  // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
1842                  // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
1843                  //$this->re_spaces = '/[\s\p{Z}\p{Lo}]/u';
1844                  $this->re_spaces = '/[\s\p{Z}]/u';
1845              } else {
1846                  // PCRE unicode support is turned OFF
1847                  $this->re_spaces = '/[\s]/';
1848              }
1849              $this->annot_obj_id = $this->annots_start_obj_id;
1850              $this->curr_annot_obj_id = $this->annots_start_obj_id;
1851              $this->apxo_obj_id = $this->apxo_start_obj_id;
1852              $this->js_obj_id = $this->js_start_obj_id;
1853              $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1854          }
1855  
1856          /**
1857           * Default destructor.
1858           * @access public
1859           * @since 1.53.0.TC016
1860           */
1861  		public function __destruct() {
1862              // restore internal encoding
1863              if (isset($this->internal_encoding) AND !empty($this->internal_encoding)) {
1864                  mb_internal_encoding($this->internal_encoding);
1865              }
1866              // unset all class variables
1867              $this->_destroy(true);
1868          }
1869  
1870          /**
1871           * Set the units of measure for the document.
1872           * @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.
1873           * @access public
1874           * @since 3.0.015 (2008-06-06)
1875           */
1876  		public function setPageUnit($unit) {
1877              $unit = strtolower($unit);
1878              //Set scale factor
1879              switch ($unit) {
1880                  // points
1881                  case 'px':
1882                  case 'pt': {
1883                      $this->k = 1;
1884                      break;
1885                  }
1886                  // millimeters
1887                  case 'mm': {
1888                      $this->k = $this->dpi / 25.4;
1889                      break;
1890                  }
1891                  // centimeters
1892                  case 'cm': {
1893                      $this->k = $this->dpi / 2.54;
1894                      break;
1895                  }
1896                  // inches
1897                  case 'in': {
1898                      $this->k = $this->dpi;
1899                      break;
1900                  }
1901                  // unsupported unit
1902                  default : {
1903                      $this->Error('Incorrect unit: '.$unit);
1904                      break;
1905                  }
1906              }
1907              $this->pdfunit = $unit;
1908              if (isset($this->CurOrientation)) {
1909                      $this->setPageOrientation($this->CurOrientation);
1910              }
1911          }
1912  
1913          /**
1914           * Set the page format
1915           * @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>
1916           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1917           * @access public
1918           * @since 3.0.015 (2008-06-06)
1919           */
1920  		public function setPageFormat($format, $orientation='P') {
1921              //Page format
1922              if (is_string($format)) {
1923                  // Page formats (45 standard ISO paper formats and 4 american common formats).
1924                  // Paper cordinates are calculated in this way: (inches * 72) where (1 inch = 2.54 cm)
1925                  switch (strtoupper($format)) {
1926                      case '4A0': {$format = array(4767.87,6740.79); break;}
1927                      case '2A0': {$format = array(3370.39,4767.87); break;}
1928                      case 'A0': {$format = array(2383.94,3370.39); break;}
1929                      case 'A1': {$format = array(1683.78,2383.94); break;}
1930                      case 'A2': {$format = array(1190.55,1683.78); break;}
1931                      case 'A3': {$format = array(841.89,1190.55); break;}
1932                      case 'A4': default: {$format = array(595.28,841.89); break;}
1933                      case 'A5': {$format = array(419.53,595.28); break;}
1934                      case 'A6': {$format = array(297.64,419.53); break;}
1935                      case 'A7': {$format = array(209.76,297.64); break;}
1936                      case 'A8': {$format = array(147.40,209.76); break;}
1937                      case 'A9': {$format = array(104.88,147.40); break;}
1938                      case 'A10': {$format = array(73.70,104.88); break;}
1939                      case 'B0': {$format = array(2834.65,4008.19); break;}
1940                      case 'B1': {$format = array(2004.09,2834.65); break;}
1941                      case 'B2': {$format = array(1417.32,2004.09); break;}
1942                      case 'B3': {$format = array(1000.63,1417.32); break;}
1943                      case 'B4': {$format = array(708.66,1000.63); break;}
1944                      case 'B5': {$format = array(498.90,708.66); break;}
1945                      case 'B6': {$format = array(354.33,498.90); break;}
1946                      case 'B7': {$format = array(249.45,354.33); break;}
1947                      case 'B8': {$format = array(175.75,249.45); break;}
1948                      case 'B9': {$format = array(124.72,175.75); break;}
1949                      case 'B10': {$format = array(87.87,124.72); break;}
1950                      case 'C0': {$format = array(2599.37,3676.54); break;}
1951                      case 'C1': {$format = array(1836.85,2599.37); break;}
1952                      case 'C2': {$format = array(1298.27,1836.85); break;}
1953                      case 'C3': {$format = array(918.43,1298.27); break;}
1954                      case 'C4': {$format = array(649.13,918.43); break;}
1955                      case 'C5': {$format = array(459.21,649.13); break;}
1956                      case 'C6': {$format = array(323.15,459.21); break;}
1957                      case 'C7': {$format = array(229.61,323.15); break;}
1958                      case 'C8': {$format = array(161.57,229.61); break;}
1959                      case 'C9': {$format = array(113.39,161.57); break;}
1960                      case 'C10': {$format = array(79.37,113.39); break;}
1961                      case 'RA0': {$format = array(2437.80,3458.27); break;}
1962                      case 'RA1': {$format = array(1729.13,2437.80); break;}
1963                      case 'RA2': {$format = array(1218.90,1729.13); break;}
1964                      case 'RA3': {$format = array(864.57,1218.90); break;}
1965                      case 'RA4': {$format = array(609.45,864.57); break;}
1966                      case 'SRA0': {$format = array(2551.18,3628.35); break;}
1967                      case 'SRA1': {$format = array(1814.17,2551.18); break;}
1968                      case 'SRA2': {$format = array(1275.59,1814.17); break;}
1969                      case 'SRA3': {$format = array(907.09,1275.59); break;}
1970                      case 'SRA4': {$format = array(637.80,907.09); break;}
1971                      case 'LETTER': {$format = array(612.00,792.00); break;}
1972                      case 'LEGAL': {$format = array(612.00,1008.00); break;}
1973                      case 'EXECUTIVE': {$format = array(521.86,756.00); break;}
1974                      case 'FOLIO': {$format = array(612.00,936.00); break;}
1975                  }
1976                  $this->fwPt = $format[0];
1977                  $this->fhPt = $format[1];
1978              } else {
1979                  $this->fwPt = $format[0] * $this->k;
1980                  $this->fhPt = $format[1] * $this->k;
1981              }
1982              $this->setPageOrientation($orientation);
1983          }
1984  
1985          /**
1986           * Set page orientation.
1987           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
1988           * @param boolean $autopagebreak Boolean indicating if auto-page-break mode should be on or off.
1989           * @param float $bottommargin bottom margin of the page.
1990           * @access public
1991           * @since 3.0.015 (2008-06-06)
1992           */
1993  		public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
1994              if ($this->fwPt > $this->fhPt) {
1995                  // landscape
1996                  $default_orientation = 'L';
1997              } else {
1998                  // portrait
1999                  $default_orientation = 'P';
2000              }
2001              $valid_orientations = array('P', 'L');
2002              if (empty($orientation)) {
2003                  $orientation = $default_orientation;
2004              } else {
2005                  $orientation = $orientation{0};
2006                  $orientation = strtoupper($orientation);
2007              }
2008              if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2009                  $this->CurOrientation = $orientation;
2010                  $this->wPt = $this->fhPt;
2011                  $this->hPt = $this->fwPt;
2012              } else {
2013                  $this->CurOrientation = $default_orientation;
2014                  $this->wPt = $this->fwPt;
2015                  $this->hPt = $this->fhPt;
2016              }
2017              $this->w = $this->wPt / $this->k;
2018              $this->h = $this->hPt / $this->k;
2019              if ($this->empty_string($autopagebreak)) {
2020                  if (isset($this->AutoPageBreak)) {
2021                      $autopagebreak = $this->AutoPageBreak;
2022                  } else {
2023                      $autopagebreak = true;
2024                  }
2025              }
2026              if ($this->empty_string($bottommargin)) {
2027                  if (isset($this->bMargin)) {
2028                      $bottommargin = $this->bMargin;
2029                  } else {
2030                      // default value = 2 cm
2031                      $bottommargin = 2 * 28.35 / $this->k;
2032                  }
2033              }
2034              $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2035              // store page dimensions
2036              $this->pagedim[$this->page] = array('w' => $this->wPt, 'h' => $this->hPt, 'wk' => $this->w, 'hk' => $this->h, 'tm' => $this->tMargin, 'bm' => $bottommargin, 'lm' => $this->lMargin, 'rm' => $this->rMargin, 'pb' => $autopagebreak, 'or' => $this->CurOrientation, 'olm' => $this->original_lMargin, 'orm' => $this->original_rMargin);
2037          }
2038  
2039          /**
2040           * Set regular expression to detect withespaces or word separators.
2041           * @param string $re regular expression (leave empty for default).
2042           * @access public
2043           * @since 4.6.016 (2009-06-15)
2044           */
2045  		public function setSpacesRE($re='/[\s]/') {
2046              // if PCRE unicode support is turned ON:
2047              //     \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
2048              //     \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
2049              //     \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
2050              $this->re_spaces = $re;
2051          }
2052  
2053          /**
2054           * Enable or disable Right-To-Left language mode
2055           * @param Boolean $enable if true enable Right-To-Left language mode.
2056           * @param Boolean $resetx if true reset the X position on direction change.
2057           * @access public
2058           * @since 2.0.000 (2008-01-03)
2059           */
2060  		public function setRTL($enable, $resetx=true) {
2061              $enable = $enable ? true : false;
2062              $resetx = ($resetx AND ($enable != $this->rtl));
2063              $this->rtl = $enable;
2064              $this->tmprtl = false;
2065              if ($resetx) {
2066                  $this->Ln(0);
2067              }
2068          }
2069  
2070          /**
2071           * Return the RTL status
2072           * @return boolean
2073           * @access public
2074           * @since 4.0.012 (2008-07-24)
2075           */
2076  		public function getRTL() {
2077              return $this->rtl;
2078          }
2079  
2080          /**
2081           * Force temporary RTL language direction
2082           * @param mixed $mode can be false, 'L' for LTR or 'R' for RTL
2083           * @access public
2084           * @since 2.1.000 (2008-01-09)
2085           */
2086  		public function setTempRTL($mode) {
2087              $newmode = false;
2088              switch ($mode) {
2089                  case 'ltr':
2090                  case 'LTR':
2091                  case 'L': {
2092                      if ($this->rtl) {
2093                          $newmode = 'L';
2094                      }
2095                      break;
2096                  }
2097                  case 'rtl':
2098                  case 'RTL':
2099                  case 'R': {
2100                      if (!$this->rtl) {
2101                          $newmode = 'R';
2102                      }
2103                      break;
2104                  }
2105                  case false:
2106                  default: {
2107                      $newmode = false;
2108                      break;
2109                  }
2110              }
2111              $this->tmprtl = $newmode;
2112          }
2113  
2114          /**
2115           * Return the current temporary RTL status
2116           * @return boolean
2117           * @access public
2118           * @since 4.8.014 (2009-11-04)
2119           */
2120  		public function isRTLTextDir() {
2121              return ($this->rtl OR ($this->tmprtl == 'R'));
2122          }
2123  
2124          /**
2125           * Set the last cell height.
2126           * @param float $h cell height.
2127           * @author Nicola Asuni
2128           * @access public
2129           * @since 1.53.0.TC034
2130           */
2131  		public function setLastH($h) {
2132              $this->lasth = $h;
2133          }
2134  
2135          /**
2136           * Get the last cell height.
2137           * @return last cell height
2138           * @access public
2139           * @since 4.0.017 (2008-08-05)
2140           */
2141  		public function getLastH() {
2142              return $this->lasth;
2143          }
2144  
2145          /**
2146           * Set the adjusting factor to convert pixels to user units.
2147           * @param float $scale adjusting factor to convert pixels to user units.
2148           * @author Nicola Asuni
2149           * @access public
2150           * @since 1.5.2
2151           */
2152  		public function setImageScale($scale) {
2153              $this->imgscale = $scale;
2154          }
2155  
2156          /**
2157           * Returns the adjusting factor to convert pixels to user units.
2158           * @return float adjusting factor to convert pixels to user units.
2159           * @author Nicola Asuni
2160           * @access public
2161           * @since 1.5.2
2162           */
2163  		public function getImageScale() {
2164              return $this->imgscale;
2165          }
2166  
2167          /**
2168           * Returns an array of page dimensions:
2169           * <ul><li>$this->pagedim[$this->page]['w'] => page_width_in_points</li><li>$this->pagedim[$this->page]['h'] => height in points</li><li>$this->pagedim[$this->page]['wk'] => page_width_in_points</li><li>$this->pagedim[$this->page]['hk'] => 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><li>$this->pagedim[$this->page]['olm'] => original_left_margin</li><li>$this->pagedim[$this->page]['orm'] => original_right_margin</li></ul>
2170           * @param int $pagenum page number (empty = current page)
2171           * @return array of page dimensions.
2172           * @author Nicola Asuni
2173           * @access public
2174           * @since 4.5.027 (2009-03-16)
2175           */
2176  		public function getPageDimensions($pagenum='') {
2177              if (empty($pagenum)) {
2178                  $pagenum = $this->page;
2179              }
2180              return $this->pagedim[$pagenum];
2181          }
2182  
2183          /**
2184           * Returns the page width in units.
2185           * @param int $pagenum page number (empty = current page)
2186           * @return int page width.
2187           * @author Nicola Asuni
2188           * @access public
2189           * @since 1.5.2
2190           * @see getPageDimensions()
2191           */
2192  		public function getPageWidth($pagenum='') {
2193              if (empty($pagenum)) {
2194                  return $this->w;
2195              }
2196              return $this->pagedim[$pagenum]['w'];
2197          }
2198  
2199          /**
2200           * Returns the page height in units.
2201           * @param int $pagenum page number (empty = current page)
2202           * @return int page height.
2203           * @author Nicola Asuni
2204           * @access public
2205           * @since 1.5.2
2206           * @see getPageDimensions()
2207           */
2208  		public function getPageHeight($pagenum='') {
2209              if (empty($pagenum)) {
2210                  return $this->h;
2211              }
2212              return $this->pagedim[$pagenum]['h'];
2213          }
2214  
2215          /**
2216           * Returns the page break margin.
2217           * @param int $pagenum page number (empty = current page)
2218           * @return int page break margin.
2219           * @author Nicola Asuni
2220           * @access public
2221           * @since 1.5.2
2222           * @see getPageDimensions()
2223           */
2224  		public function getBreakMargin($pagenum='') {
2225              if (empty($pagenum)) {
2226                  return $this->bMargin;
2227              }
2228              return $this->pagedim[$pagenum]['bm'];
2229          }
2230  
2231          /**
2232           * Returns the scale factor (number of points in user unit).
2233           * @return int scale factor.
2234           * @author Nicola Asuni
2235           * @access public
2236           * @since 1.5.2
2237           */
2238  		public function getScaleFactor() {
2239              return $this->k;
2240          }
2241  
2242          /**
2243           * Defines the left, top and right margins.
2244           * @param float $left Left margin.
2245           * @param float $top Top margin.
2246           * @param float $right Right margin. Default value is the left one.
2247           * @param boolean $keepmargins if true overwrites the default page margins
2248           * @access public
2249           * @since 1.0
2250           * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
2251           */
2252  		public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2253              //Set left, top and right margins
2254              $this->lMargin = $left;
2255              $this->tMargin = $top;
2256              if ($right == -1) {
2257                  $right = $left;
2258              }
2259              $this->rMargin = $right;
2260              if ($keepmargins) {
2261                  // overwrite original values
2262                  $this->original_lMargin = $this->lMargin;
2263                  $this->original_rMargin = $this->rMargin;
2264              }
2265          }
2266  
2267          /**
2268           * 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.
2269           * @param float $margin The margin.
2270           * @access public
2271           * @since 1.4
2272           * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2273           */
2274  		public function SetLeftMargin($margin) {
2275              //Set left margin
2276              $this->lMargin=$margin;
2277              if (($this->page > 0) AND ($this->x < $margin)) {
2278                  $this->x = $margin;
2279              }
2280          }
2281  
2282          /**
2283           * Defines the top margin. The method can be called before creating the first page.
2284           * @param float $margin The margin.
2285           * @access public
2286           * @since 1.5
2287           * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2288           */
2289  		public function SetTopMargin($margin) {
2290              //Set top margin
2291              $this->tMargin=$margin;
2292              if (($this->page > 0) AND ($this->y < $margin)) {
2293                  $this->y = $margin;
2294              }
2295          }
2296  
2297          /**
2298           * Defines the right margin. The method can be called before creating the first page.
2299           * @param float $margin The margin.
2300           * @access public
2301           * @since 1.5
2302           * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2303           */
2304  		public function SetRightMargin($margin) {
2305              $this->rMargin=$margin;
2306              if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2307                  $this->x = $this->w - $margin;
2308              }
2309          }
2310  
2311          /**
2312           * Set the internal Cell padding.
2313           * @param float $pad internal padding.
2314           * @access public
2315           * @since 2.1.000 (2008-01-09)
2316           * @see Cell(), SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2317           */
2318  		public function SetCellPadding($pad) {
2319              $this->cMargin = $pad;
2320          }
2321  
2322          /**
2323           * 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.
2324           * @param boolean $auto Boolean indicating if mode should be on or off.
2325           * @param float $margin Distance from the bottom of the page.
2326           * @access public
2327           * @since 1.0
2328           * @see Cell(), MultiCell(), AcceptPageBreak()
2329           */
2330  		public function SetAutoPageBreak($auto, $margin=0) {
2331              //Set auto page break mode and triggering margin
2332              $this->AutoPageBreak = $auto;
2333              $this->bMargin = $margin;
2334              $this->PageBreakTrigger = $this->h - $margin;
2335          }
2336  
2337          /**
2338           * Defines the way the document is to be displayed by the viewer.
2339           * @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>
2340           * @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>
2341           * @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>
2342           * @access public
2343           * @since 1.2
2344           */
2345  		public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2346              //Set display mode in viewer
2347              if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2348                  $this->ZoomMode = $zoom;
2349              } else {
2350                  $this->Error('Incorrect zoom display mode: '.$zoom);
2351              }
2352              switch ($layout) {
2353                  case 'default':
2354                  case 'single':
2355                  case 'SinglePage': {
2356                      $this->LayoutMode = 'SinglePage';
2357                      break;
2358                  }
2359                  case 'continuous':
2360                  case 'OneColumn': {
2361                      $this->LayoutMode = 'OneColumn';
2362                      break;
2363                  }
2364                  case 'two':
2365                  case 'TwoColumnLeft': {
2366                      $this->LayoutMode = 'TwoColumnLeft';
2367                      break;
2368                  }
2369                  case 'TwoColumnRight': {
2370                      $this->LayoutMode = 'TwoColumnRight';
2371                      break;
2372                  }
2373                  case 'TwoPageLeft': {
2374                      $this->LayoutMode = 'TwoPageLeft';
2375                      break;
2376                  }
2377                  case 'TwoPageRight': {
2378                      $this->LayoutMode = 'TwoPageRight';
2379                      break;
2380                  }
2381                  default: {
2382                      $this->LayoutMode = 'SinglePage';
2383                  }
2384              }
2385              switch ($mode) {
2386                  case 'UseNone': {
2387                      $this->PageMode = 'UseNone';
2388                      break;
2389                  }
2390                  case 'UseOutlines': {
2391                      $this->PageMode = 'UseOutlines';
2392                      break;
2393                  }
2394                  case 'UseThumbs': {
2395                      $this->PageMode = 'UseThumbs';
2396                      break;
2397                  }
2398                  case 'FullScreen': {
2399                      $this->PageMode = 'FullScreen';
2400                      break;
2401                  }
2402                  case 'UseOC': {
2403                      $this->PageMode = 'UseOC';
2404                      break;
2405                  }
2406                  case '': {
2407                      $this->PageMode = 'UseAttachments';
2408                      break;
2409                  }
2410                  default: {
2411                      $this->PageMode = 'UseNone';
2412                  }
2413              }
2414          }
2415  
2416          /**
2417           * 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.
2418           * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
2419           * @param boolean $compress Boolean indicating if compression must be enabled.
2420           * @access public
2421           * @since 1.4
2422           */
2423  		public function SetCompression($compress) {
2424              //Set page compression
2425              if (function_exists('gzcompress')) {
2426                  $this->compress = $compress;
2427              } else {
2428                  $this->compress = false;
2429              }
2430          }
2431  
2432          /**
2433           * Defines the title of the document.
2434           * @param string $title The title.
2435           * @access public
2436           * @since 1.2
2437           * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2438           */
2439  		public function SetTitle($title) {
2440              //Title of document
2441              $this->title = $title;
2442          }
2443  
2444          /**
2445           * Defines the subject of the document.
2446           * @param string $subject The subject.
2447           * @access public
2448           * @since 1.2
2449           * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2450           */
2451  		public function SetSubject($subject) {
2452              //Subject of document
2453              $this->subject = $subject;
2454          }
2455  
2456          /**
2457           * Defines the author of the document.
2458           * @param string $author The name of the author.
2459           * @access public
2460           * @since 1.2
2461           * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2462           */
2463  		public function SetAuthor($author) {
2464              //Author of document
2465              $this->author = $author;
2466          }
2467  
2468          /**
2469           * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2470           * @param string $keywords The list of keywords.
2471           * @access public
2472           * @since 1.2
2473           * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2474           */
2475  		public function SetKeywords($keywords) {
2476              //Keywords of document
2477              $this->keywords = $keywords;
2478          }
2479  
2480          /**
2481           * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2482           * @param string $creator The name of the creator.
2483           * @access public
2484           * @since 1.2
2485           * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2486           */
2487  		public function SetCreator($creator) {
2488              //Creator of document
2489              $this->creator = $creator;
2490          }
2491  
2492          /**
2493           * 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.
2494           * 2004-06-11 :: Nicola Asuni : changed bold tag with strong
2495           * @param string $msg The error message
2496           * @access public
2497           * @since 1.0
2498           */
2499  		public function Error($msg) {
2500              // unset all class variables
2501              $this->_destroy(true);
2502              // exit program and print error
2503              die('<strong>TCPDF ERROR: </strong>'.$msg);
2504          }
2505  
2506          /**
2507           * This method begins the generation of the PDF document.
2508           * It is not necessary to call it explicitly because AddPage() does it automatically.
2509           * Note: no page is created by this method
2510           * @access public
2511           * @since 1.0
2512           * @see AddPage(), Close()
2513           */
2514  		public function Open() {
2515              //Begin document
2516              $this->state = 1;
2517          }
2518  
2519          /**
2520           * Terminates the PDF document.
2521           * It is not necessary to call this method explicitly because Output() does it automatically.
2522           * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
2523           * @access public
2524           * @since 1.0
2525           * @see Open(), Output()
2526           */
2527  		public function Close() {
2528              if ($this->state == 3) {
2529                  return;
2530              }
2531              if ($this->page == 0) {
2532                  $this->AddPage();
2533              }
2534              // close page
2535              $this->endPage();
2536              // close document
2537              $this->_enddoc();
2538              // unset all class variables (except critical ones)
2539              $this->_destroy(false);
2540          }
2541  
2542          /**
2543           * Move pointer at the specified document page and update page dimensions.
2544           * @param int $pnum page number (1 ... numpages)
2545           * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2546           * @access public
2547           * @since 2.1.000 (2008-01-07)
2548           * @see getPage(), lastpage(), getNumPages()
2549           */
2550  		public function setPage($pnum, $resetmargins=false) {
2551              if ($pnum == $this->page) {
2552                  return;
2553              }
2554              if (($pnum > 0) AND ($pnum <= $this->numpages)) {
2555                  $this->state = 2;
2556                  // save current graphic settings
2557                  //$gvars = $this->getGraphicVars();
2558                  $oldpage = $this->page;
2559                  $this->page = $pnum;
2560                  $this->wPt = $this->pagedim[$this->page]['w'];
2561                  $this->hPt = $this->pagedim[$this->page]['h'];
2562                  $this->w = $this->wPt / $this->k;
2563                  $this->h = $this->hPt / $this->k;
2564                  $this->tMargin = $this->pagedim[$this->page]['tm'];
2565                  $this->bMargin = $this->pagedim[$this->page]['bm'];
2566                  $this->original_lMargin = $this->pagedim[$this->page]['olm'];
2567                  $this->original_rMargin = $this->pagedim[$this->page]['orm'];
2568                  $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
2569                  $this->CurOrientation = $this->pagedim[$this->page]['or'];
2570                  $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
2571                  // restore graphic settings
2572                  //$this->setGraphicVars($gvars);
2573                  if ($resetmargins) {
2574                      $this->lMargin = $this->pagedim[$this->page]['olm'];
2575                      $this->rMargin = $this->pagedim[$this->page]['orm'];
2576                      $this->SetY($this->tMargin);
2577                  } else {
2578                      // account for booklet mode
2579                      if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
2580                          $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
2581                          $this->lMargin += $deltam;
2582                          $this->rMargin -= $deltam;
2583                      }
2584                  }
2585              } else {
2586                  $this->Error('Wrong page number on setPage() function.');
2587              }
2588          }
2589  
2590          /**
2591           * Reset pointer to the last document page.
2592           * @param boolean $resetmargins if true reset left, right, top margins and Y position.
2593           * @access public
2594           * @since 2.0.000 (2008-01-04)
2595           * @see setPage(), getPage(), getNumPages()
2596           */
2597  		public function lastPage($resetmargins=false) {
2598              $this->setPage($this->getNumPages(), $resetmargins);
2599          }
2600  
2601          /**
2602           * Get current document page number.
2603           * @return int page number
2604           * @access public
2605           * @since 2.1.000 (2008-01-07)
2606           * @see setPage(), lastpage(), getNumPages()
2607           */
2608  		public function getPage() {
2609              return $this->page;
2610          }
2611  
2612  
2613          /**
2614           * Get the total number of insered pages.
2615           * @return int number of pages
2616           * @access public
2617           * @since 2.1.000 (2008-01-07)
2618           * @see setPage(), getPage(), lastpage()
2619           */
2620  		public function getNumPages() {
2621              return $this->numpages;
2622          }
2623  
2624          /**
2625           * Adds a new TOC (Table Of Content) page to the document.
2626           * @param string $orientation page orientation.
2627           * @param boolean $keepmargins if true overwrites the default page margins with the current margins
2628           * @access public
2629           * @since 5.0.001 (2010-05-06)
2630           * @see AddPage(), startPage(), endPage(), endTOCPage()
2631           */
2632  		public function addTOCPage($orientation='', $format='', $keepmargins=false) {
2633              $this->AddPage($orientation, $format, $keepmargins, true);
2634          }
2635  
2636          /**
2637           * Terminate the current TOC (Table Of Content) page
2638           * @access public
2639           * @since 5.0.001 (2010-05-06)
2640           * @see AddPage(), startPage(), endPage(), addTOCPage()
2641           */
2642  		public function endTOCPage() {
2643              $this->endPage(true);
2644          }
2645  
2646          /**
2647           * Adds a new page to the document. If a page is already present, the Footer() method is called first to output the footer (if enabled). Then the page is added, the current position set to the top-left corner according to the left and top margins (or top-right if in RTL mode), and Header() is called to display the header (if enabled).
2648           * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
2649           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2650           * @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>
2651           * @param boolean $keepmargins if true overwrites the default page margins with the current margins
2652           * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table Of Content).
2653           * @access public
2654           * @since 1.0
2655           * @see startPage(), endPage(), addTOCPage(), endTOCPage()
2656           */
2657  		public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
2658              if (!isset($this->original_lMargin) OR $keepmargins) {
2659                  $this->original_lMargin = $this->lMargin;
2660              }
2661              if (!isset($this->original_rMargin) OR $keepmargins) {
2662                  $this->original_rMargin = $this->rMargin;
2663              }
2664              // terminate previous page
2665              $this->endPage();
2666              // start new page
2667              $this->startPage($orientation, $format, $tocpage);
2668          }
2669  
2670          /**
2671           * Terminate the current page
2672           * @param boolean $tocpage if true set the tocpage state to false (end the page used to display Table Of Content).
2673           * @access public
2674           * @since 4.2.010 (2008-11-14)
2675           * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
2676           */
2677  		public function endPage($tocpage=false) {
2678              // check if page is already closed
2679              if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
2680                  return;
2681              }
2682              $this->InFooter = true;
2683              // print page footer
2684              $this->setFooter();
2685              // close page
2686              $this->_endpage();
2687              // mark page as closed
2688              $this->pageopen[$this->page] = false;
2689              $this->InFooter = false;
2690              if ($tocpage) {
2691                  $this->tocpage = false;
2692              }
2693          }
2694  
2695          /**
2696           * Starts a new page to the document. The page must be closed using the endPage() function.
2697           * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
2698           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
2699           * @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>
2700           * @param boolean $tocpage if true set the tocpage state to true (the added page will be used to display Table of Content).
2701           * @access public
2702           * @since 4.2.010 (2008-11-14)
2703           * @see AddPage(), endPage(), addTOCPage(), endTOCPage()
2704           */
2705  		public function startPage($orientation='', $format='', $tocpage=false) {
2706              if ($tocpage) {
2707                  $this->tocpage = true;
2708              }
2709              if ($this->numpages > $this->page) {
2710                  // this page has been already added
2711                  $this->setPage($this->page + 1);
2712                  $this->SetY($this->tMargin);
2713                  return;
2714              }
2715              // start a new page
2716              if ($this->state == 0) {
2717                  $this->Open();
2718              }
2719              ++$this->numpages;
2720              $this->swapMargins($this->booklet);
2721              // save current graphic settings
2722              $gvars = $this->getGraphicVars();
2723              // start new page
2724              $this->_beginpage($orientation, $format);
2725              // mark page as open
2726              $this->pageopen[$this->page] = true;
2727              // restore graphic settings
2728              $this->setGraphicVars($gvars);
2729              // mark this point
2730              $this->setPageMark();
2731              // print page header
2732              $this->setHeader();
2733              // restore graphic settings
2734              $this->setGraphicVars($gvars);
2735              // mark this point
2736              $this->setPageMark();
2737              // print table header (if any)
2738              $this->setTableHeader();
2739          }
2740  
2741          /**
2742            * Set start-writing mark on current page stream used to put borders and fills.
2743            * Borders and fills are always created after content and inserted on the position marked by this method.
2744            * This function must be called after calling Image() function for a background image.
2745            * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
2746            * @access public
2747            * @since 4.0.016 (2008-07-30)
2748           */
2749  		public function setPageMark() {
2750              $this->intmrk[$this->page] = $this->pagelen[$this->page];
2751              $this->setContentMark();
2752          }
2753  
2754          /**
2755            * Set start-writing mark on selected page.
2756            * Borders and fills are always created after content and inserted on the position marked by this method.
2757            * @param int $page page number (default is the current page)
2758            * @access protected
2759            * @since 4.6.021 (2009-07-20)
2760           */
2761  		protected function setContentMark($page=0) {
2762              if ($page <= 0) {
2763                  $page = $this->page;
2764              }
2765              if (isset($this->footerlen[$page])) {
2766                  $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
2767              } else {
2768                  $this->cntmrk[$page] = $this->pagelen[$page];
2769              }
2770          }
2771  
2772          /**
2773            * Set header data.
2774           * @param string $ln header image logo
2775           * @param string $lw header image logo width in mm
2776           * @param string $ht string to print as title on document header
2777           * @param string $hs string to print on document header
2778           * @access public
2779           */
2780  		public function setHeaderData($ln='', $lw=0, $ht='', $hs='') {
2781              $this->header_logo = $ln;
2782              $this->header_logo_width = $lw;
2783              $this->header_title = $ht;
2784              $this->header_string = $hs;
2785          }
2786  
2787          /**
2788            * Returns header data:
2789            * <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>
2790           * @return array()
2791           * @access public
2792           * @since 4.0.012 (2008-07-24)
2793           */
2794  		public function getHeaderData() {
2795              $ret = array();
2796              $ret['logo'] = $this->header_logo;
2797              $ret['logo_width'] = $this->header_logo_width;
2798              $ret['title'] = $this->header_title;
2799              $ret['string'] = $this->header_string;
2800              return $ret;
2801          }
2802  
2803          /**
2804            * Set header margin.
2805           * (minimum distance between header and top page margin)
2806           * @param int $hm distance in user units
2807           * @access public
2808           */
2809  		public function setHeaderMargin($hm=10) {
2810              $this->header_margin = $hm;
2811          }
2812  
2813          /**
2814            * Returns header margin in user units.
2815           * @return float
2816           * @since 4.0.012 (2008-07-24)
2817           * @access public
2818           */
2819  		public function getHeaderMargin() {
2820              return $this->header_margin;
2821          }
2822  
2823          /**
2824            * Set footer margin.
2825           * (minimum distance between footer and bottom page margin)
2826           * @param int $fm distance in user units
2827           * @access public
2828           */
2829  		public function setFooterMargin($fm=10) {
2830              $this->footer_margin = $fm;
2831          }
2832  
2833          /**
2834            * Returns footer margin in user units.
2835           * @return float
2836           * @since 4.0.012 (2008-07-24)
2837           * @access public
2838           */
2839  		public function getFooterMargin() {
2840              return $this->footer_margin;
2841          }
2842          /**
2843            * Set a flag to print page header.
2844           * @param boolean $val set to true to print the page header (default), false otherwise.
2845           * @access public
2846           */
2847  		public function setPrintHeader($val=true) {
2848              $this->print_header = $val;
2849          }
2850  
2851          /**
2852            * Set a flag to print page footer.
2853           * @param boolean $value set to true to print the page footer (default), false otherwise.
2854           * @access public
2855           */
2856  		public function setPrintFooter($val=true) {
2857              $this->print_footer = $val;
2858          }
2859  
2860          /**
2861            * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
2862           * @return float
2863           * @access public
2864           */
2865  		public function getImageRBX() {
2866              return $this->img_rb_x;
2867          }
2868  
2869          /**
2870            * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
2871           * @return float
2872           * @access public
2873           */
2874  		public function getImageRBY() {
2875              return $this->img_rb_y;
2876          }
2877  
2878          /**
2879            * This method is used to render the page header.
2880            * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2881           * @access public
2882           */
2883  		public function Header() {
2884              $ormargins = $this->getOriginalMargins();
2885              $headerfont = $this->getHeaderFont();
2886              $headerdata = $this->getHeaderData();
2887              if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
2888                  $this->Image(K_PATH_IMAGES.$headerdata['logo'], $this->GetX(), $this->getHeaderMargin(), $headerdata['logo_width']);
2889                  $imgy = $this->getImageRBY();
2890              } else {
2891                  $imgy = $this->GetY();
2892              }
2893              $cell_height = round(($this->getCellHeightRatio() * $headerfont[2]) / $this->getScaleFactor(), 2);
2894              // set starting margin for text data cell
2895              if ($this->getRTL()) {
2896                  $header_x = $ormargins['right'] + ($headerdata['logo_width'] * 1.1);
2897              } else {
2898                  $header_x = $ormargins['left'] + ($headerdata['logo_width'] * 1.1);
2899              }
2900              $this->SetTextColor(0, 0, 0);
2901              // header title
2902              $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
2903              $this->SetX($header_x);
2904              $this->Cell(0, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
2905              // header string
2906              $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
2907              $this->SetX($header_x);
2908              $this->MultiCell(0, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false);
2909              // print an ending header line
2910              $this->SetLineStyle(array('width' => 0.85 / $this->getScaleFactor(), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2911              $this->SetY((2.835 / $this->getScaleFactor()) + max($imgy, $this->GetY()));
2912              if ($this->getRTL()) {
2913                  $this->SetX($ormargins['right']);
2914              } else {
2915                  $this->SetX($ormargins['left']);
2916              }
2917              $this->Cell(0, 0, '', 'T', 0, 'C');
2918          }
2919  
2920          /**
2921            * This method is used to render the page footer.
2922            * It is automatically called by AddPage() and could be overwritten in your own inherited class.
2923           * @access public
2924           */
2925  		public function Footer() {
2926              $cur_y = $this->GetY();
2927              $ormargins = $this->getOriginalMargins();
2928              $this->SetTextColor(0, 0, 0);
2929              //set style for cell border
2930              $line_width = 0.85 / $this->getScaleFactor();
2931              $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)));
2932              //print document barcode
2933              $barcode = $this->getBarcode();
2934              if (!empty($barcode)) {
2935                  $this->Ln($line_width);
2936                  $barcode_width = round(($this->getPageWidth() - $ormargins['left'] - $ormargins['right'])/3);
2937                  $this->write1DBarcode($barcode, 'C128B', $this->GetX(), $cur_y + $line_width, $barcode_width, (($this->getFooterMargin() / 3) - $line_width), 0.3, '', '');
2938              }
2939              if (empty($this->pagegroups)) {
2940                  $pagenumtxt = $this->l['w_page'].' '.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
2941              } else {
2942                  $pagenumtxt = $this->l['w_page'].' '.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
2943              }
2944              $this->SetY($cur_y);
2945              //Print page number
2946              if ($this->getRTL()) {
2947                  $this->SetX($ormargins['right']);
2948                  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
2949              } else {
2950                  $this->SetX($ormargins['left']);
2951                  $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'R');
2952              }
2953          }
2954  
2955          /**
2956            * This method is used to render the page header.
2957            * @access protected
2958            * @since 4.0.012 (2008-07-24)
2959           */
2960  		protected function setHeader() {
2961              if ($this->print_header) {
2962                  $temp_thead = $this->thead;
2963                  $temp_theadMargins = $this->theadMargins;
2964                  $lasth = $this->lasth;
2965                  $this->_out('q');
2966                  $this->rMargin = $this->original_rMargin;
2967                  $this->lMargin = $this->original_lMargin;
2968                  $this->cMargin = 0;
2969                  //set current position
2970                  if ($this->rtl) {
2971                      $this->SetXY($this->original_rMargin, $this->header_margin);
2972                  } else {
2973                      $this->SetXY($this->original_lMargin, $this->header_margin);
2974                  }
2975                  $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
2976                  $this->Header();
2977                  //restore position
2978                  if ($this->rtl) {
2979                      $this->SetXY($this->original_rMargin, $this->tMargin);
2980                  } else {
2981                      $this->SetXY($this->original_lMargin, $this->tMargin);
2982                  }
2983                  $this->_out('Q');
2984                  $this->lasth = $lasth;
2985                  $this->thead = $temp_thead;
2986                  $this->theadMargins = $temp_theadMargins;
2987                  $this->newline = false;
2988              }
2989          }
2990  
2991          /**
2992            * This method is used to render the page footer.
2993            * @access protected
2994            * @since 4.0.012 (2008-07-24)
2995           */
2996  		protected function setFooter() {
2997              //Page footer
2998              // save current graphic settings
2999              $gvars = $this->getGraphicVars();
3000              // mark this point
3001              $this->footerpos[$this->page] = $this->pagelen[$this->page];
3002              $this->_out("\n");
3003              if ($this->print_footer) {
3004                  $temp_thead = $this->thead;
3005                  $temp_theadMargins = $this->theadMargins;
3006                  $lasth = $this->lasth;
3007                  $this->_out('q');
3008                  $this->rMargin = $this->original_rMargin;
3009                  $this->lMargin = $this->original_lMargin;
3010                  $this->cMargin = 0;
3011                  //set current position
3012                  $footer_y = $this->h - $this->footer_margin;
3013                  if ($this->rtl) {
3014                      $this->SetXY($this->original_rMargin, $footer_y);
3015                  } else {
3016                      $this->SetXY($this->original_lMargin, $footer_y);
3017                  }
3018                  $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3019                  $this->Footer();
3020                  //restore position
3021                  if ($this->rtl) {
3022                      $this->SetXY($this->original_rMargin, $this->tMargin);
3023                  } else {
3024                      $this->SetXY($this->original_lMargin, $this->tMargin);
3025                  }
3026                  $this->_out('Q');
3027                  $this->lasth = $lasth;
3028                  $this->thead = $temp_thead;
3029                  $this->theadMargins = $temp_theadMargins;
3030              }
3031              // restore graphic settings
3032              $this->setGraphicVars($gvars);
3033              // calculate footer length
3034              $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3035          }
3036  
3037          /**
3038            * This method is used to render the table header on new page (if any).
3039            * @access protected
3040            * @since 4.5.030 (2009-03-25)
3041           */
3042  		protected function setTableHeader() {
3043              if ($this->num_columns > 1) {
3044                  // multi column mode
3045                  return;
3046              }
3047              if (isset($this->theadMargins['top'])) {
3048                  // restore the original top-margin
3049                  $this->tMargin = $this->theadMargins['top'];
3050                  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3051                  $this->y = $this->tMargin;
3052              }
3053              if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
3054                  // set margins
3055                  $prev_lMargin = $this->lMargin;
3056                  $prev_rMargin = $this->rMargin;
3057                  $this->lMargin = $this->pagedim[$this->page]['olm'];
3058                  $this->rMargin = $this->pagedim[$this->page]['orm'];
3059                  $this->cMargin = $this->theadMargins['cmargin'];
3060                  if ($this->rtl) {
3061                      $this->x = $this->w - $this->rMargin;
3062                  } else {
3063                      $this->x = $this->lMargin;
3064                  }
3065                  // print table header
3066                  $this->writeHTML($this->thead, false, false, false, false, '');
3067                  // set new top margin to skip the table headers
3068                  if (!isset($this->theadMargins['top'])) {
3069                      $this->theadMargins['top'] = $this->tMargin;
3070                  }
3071                  $this->tMargin = $this->y;
3072                  $this->pagedim[$this->page]['tm'] = $this->tMargin;
3073                  $this->lasth = 0;
3074                  $this->lMargin = $prev_lMargin;
3075                  $this->rMargin = $prev_rMargin;
3076              }
3077          }
3078  
3079          /**
3080           * Returns the current page number.
3081           * @return int page number
3082           * @access public
3083           * @since 1.0
3084           * @see AliasNbPages(), getAliasNbPages()
3085           */
3086  		public function PageNo() {
3087              return $this->page;
3088          }
3089  
3090          /**
3091           * Defines a new spot color.
3092           * It can be expressed in RGB components or gray scale.
3093           * The method can be called before the first page is created and the value is retained from page to page.
3094           * @param int $c Cyan color for CMYK. Value between 0 and 255
3095           * @param int $m Magenta color for CMYK. Value between 0 and 255
3096           * @param int $y Yellow color for CMYK. Value between 0 and 255
3097           * @param int $k Key (Black) color for CMYK. Value between 0 and 255
3098           * @access public
3099           * @since 4.0.024 (2008-09-12)
3100           * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3101           */
3102  		public function AddSpotColor($name, $c, $m, $y, $k) {
3103              if (!isset($this->spot_colors[$name])) {
3104                  $i = 1 + count($this->spot_colors);
3105                  $this->spot_colors[$name] = array('i' => $i, 'c' => $c, 'm' => $m, 'y' => $y, 'k' => $k);
3106              }
3107          }
3108  
3109          /**
3110           * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3111           * It can be expressed in RGB components or gray scale.
3112           * The method can be called before the first page is created and the value is retained from page to page.
3113           * @param array $color array of colors
3114           * @access public
3115           * @since 3.1.000 (2008-06-11)
3116           * @see SetDrawColor()
3117           */
3118  		public function SetDrawColorArray($color) {
3119              if (isset($color)) {
3120                  $color = array_values($color);
3121                  $r = isset($color[0]) ? $color[0] : -1;
3122                  $g = isset($color[1]) ? $color[1] : -1;
3123                  $b = isset($color[2]) ? $color[2] : -1;
3124                  $k = isset($color[3]) ? $color[3] : -1;
3125                  if ($r >= 0) {
3126                      $this->SetDrawColor($r, $g, $b, $k);
3127                  }
3128              }
3129          }
3130  
3131          /**
3132           * 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.
3133           * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
3134           * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
3135           * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
3136           * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
3137           * @access public
3138           * @since 1.3
3139           * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
3140           */
3141  		public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
3142              // set default values
3143              if (!is_numeric($col1)) {
3144                  $col1 = 0;
3145              }
3146              if (!is_numeric($col2)) {
3147                  $col2 = -1;
3148              }
3149              if (!is_numeric($col3)) {
3150                  $col3 = -1;
3151              }
3152              if (!is_numeric($col4)) {
3153                  $col4 = -1;
3154              }
3155              //Set color for all stroking operations
3156              if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3157                  // Grey scale
3158                  $this->DrawColor = sprintf('%.3F G', $col1/255);
3159                  $this->strokecolor = array('G' => $col1);
3160              } elseif ($col4 == -1) {
3161                  // RGB
3162                  $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $col1/255, $col2/255, $col3/255);
3163                  $this->strokecolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3164              } else {
3165                  // CMYK
3166                  $this->DrawColor = sprintf('%.3F %.3F %.3F %.3F K', $col1/100, $col2/100, $col3/100, $col4/100);
3167                  $this->strokecolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3168              }
3169              if ($this->page > 0) {
3170                  $this->_out($this->DrawColor);
3171              }
3172          }
3173  
3174          /**
3175           * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3176           * @param string $name name of the spot color
3177           * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3178           * @access public
3179           * @since 4.0.024 (2008-09-12)
3180           * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3181           */
3182  		public function SetDrawSpotColor($name, $tint=100) {
3183              if (!isset($this->spot_colors[$name])) {
3184                  $this->Error('Undefined spot color: '.$name);
3185              }
3186              $this->DrawColor = sprintf('/CS%d CS %.3F SCN', $this->spot_colors[$name]['i'], $tint/100);
3187              if ($this->page > 0) {
3188                  $this->_out($this->DrawColor);
3189              }
3190          }
3191  
3192          /**
3193           * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3194           * It can be expressed in RGB components or gray scale.
3195           * The method can be called before the first page is created and the value is retained from page to page.
3196           * @param array $color array of colors
3197           * @access public
3198           * @since 3.1.000 (2008-6-11)
3199           * @see SetFillColor()
3200           */
3201  		public function SetFillColorArray($color) {
3202              if (isset($color)) {
3203                  $color = array_values($color);
3204                  $r = isset($color[0]) ? $color[0] : -1;
3205                  $g = isset($color[1]) ? $color[1] : -1;
3206                  $b = isset($color[2]) ? $color[2] : -1;
3207                  $k = isset($color[3]) ? $color[3] : -1;
3208                  if ($r >= 0) {
3209                      $this->SetFillColor($r, $g, $b, $k);
3210                  }
3211              }
3212          }
3213  
3214          /**
3215           * 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.
3216           * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
3217           * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
3218           * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
3219           * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
3220           * @access public
3221           * @since 1.3
3222           * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
3223           */
3224  		public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
3225              // set default values
3226              if (!is_numeric($col1)) {
3227                  $col1 = 0;
3228              }
3229              if (!is_numeric($col2)) {
3230                  $col2 = -1;
3231              }
3232              if (!is_numeric($col3)) {
3233                  $col3 = -1;
3234              }
3235              if (!is_numeric($col4)) {
3236                  $col4 = -1;
3237              }
3238              //Set color for all filling operations
3239              if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3240                  // Grey scale
3241                  $this->FillColor = sprintf('%.3F g', $col1/255);
3242                  $this->bgcolor = array('G' => $col1);
3243              } elseif ($col4 == -1) {
3244                  // RGB
3245                  $this->FillColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
3246                  $this->bgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3247              } else {
3248                  // CMYK
3249                  $this->FillColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
3250                  $this->bgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3251              }
3252              $this->ColorFlag = ($this->FillColor != $this->TextColor);
3253              if ($this->page > 0) {
3254                  $this->_out($this->FillColor);
3255              }
3256          }
3257  
3258          /**
3259           * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3260           * @param string $name name of the spot color
3261           * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3262           * @access public
3263           * @since 4.0.024 (2008-09-12)
3264           * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3265           */
3266  		public function SetFillSpotColor($name, $tint=100) {
3267              if (!isset($this->spot_colors[$name])) {
3268                  $this->Error('Undefined spot color: '.$name);
3269              }
3270              $this->FillColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
3271              $this->ColorFlag = ($this->FillColor != $this->TextColor);
3272              if ($this->page > 0) {
3273                  $this->_out($this->FillColor);
3274              }
3275          }
3276  
3277          /**
3278           * Defines the color used for text. It can be expressed in RGB components or gray scale.
3279           * The method can be called before the first page is created and the value is retained from page to page.
3280           * @param array $color array of colors
3281           * @access public
3282           * @since 3.1.000 (2008-6-11)
3283           * @see SetFillColor()
3284           */
3285  		public function SetTextColorArray($color) {
3286              if (isset($color)) {
3287                  $color = array_values($color);
3288                  $r = isset($color[0]) ? $color[0] : -1;
3289                  $g = isset($color[1]) ? $color[1] : -1;
3290                  $b = isset($color[2]) ? $color[2] : -1;
3291                  $k = isset($color[3]) ? $color[3] : -1;
3292                  if ($r >= 0) {
3293                      $this->SetTextColor($r, $g, $b, $k);
3294                  }
3295              }
3296          }
3297  
3298          /**
3299           * 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.
3300           * @param int $col1 Gray level for single color, or Red color for RGB, or Cyan color for CMYK. Value between 0 and 255
3301           * @param int $col2 Green color for RGB, or Magenta color for CMYK. Value between 0 and 255
3302           * @param int $col3 Blue color for RGB, or Yellow color for CMYK. Value between 0 and 255
3303           * @param int $col4 Key (Black) color for CMYK. Value between 0 and 255
3304           * @access public
3305           * @since 1.3
3306           * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
3307           */
3308  		public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1) {
3309              // set default values
3310              if (!is_numeric($col1)) {
3311                  $col1 = 0;
3312              }
3313              if (!is_numeric($col2)) {
3314                  $col2 = -1;
3315              }
3316              if (!is_numeric($col3)) {
3317                  $col3 = -1;
3318              }
3319              if (!is_numeric($col4)) {
3320                  $col4 = -1;
3321              }
3322              //Set color for text
3323              if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3324                  // Grey scale
3325                  $this->TextColor = sprintf('%.3F g', $col1/255);
3326                  $this->fgcolor = array('G' => $col1);
3327              } elseif ($col4 == -1) {
3328                  // RGB
3329                  $this->TextColor = sprintf('%.3F %.3F %.3F rg', $col1/255, $col2/255, $col3/255);
3330                  $this->fgcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3331              } else {
3332                  // CMYK
3333                  $this->TextColor = sprintf('%.3F %.3F %.3F %.3F k', $col1/100, $col2/100, $col3/100, $col4/100);
3334                  $this->fgcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3335              }
3336              $this->ColorFlag = ($this->FillColor != $this->TextColor);
3337          }
3338  
3339          /**
3340           * Defines the spot color used for text.
3341           * @param string $name name of the spot color
3342           * @param int $tint the intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3343           * @access public
3344           * @since 4.0.024 (2008-09-12)
3345           * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3346           */
3347  		public function SetTextSpotColor($name, $tint=100) {
3348              if (!isset($this->spot_colors[$name])) {
3349                  $this->Error('Undefined spot color: '.$name);
3350              }
3351              $this->TextColor = sprintf('/CS%d cs %.3F scn', $this->spot_colors[$name]['i'], $tint/100);
3352              $this->ColorFlag = ($this->FillColor != $this->TextColor);
3353              if ($this->page > 0) {
3354                  $this->_out($this->TextColor);
3355              }
3356          }
3357  
3358          /**
3359           * Returns the length of a string in user unit. A font must be selected.<br>
3360           * @param string $s The string whose length is to be computed
3361           * @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.
3362           * @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><li>O: overline</li></ul> or any combination. The default value is regular.
3363           * @param float $fontsize Font size in points. The default value is the current size.
3364           * @param boolean $getarray if true returns an array of characters widths, if false returns the total length.
3365           * @return mixed int total string length or array of characted widths
3366           * @author Nicola Asuni
3367           * @access public
3368           * @since 1.2
3369           */
3370  		public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
3371              return $this->GetArrStringWidth($this->utf8Bidi($this->UTF8StringToArray($s), $s, $this->tmprtl), $fontname, $fontstyle, $fontsize, $getarray);
3372          }
3373  
3374          /**
3375           * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
3376           * @param string $sa The array of chars whose total length is to be computed
3377           * @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.
3378           * @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><li>O: overline</li></ul> or any combination. The default value is regular.
3379           * @param float $fontsize Font size in points. The default value is the current size.
3380           * @param boolean $getarray if true returns an array of characters widths, if false returns the total length.
3381           * @return mixed int total string length or array of characted widths
3382           * @author Nicola Asuni
3383           * @access public
3384           * @since 2.4.000 (2008-03-06)
3385           */
3386  		public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
3387              // store current values
3388              if (!$this->empty_string($fontname)) {
3389                  $prev_FontFamily = $this->FontFamily;
3390                  $prev_FontStyle = $this->FontStyle;
3391                  $prev_FontSizePt = $this->FontSizePt;
3392                  $this->SetFont($fontname, $fontstyle, $fontsize);
3393              }
3394              // convert UTF-8 array to Latin1 if required
3395              $sa = $this->UTF8ArrToLatin1($sa);
3396              $w = 0; // total width
3397              $wa = array(); // array of characters widths
3398              foreach ($sa as $char) {
3399                  // character width
3400                  $cw = $this->GetCharWidth($char);
3401                  $wa[] = $cw;
3402                  $w += $cw;
3403              }
3404              // restore previous values
3405              if (!$this->empty_string($fontname)) {
3406                  $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt);
3407              }
3408              if ($getarray) {
3409                  return $wa;
3410              }
3411              return $w;
3412          }
3413  
3414          /**
3415           * Returns the length of the char in user unit for the current font.
3416           * @param int $char The char code whose length is to be returned
3417           * @return int char width
3418           * @author Nicola Asuni
3419           * @access public
3420           * @since 2.4.000 (2008-03-06)
3421           */
3422  		public function GetCharWidth($char) {
3423              if ($char == 173) {
3424                  // SHY character will not be printed
3425                  return (0);
3426              }
3427              $cw = &$this->CurrentFont['cw'];
3428              if (isset($cw[$char])) {
3429                  $w = $cw[$char];
3430              } elseif (isset($this->CurrentFont['dw'])) {
3431                  // default width
3432                  $w = $this->CurrentFont['dw'];
3433              } elseif (isset($cw[32])) {
3434                  // default width
3435                  $w = $cw[32];
3436              } else {
3437                  $w = 600;
3438              }
3439              return ($w * $this->FontSize / 1000);
3440          }
3441  
3442          /**
3443           * Returns the numbero of characters in a string.
3444           * @param string $s The input string.
3445           * @return int number of characters
3446           * @access public
3447           * @since 2.0.0001 (2008-01-07)
3448           */
3449  		public function GetNumChars($s) {
3450              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
3451                  return count($this->UTF8StringToArray($s));
3452              }
3453              return strlen($s);
3454          }
3455  
3456          /**
3457           * Fill the list of available fonts ($this->fontlist).
3458           * @access protected
3459           * @since 4.0.013 (2008-07-28)
3460           */
3461  		protected function getFontsList() {
3462              $fontsdir = opendir($this->_getfontpath());
3463              while (($file = readdir($fontsdir)) !== false) {
3464                  if (substr($file, -4) == '.php') {
3465                      array_push($this->fontlist, strtolower(basename($file, '.php')));
3466                  }
3467              }
3468              closedir($fontsdir);
3469          }
3470  
3471          /**
3472           * Imports a TrueType, Type1, core, or CID0 font and makes it available.
3473           * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
3474           * 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.
3475           * @param string $family Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
3476           * @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>
3477           * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
3478           * @return array containing the font data, or false in case of error.
3479           * @access public
3480           * @since 1.5
3481           * @see SetFont()
3482           */
3483  		public function AddFont($family, $style='', $fontfile='') {
3484              if ($this->empty_string($family)) {
3485                  if (!$this->empty_string($this->FontFamily)) {
3486                      $family = $this->FontFamily;
3487                  } else {
3488                      $this->Error('Empty font family');
3489                  }
3490              }
3491              $family = strtolower($family);
3492              if ((!$this->isunicode) AND ($family == 'arial')) {
3493                  $family = 'helvetica';
3494              }
3495              if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
3496                  $style = '';
3497              }
3498              $tempstyle = strtoupper($style);
3499              $style = '';
3500              // underline
3501              if (strpos($tempstyle, 'U') !== false) {
3502                  $this->underline = true;
3503              } else {
3504                  $this->underline = false;
3505              }
3506              // line-through (deleted)
3507              if (strpos($tempstyle, 'D') !== false) {
3508                  $this->linethrough = true;
3509              } else {
3510                  $this->linethrough = false;
3511              }
3512              // overline
3513              if (strpos($tempstyle, 'O') !== false) {
3514                  $this->overline = true;
3515              } else {
3516                  $this->overline = false;
3517              }
3518              // bold
3519              if (strpos($tempstyle, 'B') !== false) {
3520                  $style .= 'B';
3521              }
3522              // oblique
3523              if (strpos($tempstyle, 'I') !== false) {
3524                  $style .= 'I';
3525              }
3526              $bistyle = $style;
3527              $fontkey = $family.$style;
3528              $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
3529              $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
3530              // check if the font has been already added
3531              if ($this->getFontBuffer($fontkey) !== false) {
3532                  return $fontdata;
3533              }
3534              if (isset($type)) {
3535                  unset($type);
3536              }
3537              if (isset($cw)) {
3538                  unset($cw);
3539              }
3540              // get specified font directory (if any)
3541              $fontdir = false;
3542              if (!$this->empty_string($fontfile)) {
3543                  $fontdir = dirname($fontfile);
3544                  if ($this->empty_string($fontdir) OR ($fontdir == '.')) {
3545                      $fontdir = '';
3546                  } else {
3547                      $fontdir .= '/';
3548                  }
3549              }
3550              // search and include font file
3551              if ($this->empty_string($fontfile) OR (!file_exists($fontfile))) {
3552                  // build a standard filenames for specified font
3553                  $fontfile1 = str_replace(' ', '', $family).strtolower($style).'.php';
3554                  $fontfile2 = str_replace(' ', '', $family).'.php';
3555                  // search files on various directories
3556                  if (($fontdir !== false) AND file_exists($fontdir.$fontfile1)) {
3557                      $fontfile = $fontdir.$fontfile1;
3558                  } elseif (file_exists($this->_getfontpath().$fontfile1)) {
3559                      $fontfile = $this->_getfontpath().$fontfile1;
3560                  } elseif (file_exists($fontfile1)) {
3561                      $fontfile = $fontfile1;
3562                  } elseif (($fontdir !== false) AND file_exists($fontdir.$fontfile2)) {
3563                      $fontfile = $fontdir.$fontfile2;
3564                  } elseif (file_exists($this->_getfontpath().$fontfile2)) {
3565                      $fontfile = $this->_getfontpath().$fontfile2;
3566                  } else {
3567                      $fontfile = $fontfile2;
3568                  }
3569              }
3570              // include font file
3571              if (file_exists($fontfile)) {
3572                  include($fontfile);
3573              } else {
3574                  $this->Error('Could not include font definition file: '.$family.'');
3575              }
3576              // check font parameters
3577              if ((!isset($type)) OR (!isset($cw))) {
3578                  $this->Error('The font definition file has a bad format: '.$fontfile.'');
3579              }
3580              // SET default parameters
3581              if (!isset($file) OR $this->empty_string($file)) {
3582                  $file = '';
3583              }
3584              if (!isset($enc) OR $this->empty_string($enc)) {
3585                  $enc = '';
3586              }
3587              if (!isset($cidinfo) OR $this->empty_string($cidinfo)) {
3588                  $cidinfo = array('Registry'=>'Adobe','Ordering'=>'Identity','Supplement'=>0);
3589                  $cidinfo['uni2cid'] = array();
3590              }
3591              if (!isset($ctg) OR $this->empty_string($ctg)) {
3592                  $ctg = '';
3593              }
3594              if (!isset($desc) OR $this->empty_string($desc)) {
3595                  $desc = array();
3596              }
3597              if (!isset($up) OR $this->empty_string($up)) {
3598                  $up = -100;
3599              }
3600              if (!isset($ut) OR $this->empty_string($ut)) {
3601                  $ut = 50;
3602              }
3603              if (!isset($cw) OR $this->empty_string($cw)) {
3604                  $cw = array();
3605              }
3606              if (!isset($dw) OR $this->empty_string($dw)) {
3607                  // set default width
3608                  if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
3609                      $dw = $desc['MissingWidth'];
3610                  } elseif (isset($cw[32])) {
3611                      $dw = $cw[32];
3612                  } else {
3613                      $dw = 600;
3614                  }
3615              }
3616              ++$this->numfonts;
3617              if ($type == 'cidfont0') {
3618                  // register CID font (all styles at once)
3619                  $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
3620                  $sname = $name.$styles[$bistyle];
3621                  // artificial bold
3622                  if (strpos($bistyle, 'B') !== false) {
3623                      if (isset($desc['StemV'])) {
3624                          $desc['StemV'] *= 2;
3625                      } else {
3626                          $desc['StemV'] = 120;
3627                      }
3628                  }
3629                  // artificial italic
3630                  if (strpos($bistyle, 'I') !== false) {
3631                      if (isset($desc['ItalicAngle'])) {
3632                          $desc['ItalicAngle'] -= 11;
3633                      } else {
3634                          $desc['ItalicAngle'] = -11;
3635                      }
3636                  }
3637              } elseif ($type == 'core') {
3638                  $name = $this->CoreFonts[$fontkey];
3639              } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
3640                  // ...
3641              } elseif ($type == 'TrueTypeUnicode') {
3642                  $enc = 'Identity-H';
3643              } else {
3644                  $this->Error('Unknow font type: '.$type.'');
3645              }
3646              $this->setFontBuffer($fontkey, array('i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg));
3647              if (isset($diff) AND (!empty($diff))) {
3648                  //Search existing encodings
3649                  $d = 0;
3650                  $nb = count($this->diffs);
3651                  for ($i=1; $i <= $nb; ++$i) {
3652                      if ($this->diffs[$i] == $diff) {
3653                          $d = $i;
3654                          break;
3655                      }
3656                  }
3657                  if ($d == 0) {
3658                      $d = $nb + 1;
3659                      $this->diffs[$d] = $diff;
3660                  }
3661                  $this->setFontSubBuffer($fontkey, 'diff', $d);
3662              }
3663              if (!$this->empty_string($file)) {
3664                  if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
3665                      $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir);
3666                  } elseif ($type != 'core') {
3667                      $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir);
3668                  }
3669              }
3670              return $fontdata;
3671          }
3672  
3673          /**
3674           * Sets the font used to print character strings.
3675           * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
3676           * The method can be called before the first page is created and the font is retained from page to page.
3677           * If you just wish to change the current font size, it is simpler to call SetFontSize().
3678           * 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 />
3679           * @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.
3680           * @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><li>O: overline</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.
3681           * @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
3682           * @param string $fontfile The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
3683           * @access public
3684           * @since 1.0
3685           * @see AddFont(), SetFontSize()
3686           */
3687  		public function SetFont($family, $style='', $size=0, $fontfile='') {
3688              //Select a font; size given in points
3689              if ($size == 0) {
3690                  $size = $this->FontSizePt;
3691              }
3692              // try to add font (if not already added)
3693              $fontdata = $this->AddFont($family, $style, $fontfile);
3694              $this->FontFamily = $fontdata['family'];
3695              $this->FontStyle = $fontdata['style'];
3696              $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
3697              $this->SetFontSize($size);
3698          }
3699  
3700          /**
3701           * Defines the size of the current font.
3702           * @param float $size The size (in points)
3703           * @access public
3704           * @since 1.0
3705           * @see SetFont()
3706           */
3707  		public function SetFontSize($size) {
3708              //Set font size in points
3709              $this->FontSizePt = $size;
3710              $this->FontSize = $size / $this->k;
3711              if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
3712                  $this->FontAscent = $this->CurrentFont['desc']['Ascent'] * $this->FontSize / 1000;
3713              } else {
3714                  $this->FontAscent = 0.85 * $this->FontSize;
3715              }
3716              if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
3717                  $this->FontDescent = - $this->CurrentFont['desc']['Descent'] * $this->FontSize / 1000;
3718              } else {
3719                  $this->FontDescent = 0.15 * $this->FontSize;
3720              }
3721              if (($this->page > 0) AND (isset($this->CurrentFont['i']))) {
3722                  $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
3723              }
3724          }
3725  
3726          /**
3727           * Return the font descent value
3728           * @param string $font font name
3729           * @param string $style font style
3730           * @param float $size The size (in points)
3731           * @return int font descent
3732           * @access public
3733           * @since 4.9.003 (2010-03-30)
3734           */
3735  		public function getFontDescent($font, $style='', $size=0) {
3736              //Set font size in points
3737              $sizek = $size / $this->k;
3738              $fontdata = $this->AddFont($font, $style);
3739              if (isset($fontdata['desc']['Descent']) AND ($fontdata['desc']['Descent'] <= 0)) {
3740                  $descent = - $fontdata['desc']['Descent'] * $sizek / 1000;
3741              } else {
3742                  $descent = 0.15 * $sizek;
3743              }
3744              return $descent;
3745          }
3746  
3747          /**
3748           * Return the font ascent value
3749           * @param string $font font name
3750           * @param string $style font style
3751           * @param float $size The size (in points)
3752           * @return int font ascent
3753           * @access public
3754           * @since 4.9.003 (2010-03-30)
3755           */
3756  		public function getFontAscent($font, $style='', $size=0) {
3757              //Set font size in points
3758              $sizek = $size / $this->k;
3759              $fontdata = $this->AddFont($font, $style);
3760              if (isset($fontdata['desc']['Ascent']) AND ($fontdata['desc']['Ascent'] > 0)) {
3761                  $ascent = $fontdata['desc']['Ascent'] * $sizek / 1000;
3762              } else {
3763                  $ascent = 0.85 * $sizek;
3764              }
3765              return $ascent;
3766          }
3767  
3768          /**
3769           * Defines the default monospaced font.
3770           * @param string $font Font name.
3771           * @access public
3772           * @since 4.5.025
3773           */
3774  		public function SetDefaultMonospacedFont($font) {
3775              $this->default_monospaced_font = $font;
3776          }
3777  
3778          /**
3779           * 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 />
3780           * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
3781           * @access public
3782           * @since 1.5
3783           * @see Cell(), Write(), Image(), Link(), SetLink()
3784           */
3785  		public function AddLink() {
3786              //Create a new internal link
3787              $n = count($this->links) + 1;
3788              $this->links[$n] = array(0, 0);
3789              return $n;
3790          }
3791  
3792          /**
3793           * Defines the page and position a link points to.
3794           * @param int $link The link identifier returned by AddLink()
3795           * @param float $y Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
3796           * @param int $page Number of target page; -1 indicates the current page. This is the default value
3797           * @access public
3798           * @since 1.5
3799           * @see AddLink()
3800           */
3801  		public function SetLink($link, $y=0, $page=-1) {
3802              if ($y == -1) {
3803                  $y = $this->y;
3804              }
3805              if ($page == -1) {
3806                  $page = $this->page;
3807              }
3808              $this->links[$link] = array($page, $y);
3809          }
3810  
3811          /**
3812           * Puts a link on a rectangular area of the page.
3813           * 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.
3814           * @param float $x Abscissa of the upper-left corner of the rectangle
3815           * @param float $y Ordinate of the upper-left corner of the rectangle
3816           * @param float $w Width of the rectangle
3817           * @param float $h Height of the rectangle
3818           * @param mixed $link URL or identifier returned by AddLink()
3819           * @param int $spaces number of spaces on the text to link
3820           * @access public
3821           * @since 1.5
3822           * @see AddLink(), Annotation(), Cell(), Write(), Image()
3823           */
3824  		public function Link($x, $y, $w, $h, $link, $spaces=0) {
3825              $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
3826          }
3827  
3828          /**
3829           * Puts a markup annotation on a rectangular area of the page.
3830           * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
3831           * @param float $x Abscissa of the upper-left corner of the rectangle
3832           * @param float $y Ordinate of the upper-left corner of the rectangle
3833           * @param float $w Width of the rectangle
3834           * @param float $h Height of the rectangle
3835           * @param string $text annotation text or alternate content
3836           * @param array $opt array of options (see section 8.4 of PDF reference 1.7).
3837           * @param int $spaces number of spaces on the text to link
3838           * @access public
3839           * @since 4.0.018 (2008-08-06)
3840           */
3841  		public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
3842              if ($x === '') {
3843                  $x = $this->x;
3844              }
3845              if ($y === '') {
3846                  $y = $this->y;
3847              }
3848              // recalculate coordinates to account for graphic transformations
3849              if (isset($this->transfmatrix)) {
3850                  for ($i=$this->transfmatrix_key; $i > 0; --$i) {
3851                      $maxid = count($this->transfmatrix[$i]) - 1;
3852                      for ($j=$maxid; $j >= 0; --$j) {
3853                          $ctm = $this->transfmatrix[$i][$j];
3854                          if (isset($ctm['a'])) {
3855                              $x = $x * $this->k;
3856                              $y = ($this->h - $y) * $this->k;
3857                              $w = $w * $this->k;
3858                              $h = $h * $this->k;
3859                              // top left
3860                              $xt = $x;
3861                              $yt = $y;
3862                              $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3863                              $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3864                              // top right
3865                              $xt = $x + $w;
3866                              $yt = $y;
3867                              $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3868                              $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3869                              // bottom left
3870                              $xt = $x;
3871                              $yt = $y - $h;
3872                              $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3873                              $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3874                              // bottom right
3875                              $xt = $x + $w;
3876                              $yt = $y - $h;
3877                              $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
3878                              $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
3879                              // new coordinates (rectangle area)
3880                              $x = min($x1, $x2, $x3, $x4);
3881                              $y = max($y1, $y2, $y3, $y4);
3882                              $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
3883                              $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
3884                              $x = $x / $this->k;
3885                              $y = $this->h - ($y / $this->k);
3886                          }
3887                      }
3888                  }
3889              }
3890              if ($this->page <= 0) {
3891                  $page = 1;
3892              } else {
3893                  $page = $this->page;
3894              }
3895              if (!isset($this->PageAnnots[$page])) {
3896                  $this->PageAnnots[$page] = array();
3897              }
3898              $this->PageAnnots[$page][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
3899              if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!$this->empty_string($opt['FS'])) AND file_exists($opt['FS']) AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
3900                  $this->embeddedfiles[basename($opt['FS'])] = array('file' => $opt['FS'], 'n' => (count($this->embeddedfiles) + $this->embedded_start_obj_id));
3901              }
3902              // Add widgets annotation's icons
3903              if (isset($opt['mk']['i']) AND file_exists($opt['mk']['i'])) {
3904                  $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
3905              }
3906              if (isset($opt['mk']['ri']) AND file_exists($opt['mk']['ri'])) {
3907                  $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
3908              }
3909              if (isset($opt['mk']['ix']) AND file_exists($opt['mk']['ix'])) {
3910                  $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
3911              }
3912              ++$this->annot_obj_id;
3913          }
3914  
3915          /**
3916           * Embedd the attached files.
3917           * @since 4.4.000 (2008-12-07)
3918           * @access protected
3919           * @see Annotation()
3920           */
3921  		protected function _putEmbeddedFiles() {
3922              reset($this->embeddedfiles);
3923              foreach ($this->embeddedfiles as $filename => $filedata) {
3924                  $data = file_get_contents($filedata['file']);
3925                  $filter = '';
3926                  if ($this->compress) {
3927                      $data = gzcompress($data);
3928                      $filter = ' /Filter /FlateDecode';
3929                  }
3930                  $this->offsets[$filedata['n']] = $this->bufferlen;
3931                  $out = $filedata['n'].' 0 obj';
3932                  $out .= ' <</Type /EmbeddedFile'.$filter.' /Length '.strlen($data).' >>';
3933                  $out .= ' '.$this->_getstream($data, $filedata['n']);
3934                  $out .= ' endobj';
3935                  $this->_out($out);
3936              }
3937          }
3938  
3939          /**
3940           * Prints a text cell at the specified position.
3941           * The origin is on the left of the first charcter, on the baseline.
3942           * This method allows to place a string precisely on the page.
3943           * @param float $x Abscissa of the cell origin
3944           * @param float $y Ordinate of the cell origin
3945           * @param string $txt String to print
3946           * @param int $fstroke outline size in user units (false = disable)
3947           * @param boolean $fclip if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
3948           * @param boolean $ffill if true fills the text
3949           * @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>
3950           * @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.
3951           * @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>
3952           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
3953           * @param mixed $link URL or identifier returned by AddLink().
3954           * @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>
3955           * @param boolean $ignore_min_height if true ignore automatic minimum height value.
3956           * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li><li>B : cell bottom</li></ul>
3957           * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
3958           * @param boolean $rtloff if true uses the page top-left corner as origin of axis for $x and $y initial position.
3959           * @access public
3960           * @since 1.0
3961           * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
3962           */
3963  		public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
3964              $textrendermode = $this->textrendermode;
3965              $textstrokewidth = $this->textstrokewidth;
3966              $this->setTextRenderingMode($fstroke, $ffill, $fclip);
3967              $this->SetXY($x, $y, $rtloff);
3968              $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
3969              // restore previous rendering mode
3970              $this->textrendermode = $textrendermode;
3971              $this->textstrokewidth = $textstrokewidth;
3972          }
3973  
3974          /**
3975           * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
3976           * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
3977           * This method is called automatically and should not be called directly by the application.
3978           * @return boolean
3979           * @access public
3980           * @since 1.4
3981           * @see SetAutoPageBreak()
3982           */
3983  		public function AcceptPageBreak() {
3984              if ($this->num_columns > 1) {
3985                  // multi column mode
3986                  if($this->current_column < ($this->num_columns - 1)) {
3987                      // go to next column
3988                      $this->selectColumn($this->current_column + 1);
3989                  } else {
3990                      // add a new page
3991                      $this->AddPage();
3992                      // set first column
3993                      $this->selectColumn(0);
3994                  }
3995                  // avoid page breaking from checkPageBreak()
3996                  return false;
3997              }
3998              return $this->AutoPageBreak;
3999          }
4000  
4001          /**
4002           * Add page if needed.
4003           * @param float $h Cell height. Default value: 0.
4004           * @param mixed $y starting y position, leave empty for current position.
4005           * @param boolean $addpage if true add a page, otherwise only return the true/false state
4006           * @return boolean true in case of page break, false otherwise.
4007           * @since 3.2.000 (2008-07-01)
4008           * @access protected
4009           */
4010  		protected function checkPageBreak($h=0, $y='', $addpage=true) {
4011              if ($this->empty_string($y)) {
4012                  $y = $this->y;
4013              }
4014              if ((($y + $h) > $this->PageBreakTrigger) AND (!$this->InFooter) AND ($this->AcceptPageBreak())) {
4015                  if ($addpage) {
4016                      //Automatic page break
4017                      $x = $this->x;
4018                      $this->AddPage($this->CurOrientation);
4019                      $this->y = $this->tMargin;
4020                      $oldpage = $this->page - 1;
4021                      if ($this->rtl) {
4022                          if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
4023                              $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
4024                          } else {
4025                              $this->x = $x;
4026                          }
4027                      } else {
4028                          if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
4029                              $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
4030                          } else {
4031                              $this->x = $x;
4032                          }
4033                      }
4034                  }
4035                  return true;
4036              }
4037              return false;
4038          }
4039  
4040          /**
4041           * 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 />
4042           * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
4043           * @param float $w Cell width. If 0, the cell extends up to the right margin.
4044           * @param float $h Cell height. Default value: 0.
4045           * @param string $txt String to print. Default value: empty string.
4046           * @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>
4047           * @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>
4048          Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
4049           * @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>
4050           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
4051           * @param mixed $link URL or identifier returned by AddLink().
4052           * @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>
4053           * @param boolean $ignore_min_height if true ignore automatic minimum height value.
4054           * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
4055           * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4056           * @access public
4057           * @since 1.0
4058           * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
4059           */
4060  		public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
4061              if (!$ignore_min_height) {
4062                  $min_cell_height = $this->FontSize * $this->cell_height_ratio;
4063                  if ($h < $min_cell_height) {
4064                      $h = $min_cell_height;
4065                  }
4066              }
4067              $this->checkPageBreak($h);
4068              $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign));
4069          }
4070  
4071          /**
4072           * Removes SHY characters from text.
4073           * @param string $txt input string
4074           * @return string without SHY characters.
4075           * @access public
4076           * @since (4.5.019) 2009-02-28
4077           */
4078  		public function removeSHY($txt='') {
4079              /*
4080              * Unicode Data
4081              * Name : SOFT HYPHEN, commonly abbreviated as SHY
4082              * HTML Entity (decimal): &#173;
4083              * HTML Entity (hex): &#xad;
4084              * HTML Entity (named): &shy;
4085              * How to type in Microsoft Windows: [Alt +00AD] or [Alt 0173]
4086              * UTF-8 (hex): 0xC2 0xAD (c2ad)
4087              * UTF-8 character: chr(194).chr(173)
4088              */
4089              $txt = preg_replace('/([\\xc2]{1}[\\xad]{1})/', '', $txt);
4090              if (!$this->isunicode) {
4091                  $txt = preg_replace('/([\\xad]{1})/', '', $txt);
4092              }
4093              return $txt;
4094          }
4095  
4096          /**
4097           * 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 />
4098           * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
4099           * @param float $w Cell width. If 0, the cell extends up to the right margin.
4100           * @param float $h Cell height. Default value: 0.
4101           * @param string $txt String to print. Default value: empty string.
4102           * @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>
4103           * @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.
4104           * @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>
4105           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
4106           * @param mixed $link URL or identifier returned by AddLink().
4107           * @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>
4108           * @param boolean $ignore_min_height if true ignore automatic minimum height value.
4109           * @param string $calign cell vertical alignment relative to the specified Y value. Possible values are:<ul><li>T : cell top</li><li>C : center</li><li>B : cell bottom</li><li>A : font top</li><li>L : font baseline</li><li>D : font bottom</li></ul>
4110           * @param string $valign text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4111           * @access protected
4112           * @since 1.0
4113           * @see Cell()
4114           */
4115  		protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=0, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
4116              $txt = $this->removeSHY($txt);
4117              $rs = ''; //string to be returned
4118              if (!$ignore_min_height) {
4119                  $min_cell_height = $this->FontSize * $this->cell_height_ratio;
4120                  if ($h < $min_cell_height) {
4121                      $h = $min_cell_height;
4122                  }
4123              }
4124              $k = $this->k;
4125              $x = $this->x;
4126              $y = $this->y;
4127              // cell vertical alignment
4128              switch ($calign) {
4129                  case 'A': {
4130                      // font top
4131                      switch ($valign) {
4132                          case 'T': {
4133                              // top
4134                              $y -= ($this->LineWidth / 2);
4135                              break;
4136                          }
4137                          case 'B': {
4138                              // bottom
4139                              $y -= ($h - $this->FontAscent - $this->FontDescent - ($this->LineWidth / 2));
4140                              break;
4141                          }
4142                          default:
4143                          case 'M': {
4144                              // center
4145                              $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
4146                              break;
4147                          }
4148                      }
4149                      break;
4150                  }
4151                  case 'L': {
4152                      // font baseline
4153                      switch ($valign) {
4154                          case 'T': {
4155                              // top
4156                              $y -= ($this->FontAscent + ($this->LineWidth / 2));
4157                              break;
4158                          }
4159                          case 'B': {
4160                              // bottom
4161                              $y -= ($h - $this->FontDescent - ($this->LineWidth / 2));
4162                              break;
4163                          }
4164                          default:
4165                          case 'M': {
4166                              // center
4167                              $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
4168                              break;
4169                          }
4170                      }
4171                      break;
4172                  }
4173                  case 'D': {
4174                      // font bottom
4175                      switch ($valign) {
4176                          case 'T': {
4177                              // top
4178                              $y -= ($this->FontAscent + $this->FontDescent + ($this->LineWidth / 2));
4179                              break;
4180                          }
4181                          case 'B': {
4182                              // bottom
4183                              $y -= ($h - ($this->LineWidth / 2));
4184                              break;
4185                          }
4186                          default:
4187                          case 'M': {
4188                              // center
4189                              $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
4190                              break;
4191                          }
4192                      }
4193                      break;
4194                  }
4195                  case 'B': {
4196                      // cell bottom
4197                      $y -= $h;
4198                      break;
4199                  }
4200                  case 'C': {
4201                      // cell center
4202                      $y -= ($h / 2);
4203                      break;
4204                  }
4205                  default:
4206                  case 'T': {
4207                      // cell top
4208                      break;
4209                  }
4210              }
4211              // text vertical alignment
4212              switch ($valign) {
4213                  case 'T': {
4214                      // top
4215                      $basefonty = $y + $this->FontAscent + ($this->LineWidth / 2);
4216                      break;
4217                  }
4218                  case 'B': {
4219                      // bottom
4220                      $basefonty = $y + $h - $this->FontDescent - ($this->LineWidth / 2);
4221                      break;
4222                  }
4223                  default:
4224                  case 'M': {
4225                      // center
4226                      $basefonty = $y + (($h + $this->FontAscent - $this->FontDescent) / 2);
4227                      break;
4228                  }
4229              }
4230              if ($this->empty_string($w) OR ($w <= 0)) {
4231                  if ($this->rtl) {
4232                      $w = $x - $this->lMargin;
4233                  } else {
4234                      $w = $this->w - $this->rMargin - $x;
4235                  }
4236              }
4237              $s = '';
4238              // fill and borders
4239              if (($fill == 1) OR ($border == 1)) {
4240                  if ($fill == 1) {
4241                      $op = ($border == 1) ? 'B' : 'f';
4242                  } else {
4243                      $op = 'S';
4244                  }
4245                  if ($this->rtl) {
4246                      $xk = (($this->x  - $w) * $k);
4247                  } else {
4248                      $xk = ($this->x * $k);
4249                  }
4250                  $s .= sprintf('%.2F %.2F %.2F %.2F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
4251              }
4252              if (is_string($border)) {
4253                  $lm = ($this->LineWidth / 2);
4254                  if (strpos($border,'L') !== false) {
4255                      if ($this->rtl) {
4256                          $xk = ($x - $w) * $k;
4257                      } else {
4258                          $xk = $x * $k;
4259                      }
4260                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm)) * $k));
4261                  }
4262                  if (strpos($border,'T') !== false) {
4263                      if ($this->rtl) {
4264                          $xk = ($x - $w + $lm) * $k;
4265                          $xwk = ($x - $lm) * $k;
4266                      } else {
4267                          $xk = ($x - $lm) * $k;
4268                          $xwk = ($x + $w + $lm) * $k;
4269                      }
4270                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y) * $k), $xwk, (($this->h - $y) * $k));
4271                  }
4272                  if (strpos($border,'R') !== false) {
4273                      if ($this->rtl) {
4274                          $xk = $x * $k;
4275                      } else {
4276                          $xk = ($x + $w) * $k;
4277                      }
4278                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - $y + $lm) * $k), $xk, (($this->h - ($y + $h + $lm))* $k));
4279                  }
4280                  if (strpos($border,'B') !== false) {
4281                      if ($this->rtl) {
4282                          $xk = ($x - $w + $lm) * $k;
4283                          $xwk = ($x - $lm) * $k;
4284                      } else {
4285                          $xk = ($x - $lm) * $k;
4286                          $xwk = ($x + $w + $lm) * $k;
4287                      }
4288                      $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $xk, (($this->h - ($y + $h)) * $k), $xwk, (($this->h - ($y + $h)) * $k));
4289                  }
4290              }
4291              if ($txt != '') {
4292                  $txt2 = $txt;
4293                  if ($this->isunicode) {
4294                      if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
4295                          $txt2 = $this->UTF8ToLatin1($txt2);
4296                      } else {
4297                          $unicode = $this->UTF8StringToArray($txt); // array of UTF-8 unicode values
4298                          $unicode = $this->utf8Bidi($unicode, '', $this->tmprtl);
4299                          if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
4300                              // ---- Fix for bug #2977340 "Incorrect Thai characters position arrangement" ----
4301                              // NOTE: this doesn't work with HTML justification
4302                              // Symbols that could overlap on the font top (only works in LTR)
4303                              $topchar = array(3611, 3613, 3615, 3650, 3651, 3652); // chars that extends on top
4304                              $topsym = array(3633, 3636, 3637, 3638, 3639, 3655, 3656, 3657, 3658, 3659, 3660, 3661, 3662); // symbols with top position
4305                              $numchars = count($unicode); // number of chars
4306                              $unik = 0;
4307                              $uniblock = array();
4308                              $uniblock[$unik] = array();
4309                              $uniblock[$unik][] = $unicode[0];
4310                              // resolve overlapping conflicts by splitting the string in several parts
4311                              for ($i = 1; $i < $numchars; ++$i) {
4312                                  // check if symbols overlaps at top
4313                                  if (in_array($unicode[$i], $topsym) AND (in_array($unicode[($i - 1)], $topsym) OR in_array($unicode[($i - 1)], $topchar))) {
4314                                      // move symbols to another array
4315                                      ++$unik;
4316                                      $uniblock[$unik] = array();
4317                                      $uniblock[$unik][] = $unicode[$i];
4318                                      ++$unik;
4319                                      $uniblock[$unik] = array();
4320                                      $unicode[$i] = 8203; // Unicode Character 'ZERO WIDTH SPACE' (U+200B)
4321                                  } else {
4322                                      $uniblock[$unik][] = $unicode[$i];
4323                                  }
4324                              }
4325                              // ---- END OF Fix for bug #2977340
4326                          }
4327                          $txt2 = $this->arrUTF8ToUTF16BE($unicode, false);
4328                      }
4329                  }
4330                  $txt2 = $this->_escape($txt2);
4331                  // text length
4332                  $txwidth = $this->GetStringWidth($txt);
4333                  $width = $txwidth;
4334                  // ratio between cell length and text length
4335                  if ($width <= 0) {
4336                      $ratio = 1;
4337                  } else {
4338                      $ratio = ($w - (2 * $this->cMargin)) / $width;
4339                  }
4340                  // stretch text if required
4341                  if (($stretch > 0) AND (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0)))) {
4342                      if ($stretch > 2) {
4343                          // spacing
4344                          //Calculate character spacing in points
4345                          $char_space = (($w - $width - (2 * $this->cMargin)) * $this->k) / max($this->GetNumChars($txt)-1,1);
4346                          //Set character spacing
4347                          $rs .= sprintf('BT %.2F Tc ET ', $char_space);
4348                      } else {
4349                          // scaling
4350                          //Calculate horizontal scaling
4351                          $horiz_scale = $ratio * 100.0;
4352                          //Set horizontal scaling
4353                          $rs .= sprintf('BT %.2F Tz ET ', $horiz_scale);
4354                      }
4355                      $align = '';
4356                      $width = $w - (2 * $this->cMargin);
4357                  } else {
4358                      $stretch == 0;
4359                  }
4360                  if ($this->ColorFlag) {
4361                      $s .= 'q '.$this->TextColor.' ';
4362                  }
4363                  // rendering mode
4364                  $s .= sprintf('BT %d Tr %.2F w ET ', $this->textrendermode, $this->textstrokewidth);
4365                  // count number of spaces
4366                  $ns = substr_count($txt, ' ');
4367                  // Justification
4368                  $spacewidth = 0;
4369                  if (($align == 'J') AND ($ns > 0)) {
4370                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
4371                              // get string width without spaces
4372                              $width = $this->GetStringWidth(str_replace(' ', '', $txt));
4373                              // calculate average space width
4374                              $spacewidth = -1000 * ($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1) / $this->FontSize;
4375                              // set word position to be used with TJ operator
4376                              $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%.3F', $spacewidth).' (', $txt2);
4377                              $unicode_justification = true;
4378                      } else {
4379                          // get string width
4380                          $width = $txwidth;
4381                          $spacewidth = (($w - $width - (2 * $this->cMargin)) / ($ns?$ns:1)) * $this->k;
4382                          // set word spacing
4383                          $rs .= sprintf('BT %.3F Tw ET ', $spacewidth);
4384                      }
4385                      $width = $w - (2 * $this->cMargin);
4386                  }
4387                  // replace carriage return characters
4388                  $txt2 = str_replace("\r", ' ', $txt2);
4389                  switch ($align) {
4390                      case 'C': {
4391                          $dx = ($w - $width) / 2;
4392                          break;
4393                      }
4394                      case 'R': {
4395                          if ($this->rtl) {
4396                              $dx = $this->cMargin;
4397                          } else {
4398                              $dx = $w - $width - $this->cMargin;
4399                          }
4400                          break;
4401                      }
4402                      case 'L': {
4403                          if ($this->rtl) {
4404                              $dx = $w - $width - $this->cMargin;
4405                          } else {
4406                              $dx = $this->cMargin;
4407                          }
4408                          break;
4409                      }
4410                      case 'J':
4411                      default: {
4412                          $dx = $this->cMargin;
4413                          break;
4414                      }
4415                  }
4416                  if ($this->rtl) {
4417                      $xdx = $this->x - $dx - $width;
4418                  } else {
4419                      $xdx = $this->x + $dx;
4420                  }
4421                  $xdk = $xdx * $k;
4422                  // print text
4423                  $s .= sprintf('BT %.2F %.2F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
4424                  if (isset($uniblock)) {
4425                      // print overlapping characters as separate string
4426                      $xshift = 0; // horizontal shift
4427                      $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
4428                      $spw = (($w - $txwidth - (2 * $this->cMargin)) / ($ns?$ns:1));
4429                      foreach ($uniblock as $uk => $uniarr) {
4430                          if (($uk % 2) == 0) {
4431                              // x space to skip
4432                              if ($spacewidth != 0) {
4433                                  // justification shift
4434                                  $xshift += (count(array_keys($uniarr, 32)) * $spw);
4435                              }
4436                              $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
4437                          } else {
4438                              // character to print
4439                              $topchr = $this->arrUTF8ToUTF16BE($uniarr, false);
4440                              $topchr = $this->_escape($topchr);
4441                              $s .= sprintf(' BT %.2F %.2F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
4442                          }
4443                      }
4444                  }
4445                  if ($this->underline)  {
4446                      $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
4447                  }
4448                  if ($this->linethrough) {
4449                      $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
4450                  }
4451                  if ($this->overline)  {
4452                      $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
4453                  }
4454                  if ($this->ColorFlag) {
4455                      $s .= ' Q';
4456                  }
4457                  if ($link) {
4458                      $this->Link($xdx, $y + (($h - $this->FontSize)/2), $width, $this->FontSize, $link, $ns);
4459                  }
4460              }
4461              // output cell
4462              if ($s) {
4463                  // output cell
4464                  $rs .= $s;
4465                  // reset text stretching
4466                  if ($stretch > 2) {
4467                      //Reset character horizontal spacing
4468                      $rs .= ' BT 0 Tc ET';
4469                  } elseif ($stretch > 0) {
4470                      //Reset character horizontal scaling
4471                      $rs .= ' BT 100 Tz ET';
4472                  }
4473              }
4474              // reset word spacing
4475              if (!(($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($align == 'J')) {
4476                  $rs .= ' BT 0 Tw ET';
4477              }
4478              $this->lasth = $h;
4479              if ($ln > 0) {
4480                  //Go to the beginning of the next line
4481                  $this->y = $y + $h;
4482                  if ($ln == 1) {
4483                      if ($this->rtl) {
4484                          $this->x = $this->w - $this->rMargin;
4485                      } else {
4486                          $this->x = $this->lMargin;
4487                      }
4488                  }
4489              } else {
4490                  // go left or right by case
4491                  if ($this->rtl) {
4492                      $this->x -= $w;
4493                  } else {
4494                      $this->x += $w;
4495                  }
4496              }
4497              $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
4498              $rs = $gstyles.$rs;
4499              return $rs;
4500          }
4501  
4502          /**
4503           * This method allows printing text with line breaks.
4504           * 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 />
4505           * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
4506           * @param float $w Width of cells. If 0, they extend up to the right margin of the page.
4507           * @param float $h Cell minimum height. The cell extends automatically if needed.
4508           * @param string $txt String to print
4509           * @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>
4510           * @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>
4511           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
4512           * @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>
4513           * @param float $x x position in user units
4514           * @param float $y y position in user units
4515           * @param boolean $reseth if true reset the last cell height (default true).
4516           * @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>
4517           * @param boolean $ishtml set to true if $txt is HTML content (default = false).
4518           * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
4519           * @param float $maxh maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature. This feature works only when $ishtml=false.
4520           * @return int Return the number of cells or 1 for html mode.
4521           * @access public
4522           * @since 1.3
4523           * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
4524           */
4525  		public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=0, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0) {
4526              if ($this->empty_string($this->lasth) OR $reseth) {
4527                  //set row height
4528                  $this->lasth = $this->FontSize * $this->cell_height_ratio;
4529              }
4530              if (!$this->empty_string($y)) {
4531                  $this->SetY($y);
4532              } else {
4533                  $y = $this->GetY();
4534              }
4535              // check for page break
4536              $this->checkPageBreak($h);
4537              $y = $this->GetY();
4538              // get current page number
4539              $startpage = $this->page;
4540              if (!$this->empty_string($x)) {
4541                  $this->SetX($x);
4542              } else {
4543                  $x = $this->GetX();
4544              }
4545              if ($this->empty_string($w) OR ($w <= 0)) {
4546                  if ($this->rtl) {
4547                      $w = $this->x - $this->lMargin;
4548                  } else {
4549                      $w = $this->w - $this->rMargin - $this->x;
4550                  }
4551              }
4552              // store original margin values
4553              $lMargin = $this->lMargin;
4554              $rMargin = $this->rMargin;
4555              if ($this->rtl) {
4556                  $this->SetRightMargin($this->w - $this->x);
4557                  $this->SetLeftMargin($this->x - $w);
4558              } else {
4559                  $this->SetLeftMargin($this->x);
4560                  $this->SetRightMargin($this->w - $this->x - $w);
4561              }
4562              $starty = $this->y;
4563              if ($autopadding) {
4564                  // Adjust internal padding
4565                  if ($this->cMargin < ($this->LineWidth / 2)) {
4566                      $this->cMargin = ($this->LineWidth / 2);
4567                  }
4568                  // Add top space if needed
4569                  if (($this->lasth - $this->FontSize) < $this->LineWidth) {
4570                      $this->y += $this->LineWidth / 2;
4571                  }
4572                  // add top padding
4573                  $this->y += $this->cMargin;
4574              }
4575              if ($ishtml) {
4576                  // ******* Write HTML text
4577                  $this->writeHTML($txt, true, 0, $reseth, true, $align);
4578                  $nl = 1;
4579              } else {
4580                  // ******* Write text
4581                  $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh);
4582              }
4583              if ($autopadding) {
4584                  // add bottom padding
4585                  $this->y += $this->cMargin;
4586                  // Add bottom space if needed
4587                  if (($this->lasth - $this->FontSize) < $this->LineWidth) {
4588                      $this->y += $this->LineWidth / 2;
4589                  }
4590              }
4591              // Get end-of-text Y position
4592              $currentY = $this->y;
4593              // get latest page number
4594              $endpage = $this->page;
4595              // check if a new page has been created
4596              if ($endpage > $startpage) {
4597                  // design borders around HTML cells.
4598                  for ($page=$startpage; $page <= $endpage; ++$page) {
4599                      $this->setPage($page);
4600                      if ($page == $startpage) {
4601                          $this->y = $starty; // put cursor at the beginning of cell on the first page
4602                          $h = $this->getPageHeight() - $starty - $this->getBreakMargin();
4603                          $cborder = $this->getBorderMode($border, $position='start');
4604                      } elseif ($page == $endpage) {
4605                          $this->y = $this->tMargin; // put cursor at the beginning of last page
4606                          $h = $currentY - $this->tMargin;
4607                          $cborder = $this->getBorderMode($border, $position='end');
4608                      } else {
4609                          $this->y = $this->tMargin; // put cursor at the beginning of the current page
4610                          $h = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
4611                          $cborder = $this->getBorderMode($border, $position='middle');
4612                      }
4613                      $nx = $x;
4614                      // account for margin changes
4615                      if ($page > $startpage) {
4616                          if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
4617                              $nx = $x + ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
4618                          } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
4619                              $nx = $x + ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
4620                          }
4621                      }
4622                      $this->SetX($nx);
4623                      $ccode = $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, false);
4624                      if ($cborder OR $fill) {
4625                          $pagebuff = $this->getPageBuffer($this->page);
4626                          $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
4627                          $pend = substr($pagebuff, $this->intmrk[$this->page]);
4628                          $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
4629                          $this->intmrk[$this->page] += strlen($ccode."\n");
4630                      }
4631                  }
4632              } else {
4633                  $h = max($h, ($currentY - $y));
4634                  // put cursor at the beginning of text
4635                  $this->SetY($y);
4636                  $this->SetX($x);
4637                  // design a cell around the text
4638                  $ccode = $this->getCellCode($w, $h, '', $border, 1, '', $fill, '', 0, true);
4639                  if ($border OR $fill) {
4640                      if (end($this->transfmrk[$this->page]) !== false) {
4641                          $pagemarkkey = key($this->transfmrk[$this->page]);
4642                          $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
4643                      } elseif ($this->InFooter) {
4644                          $pagemark = &$this->footerpos[$this->page];
4645                      } else {
4646                          $pagemark = &$this->intmrk[$this->page];
4647                      }
4648                      $pagebuff = $this->getPageBuffer($this->page);
4649                      $pstart = substr($pagebuff, 0, $pagemark);
4650                      $pend = substr($pagebuff, $pagemark);
4651                      $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
4652                      $pagemark += strlen($ccode."\n");
4653                  }
4654              }
4655              // Get end-of-cell Y position
4656              $currentY = $this->GetY();
4657              // restore original margin values
4658              $this->SetLeftMargin($lMargin);
4659              $this->SetRightMargin($rMargin);
4660              if ($ln > 0) {
4661                  //Go to the beginning of the next line
4662                  $this->SetY($currentY);
4663                  if ($ln == 2) {
4664                      $this->SetX($x + $w);
4665                  }
4666              } else {
4667                  // go left or right by case
4668                  $this->setPage($startpage);
4669                  $this->y = $y;
4670                  $this->SetX($x + $w);
4671              }
4672              $this->setContentMark();
4673              return $nl;
4674          }
4675  
4676          /**
4677           * Get the border mode accounting for multicell position (opens bottom side of multicell crossing pages)
4678           * @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>
4679           * @param string multicell position: 'start', 'middle', 'end'
4680           * @return border mode
4681           * @access protected
4682           * @since 4.4.002 (2008-12-09)
4683           */
4684  		protected function getBorderMode($border, $position='start') {
4685              if ((!$this->opencell) AND ($border == 1)) {
4686                  return 1;
4687              }
4688              $cborder = '';
4689              switch ($position) {
4690                  case 'start': {
4691                      if ($border == 1) {
4692                          $cborder = 'LTR';
4693                      } else {
4694                          if (!(false === strpos($border, 'L'))) {
4695                              $cborder .= 'L';
4696                          }
4697                          if (!(false === strpos($border, 'T'))) {
4698                              $cborder .= 'T';
4699                          }
4700                          if (!(false === strpos($border, 'R'))) {
4701                              $cborder .= 'R';
4702                          }
4703                          if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
4704                              $cborder .= 'B';
4705                          }
4706                      }
4707                      break;
4708                  }
4709                  case 'middle': {
4710                      if ($border == 1) {
4711                          $cborder = 'LR';
4712                      } else {
4713                          if (!(false === strpos($border, 'L'))) {
4714                              $cborder .= 'L';
4715                          }
4716                          if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
4717                              $cborder .= 'T';
4718                          }
4719                          if (!(false === strpos($border, 'R'))) {
4720                              $cborder .= 'R';
4721                          }
4722                          if ((!$this->opencell) AND (!(false === strpos($border, 'B')))) {
4723                              $cborder .= 'B';
4724                          }
4725                      }
4726                      break;
4727                  }
4728                  case 'end': {
4729                      if ($border == 1) {
4730                          $cborder = 'LRB';
4731                      } else {
4732                          if (!(false === strpos($border, 'L'))) {
4733                              $cborder .= 'L';
4734                          }
4735                          if ((!$this->opencell) AND (!(false === strpos($border, 'T')))) {
4736                              $cborder .= 'T';
4737                          }
4738                          if (!(false === strpos($border, 'R'))) {
4739                              $cborder .= 'R';
4740                          }
4741                          if (!(false === strpos($border, 'B'))) {
4742                              $cborder .= 'B';
4743                          }
4744                      }
4745                      break;
4746                  }
4747                  default: {
4748                      $cborder = $border;
4749                      break;
4750                  }
4751              }
4752              return $cborder;
4753          }
4754  
4755          /**
4756           * This method returns the estimated number of lines required to print the text (not the real number just a quick estimation).
4757           * If you want o know the exact number of lines you have to use the following technique:
4758           * <pre>
4759           *  // store current object
4760           *  $pdf->startTransaction();
4761           *  // get the number of lines for multicell
4762           *  $lines = $pdf->MultiCell($w, 0, $txt, 0, 'L', 0, 0, '', '', true, 0, false, true, 0);
4763           *  // restore previous object
4764           *  $pdf = $pdf->rollbackTransaction();
4765           * </pre>
4766           * @param string $txt text to print
4767           * @param float $w width of cell. If 0, they extend up to the right margin of the page.
4768           * @return int Return the estimated number of lines.
4769           * @access public
4770           * @since 4.5.011
4771           */
4772  		public function getNumLines($txt, $w=0) {
4773              $lines = 0;
4774              if ($this->empty_string($w) OR ($w <= 0)) {
4775                  if ($this->rtl) {
4776                      $w = $this->x - $this->lMargin;
4777                  } else {
4778                      $w = $this->w - $this->rMargin - $this->x;
4779                  }
4780              }
4781              // max column width
4782              $wmax = $w - (2 * $this->cMargin);
4783              // remove carriage returns
4784              $txt = str_replace("\r", '', $txt);
4785              // remove last newline (if any)
4786              if (substr($txt,-1) == "\n") {
4787                  $txt = substr($txt, 0, -1);
4788              }
4789              // divide text in blocks
4790              $txtblocks = explode("\n", $txt);
4791              // for each text block
4792              foreach ($txtblocks as $block) {
4793                  // estimate the number of lines
4794                  $lines += $this->empty_string($block) ? 1 : (ceil($this->GetStringWidth($block) / $wmax));
4795              }
4796              // return the number of lines
4797              return $lines;
4798          }
4799  
4800          /**
4801           * This method prints text from the current position.<br />
4802           * @param float $h Line height
4803           * @param string $txt String to print
4804           * @param mixed $link URL or identifier returned by AddLink()
4805           * @param int $fill Indicates if the background must be painted (1) or transparent (0). Default value: 0.
4806           * @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>
4807           * @param boolean $ln if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
4808           * @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>
4809           * @param boolean $firstline if true prints only the first line and return the remaining string.
4810           * @param boolean $firstblock if true the string is the starting of a line.
4811           * @param float $maxh maximum height. The remaining unprinted text will be returned. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
4812           * @return mixed Return the number of cells or the remaining string if $firstline = true.
4813           * @access public
4814           * @since 1.5
4815           */
4816  		public function Write($h, $txt, $link='', $fill=0, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0) {
4817              if (strlen($txt) == 0) {
4818                  $txt = ' ';
4819              }
4820              // remove carriage returns
4821              $s = str_replace("\r", '', $txt);
4822              // check if string contains arabic text
4823              if (preg_match(K_RE_PATTERN_ARABIC, $s)) {
4824                  $arabic = true;
4825              } else {
4826                  $arabic = false;
4827              }
4828              // check if string contains RTL text
4829              if ($arabic OR ($this->tmprtl == 'R') OR preg_match(K_RE_PATTERN_RTL, $txt)) {
4830                  $rtlmode = true;
4831              } else {
4832                  $rtlmode = false;
4833              }
4834              // get a char width
4835              $chrwidth = $this->GetCharWidth('.');
4836              // get array of unicode values
4837              $chars = $this->UTF8StringToArray($s);
4838              // get array of chars
4839              $uchars = $this->UTF8ArrayToUniArray($chars);
4840              // get the number of characters
4841              $nb = count($chars);
4842              // replacement for SHY character (minus symbol)
4843              $shy_replacement = 45;
4844              $shy_replacement_char = $this->unichr($shy_replacement);
4845              // widht for SHY replacement
4846              $shy_replacement_width = $this->GetCharWidth($shy_replacement);
4847              // store current position
4848              $prevx = $this->x;
4849              $prevy = $this->y;
4850              // max Y
4851              $maxy = $this->y + $maxh - $h - (2 * $this->cMargin);
4852              // calculate remaining line width ($w)
4853              if ($this->rtl) {
4854                  $w = $this->x - $this->lMargin;
4855              } else {
4856                  $w = $this->w - $this->rMargin - $this->x;
4857              }
4858              // max column width
4859              $wmax = $w - (2 * $this->cMargin);
4860              if ((!$firstline) AND (($chrwidth > $wmax) OR ($this->GetCharWidth($chars[0]) > $wmax))) {
4861                  // a single character do not fit on column
4862                  return '';
4863              }
4864              $i = 0; // character position
4865              $j = 0; // current starting position
4866              $sep = -1; // position of the last blank space
4867              $shy = false; // true if the last blank is a soft hypen (SHY)
4868              $l = 0; // current string length
4869              $nl = 0; //number of lines
4870              $linebreak = false;
4871              $pc = 0; // previous character
4872              // for each character
4873              while ($i < $nb) {
4874                  if (($maxh > 0) AND ($this->y >= $maxy) ) {
4875                      break;
4876                  }
4877                  //Get the current character
4878                  $c = $chars[$i];
4879                  if ($c == 10) { // 10 = "\n" = new line
4880                      //Explicit line break
4881                      if ($align == 'J') {
4882                          if ($this->rtl) {
4883                              $talign = 'R';
4884                          } else {
4885                              $talign = 'L';
4886                          }
4887                      } else {
4888                          $talign = $align;
4889                      }
4890                      $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4891                      if ($firstline) {
4892                          $startx = $this->x;
4893                          $tmparr = array_slice($chars, $j, ($i - $j));
4894                          if ($rtlmode) {
4895                              $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4896                          }
4897                          $linew = $this->GetArrStringWidth($tmparr);
4898                          unset($tmparr);
4899                          if ($this->rtl) {
4900                              $this->endlinex = $startx - $linew;
4901                          } else {
4902                              $this->endlinex = $startx + $linew;
4903                          }
4904                          $w = $linew;
4905                          $tmpcmargin = $this->cMargin;
4906                          if ($maxh == 0) {
4907                              $this->cMargin = 0;
4908                          }
4909                      }
4910                      if ($firstblock AND $this->isRTLTextDir()) {
4911                          $tmpstr = rtrim($tmpstr);
4912                      }
4913                      $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
4914                      unset($tmpstr);
4915                      if ($firstline) {
4916                          $this->cMargin = $tmpcmargin;
4917                          return ($this->UniArrSubString($uchars, $i));
4918                      }
4919                      ++$nl;
4920                      $j = $i + 1;
4921                      $l = 0;
4922                      $sep = -1;
4923                      $shy = false;
4924                      // account for margin changes
4925                      if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
4926                          // AcceptPageBreak() may be overriden on extended classed to include margin changes
4927                          $this->AcceptPageBreak();
4928                      }
4929                      $w = $this->getRemainingWidth();
4930                      $wmax = $w - (2 * $this->cMargin);
4931                  } else {
4932                      // 160 is the non-breaking space.
4933                      // 173 is SHY (Soft Hypen).
4934                      // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
4935                      // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
4936                      // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
4937                      if (($c != 160) AND (($c == 173) OR preg_match($this->re_spaces, $this->unichr($c)))) {
4938                          // update last blank space position
4939                          $sep = $i;
4940                          // check if is a SHY
4941                          if ($c == 173) {
4942                              $shy = true;
4943                              if ($pc == 45) {
4944                                  $tmp_shy_replacement_width = 0;
4945                                  $tmp_shy_replacement_char = '';
4946                              } else {
4947                                  $tmp_shy_replacement_width = $shy_replacement_width;
4948                                  $tmp_shy_replacement_char = $shy_replacement_char;
4949                              }
4950                          } else {
4951                              $shy = false;
4952                          }
4953                      }
4954                      // update string length
4955                      if ((($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) AND ($arabic)) {
4956                          // with bidirectional algorithm some chars may be changed affecting the line length
4957                          // *** very slow ***
4958                          $l = $this->GetArrStringWidth($this->utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl));
4959                      } else {
4960                          $l += $this->GetCharWidth($c);
4961                      }
4962                      if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) ) {
4963                          // we have reached the end of column
4964                          if ($sep == -1) {
4965                              // check if the line was already started
4966                              if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $chrwidth)))
4967                                  OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $chrwidth)))) {
4968                                  // print a void cell and go to next line
4969                                  $this->Cell($w, $h, '', 0, 1);
4970                                  $linebreak = true;
4971                                  if ($firstline) {
4972                                      return ($this->UniArrSubString($uchars, $j));
4973                                  }
4974                              } else {
4975                                  // truncate the word because do not fit on column
4976                                  $tmpstr = $this->UniArrSubString($uchars, $j, $i);
4977                                  if ($firstline) {
4978                                      $startx = $this->x;
4979                                      $tmparr = array_slice($chars, $j, ($i - $j));
4980                                      if ($rtlmode) {
4981                                          $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
4982                                      }
4983                                      $linew = $this->GetArrStringWidth($tmparr);
4984                                      unset($tmparr);
4985                                      if ($this->rtl) {
4986                                          $this->endlinex = $startx - $linew;
4987                                      } else {
4988                                          $this->endlinex = $startx + $linew;
4989                                      }
4990                                      $w = $linew;
4991                                      $tmpcmargin = $this->cMargin;
4992                                      if ($maxh == 0) {
4993                                          $this->cMargin = 0;
4994                                      }
4995                                  }
4996                                  if ($firstblock AND $this->isRTLTextDir()) {
4997                                      $tmpstr = rtrim($tmpstr);
4998                                  }
4999                                  $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
5000                                  unset($tmpstr);
5001                                  if ($firstline) {
5002                                      $this->cMargin = $tmpcmargin;
5003                                      return ($this->UniArrSubString($uchars, $i));
5004                                  }
5005                                  $j = $i;
5006                                  --$i;
5007                              }
5008                          } else {
5009                              // word wrapping
5010                              if ($this->rtl AND (!$firstblock)) {
5011                                  $endspace = 1;
5012                              } else {
5013                                  $endspace = 0;
5014                              }
5015                              if ($shy) {
5016                                  // add hypen (minus symbol) at the end of the line
5017                                  $shy_width = $tmp_shy_replacement_width;
5018                                  if ($this->rtl) {
5019                                      $shy_char_left = $tmp_shy_replacement_char;
5020                                      $shy_char_right = '';
5021                                  } else {
5022                                      $shy_char_left = '';
5023                                      $shy_char_right = $tmp_shy_replacement_char;
5024                                  }
5025                              } else {
5026                                  $shy_width = 0;
5027                                  $shy_char_left = '';
5028                                  $shy_char_right = '';
5029                              }
5030                              $tmpstr = $this->UniArrSubString($uchars, $j, ($sep + $endspace));
5031                              if ($firstline) {
5032                                  $startx = $this->x;
5033                                  $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
5034                                  if ($rtlmode) {
5035                                      $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
5036                                  }
5037                                  $linew = $this->GetArrStringWidth($tmparr);
5038                                  unset($tmparr);
5039                                  if ($this->rtl) {
5040                                      $this->endlinex = $startx - $linew - $shy_width;
5041                                  } else {
5042                                      $this->endlinex = $startx + $linew + $shy_width;
5043                                  }
5044                                  $w = $linew;
5045                                  $tmpcmargin = $this->cMargin;
5046                                  if ($maxh == 0) {
5047                                      $this->cMargin = 0;
5048                                  }
5049                              }
5050                              // print the line
5051                              if ($firstblock AND $this->isRTLTextDir()) {
5052                                  $tmpstr = rtrim($tmpstr);
5053                              }
5054                              $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
5055                              unset($tmpstr);
5056                              if ($firstline) {
5057                                  // return the remaining text
5058                                  $this->cMargin = $tmpcmargin;
5059                                  return ($this->UniArrSubString($uchars, ($sep + $endspace)));
5060                              }
5061                              $i = $sep;
5062                              $sep = -1;
5063                              $shy = false;
5064                              $j = ($i+1);
5065                          }
5066                          // account for margin changes
5067                          if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND (!$this->InFooter)) {
5068                              // AcceptPageBreak() may be overriden on extended classed to include margin changes
5069                              $this->AcceptPageBreak();
5070                          }
5071                          $w = $this->getRemainingWidth();
5072                          $wmax = $w - (2 * $this->cMargin);
5073                          if ($linebreak) {
5074                              $linebreak = false;
5075                          } else {
5076                              ++$nl;
5077                              $l = 0;
5078                          }
5079                      }
5080                  }
5081                  // save last character
5082                  $pc = $c;
5083                  ++$i;
5084              } // end while i < nb
5085              // print last substring (if any)
5086              if ($l > 0) {
5087                  switch ($align) {
5088                      case 'J':
5089                      case 'C': {
5090                          $w = $w;
5091                          break;
5092                      }
5093                      case 'L': {
5094                          if ($this->rtl) {
5095                              $w = $w;
5096                          } else {
5097                              $w = $l;
5098                          }
5099                          break;
5100                      }
5101                      case 'R': {
5102                          if ($this->rtl) {
5103                              $w = $l;
5104                          } else {
5105                              $w = $w;
5106                          }
5107                          break;
5108                      }
5109                      default: {
5110                          $w = $l;
5111                          break;
5112                      }
5113                  }
5114                  $tmpstr = $this->UniArrSubString($uchars, $j, $nb);
5115                  if ($firstline) {
5116                      $startx = $this->x;
5117                      $tmparr = array_slice($chars, $j, ($nb - $j));
5118                      if ($rtlmode) {
5119                          $tmparr = $this->utf8Bidi($tmparr, $tmpstr, $this->tmprtl);
5120                      }
5121                      $linew = $this->GetArrStringWidth($tmparr);
5122                      unset($tmparr);
5123                      if ($this->rtl) {
5124                          $this->endlinex = $startx - $linew;
5125                      } else {
5126                          $this->endlinex = $startx + $linew;
5127                      }
5128                      $w = $linew;
5129                      $tmpcmargin = $this->cMargin;
5130                      if ($maxh == 0) {
5131                          $this->cMargin = 0;
5132                      }
5133                  }
5134                  if ($firstblock AND $this->isRTLTextDir()) {
5135                      $tmpstr = rtrim($tmpstr);
5136                  }
5137                  $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
5138                  unset($tmpstr);
5139                  if ($firstline) {
5140                      $this->cMargin = $tmpcmargin;
5141                      return ($this->UniArrSubString($uchars, $nb));
5142                  }
5143                  ++$nl;
5144              }
5145              if ($firstline) {
5146                  return '';
5147              }
5148              return $nl;
5149          }
5150  
5151          /**
5152           * Returns the remaining width between the current position and margins.
5153           * @return int Return the remaining width
5154           * @access protected
5155           */
5156  		protected function getRemainingWidth() {
5157              if ($this->rtl) {
5158                  return ($this->x - $this->lMargin);
5159              } else {
5160                  return ($this->w - $this->rMargin - $this->x);
5161              }
5162          }
5163  
5164           /**
5165           * Extract a slice of the $strarr array and return it as string.
5166           * @param string $strarr The input array of characters.
5167           * @param int $start the starting element of $strarr.
5168           * @param int $end first element that will not be returned.
5169           * @return Return part of a string
5170           * @access public
5171           */
5172  		public function UTF8ArrSubString($strarr, $start='', $end='') {
5173              if (strlen($start) == 0) {
5174                  $start = 0;
5175              }
5176              if (strlen($end) == 0) {
5177                  $end = count($strarr);
5178              }
5179              $string = '';
5180              for ($i=$start; $i < $end; ++$i) {
5181                  $string .= $this->unichr($strarr[$i]);
5182              }
5183              return $string;
5184          }
5185  
5186           /**
5187           * Extract a slice of the $uniarr array and return it as string.
5188           * @param string $uniarr The input array of characters.
5189           * @param int $start the starting element of $strarr.
5190           * @param int $end first element that will not be returned.
5191           * @return Return part of a string
5192           * @access public
5193           * @since 4.5.037 (2009-04-07)
5194           */
5195  		public function UniArrSubString($uniarr, $start='', $end='') {
5196              if (strlen($start) == 0) {
5197                  $start = 0;
5198              }
5199              if (strlen($end) == 0) {
5200                  $end = count($uniarr);
5201              }
5202              $string = '';
5203              for ($i=$start; $i < $end; ++$i) {
5204                  $string .= $uniarr[$i];
5205              }
5206              return $string;
5207          }
5208  
5209           /**
5210           * Convert an array of UTF8 values to array of unicode characters
5211           * @param string $ta The input array of UTF8 values.
5212           * @return Return array of unicode characters
5213           * @access public
5214           * @since 4.5.037 (2009-04-07)
5215           */
5216  		public function UTF8ArrayToUniArray($ta) {
5217              return array_map(array($this, 'unichr'), $ta);
5218          }
5219  
5220          /**
5221           * Returns the unicode caracter specified by UTF-8 value
5222           * @param int $c UTF-8 value
5223           * @return Returns the specified character.
5224           * @author Miguel Perez, Nicola Asuni
5225           * @access public
5226           * @since 2.3.000 (2008-03-05)
5227           */
5228  		public function unichr($c) {
5229              if (!$this->isunicode) {
5230                  return chr($c);
5231              } elseif ($c <= 0x7F) {
5232                  // one byte
5233                  return chr($c);
5234              } elseif ($c <= 0x7FF) {
5235                  // two bytes
5236                  return chr(0xC0 | $c >> 6).chr(0x80 | $c & 0x3F);
5237              } elseif ($c <= 0xFFFF) {
5238                  // three bytes
5239                  return chr(0xE0 | $c >> 12).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
5240              } elseif ($c <= 0x10FFFF) {
5241                  // four bytes
5242                  return chr(0xF0 | $c >> 18).chr(0x80 | $c >> 12 & 0x3F).chr(0x80 | $c >> 6 & 0x3F).chr(0x80 | $c & 0x3F);
5243              } else {
5244                  return '';
5245              }
5246          }
5247  
5248          /**
5249           * Return the image type given the file name or array returned by getimagesize() function.
5250           * @param string $imgfile image file name
5251           * @param array $iminfo array of image information returned by getimagesize() function.
5252           * @return string image type
5253           * @since 4.8.017 (2009-11-27)
5254           */
5255  		public function getImageFileType($imgfile, $iminfo=array()) {
5256              if (isset($iminfo['mime']) AND !empty($iminfo['mime'])) {
5257                  $mime = explode('/', $iminfo['mime']);
5258                  if ((count($mime) > 1) AND ($mime[0] == 'image') AND (!empty($mime[1]))) {
5259                      return trim($mime[1]);
5260                  }
5261              }
5262              $type = '';
5263              $fileinfo = pathinfo($imgfile);
5264              if (isset($fileinfo['extension']) AND (!$this->empty_string($fileinfo['extension']))) {
5265                  $type = strtolower(trim($fileinfo['extension']));
5266              }
5267              if ($type == 'jpg') {
5268                  $type = 'jpeg';
5269              }
5270              return $type;
5271          }
5272  
5273          /**
5274           * Puts an image in the page.
5275           * The upper-left corner must be given.
5276           * The dimensions can be specified in different ways:<ul>
5277           * <li>explicit width and height (expressed in user unit)</li>
5278           * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
5279           * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
5280           * 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;
5281           * The format can be specified explicitly or inferred from the file extension.<br />
5282           * It is possible to put a link on the image.<br />
5283           * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
5284           * @param string $file Name of the file containing the image.
5285           * @param float $x Abscissa of the upper-left corner.
5286           * @param float $y Ordinate of the upper-left corner.
5287           * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
5288           * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
5289           * @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.
5290           * @param mixed $link URL or identifier returned by AddLink().
5291           * @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>
5292           * @param mixed $resize If true resize (reduce) the image to fit $w and $h (requires GD or ImageMagick library); if false do not resize; if 2 force resize in all cases (upscaling and downscaling).
5293           * @param int $dpi dot-per-inch resolution used on resize
5294           * @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>
5295           * @param boolean $ismask true if this image is a mask, false otherwise
5296           * @param mixed $imgmask image object returned by this function or false
5297           * @param mixed $border Indicates if borders must be drawn around the image. 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>
5298           * @param boolean $fitbox If true scale image dimensions proportionally to fit within the ($w, $h) box.
5299           * @param boolean $hidden if true do not display the image.
5300           * @param boolean $fitonpage if true the image is resized to not exceed page dimensions.
5301           * @return image information
5302           * @access public
5303           * @since 1.1
5304           */
5305  		public function Image($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='', $ismask=false, $imgmask=false, $border=0, $fitbox=false, $hidden=false, $fitonpage=false) {
5306              if ($x === '') {
5307                  $x = $this->x;
5308              }
5309              if ($y === '') {
5310                  $y = $this->y;
5311              }
5312              // get image dimensions
5313              $imsize = @getimagesize($file);
5314              if ($imsize === FALSE) {
5315                  // encode spaces on filename
5316                  $file = str_replace(' ', '%20', $file);
5317                  $imsize = @getimagesize($file);
5318                  if ($imsize === FALSE) {
5319                      if (($w > 0) AND ($h > 0)) {
5320                          $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
5321                          $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
5322                          $imsize = array($pw, $ph);
5323                      } else {
5324                          $this->Error('[Image] Unable to get image width and height: '.$file);
5325                      }
5326                  }
5327              }
5328              // get original image width and height in pixels
5329              list($pixw, $pixh) = $imsize;
5330              // calculate image width and height on document
5331              if (($w <= 0) AND ($h <= 0)) {
5332                  // convert image size to document unit
5333                  $w = $this->pixelsToUnits($pixw);
5334                  $h = $this->pixelsToUnits($pixh);
5335              } elseif ($w <= 0) {
5336                  $w = $h * $pixw / $pixh;
5337              } elseif ($h <= 0) {
5338                  $h = $w * $pixh / $pixw;
5339              } elseif ($fitbox AND ($w > 0) AND ($h > 0)) {
5340                  // scale image dimensions proportionally to fit within the ($w, $h) box
5341                  if ((($w * $pixh) / ($h * $pixw)) < 1) {
5342                      $h = $w * $pixh / $pixw;
5343                  } else {
5344                      $w = $h * $pixw / $pixh;
5345                  }
5346              }
5347              // Check whether we need a new page first as this does not fit
5348              $prev_x = $this->x;
5349              if ($this->checkPageBreak($h, $y)) {
5350                  $y = $this->y;
5351                  if ($this->rtl) {
5352                      $x += ($prev_x - $this->x);
5353                  } else {
5354                      $x += ($this->x - $prev_x);
5355                  }
5356              }
5357              // resize image to be contained on a single page
5358              if ($fitonpage) {
5359                  $ratio_wh = $w / $h;
5360                  if (($y + $h) > $this->PageBreakTrigger) {
5361                      $h = $this->PageBreakTrigger - $y;
5362                      $w = $h * $ratio_wh;
5363                  }
5364                  if (($x + $w) > ($this->w - $this->rMargin)) {
5365                      $w = $this->w - $this->rMargin - $x;
5366                      $h = $w / $ratio_wh;
5367                  }
5368              }
5369              // calculate new minimum dimensions in pixels
5370              $neww = round($w * $this->k * $dpi / $this->dpi);
5371              $newh = round($h * $this->k * $dpi / $this->dpi);
5372              // check if resize is necessary (resize is used only to reduce the image)
5373              $newsize = ($neww * $newh);
5374              $pixsize = ($pixw * $pixh);
5375              if (intval($resize) == 2) {
5376                  $resize = true;
5377              } elseif ($newsize >= $pixsize) {
5378                  $resize = false;
5379              }
5380              // check if image has been already added on document
5381              $newimage = true;
5382              if (in_array($file, $this->imagekeys)) {
5383                  $newimage = false;
5384                  // get existing image data
5385                  $info = $this->getImageBuffer($file);
5386                  // check if the newer image is larger
5387                  $oldsize = ($info['w'] * $info['h']);
5388                  if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
5389                      $newimage = true;
5390                  }
5391              }
5392              if ($newimage) {
5393                  //First use of image, get info
5394                  if ($type == '') {
5395                      $type = $this->getImageFileType($file, $imsize);
5396                  }
5397                  $mqr = $this->get_mqr();
5398                  $this->set_mqr(false);
5399                  // Specific image handlers
5400                  $mtd = '_parse'.$type;
5401                  // GD image handler function
5402                  $gdfunction = 'imagecreatefrom'.$type;
5403                  $info = false;
5404                  if ((method_exists($this, $mtd)) AND (!($resize AND function_exists($gdfunction)))) {
5405                      // TCPDF image functions
5406                      $info = $this->$mtd($file);
5407                      if ($info == 'pngalpha') {
5408                          return $this->ImagePngAlpha($file, $x, $y, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign);
5409                      }
5410                  }
5411                  if (!$info) {
5412                      if (function_exists($gdfunction)) {
5413                          // GD library
5414                          $img = $gdfunction($file);
5415                          if ($resize) {
5416                              $imgr = imagecreatetruecolor($neww, $newh);
5417                              if (($type == 'gif') OR ($type == 'png')) {
5418                                  $imgr = $this->_setGDImageTransparency($imgr, $img);
5419                              }
5420                              imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
5421                              if (($type == 'gif') OR ($type == 'png')) {
5422                                  $info = $this->_toPNG($imgr);
5423                              } else {
5424                                  $info = $this->_toJPEG($imgr);
5425                              }
5426                          } else {
5427                              if (($type == 'gif') OR ($type == 'png')) {
5428                                  $info = $this->_toPNG($img);
5429                              } else {
5430                                  $info = $this->_toJPEG($img);
5431                              }
5432                          }
5433                      } elseif (extension_loaded('imagick')) {
5434                          // ImageMagick library
5435                          $img = new Imagick();
5436                          if ($type == 'SVG') {
5437                              // get SBG file content
5438                              $svgimg = file_get_contents($file);
5439                              // get width and height
5440                              $regs = array();
5441                              if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
5442                                  $tmp = array();
5443                                  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
5444                                      $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false) * $dpi / 72;
5445                                      $svgimg = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$ow.$this->pdfunit.'"', $svgimg);
5446                                  }
5447                                  $tmp = array();
5448                                  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
5449                                      $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false) * $dpi / 72;
5450                                      $svgimg = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$oh.$this->pdfunit.'"', $svgimg);
5451                                  }
5452                                  $tmp = array();
5453                                  if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
5454                                      $vbw = $ow * (72 / $dpi) * $this->imgscale * $this->k;
5455                                      $vbh = $oh * (72 / $dpi) * $this->imgscale * $this->k;
5456                                      $svgimg = preg_replace('/<svg/si', '<svg viewBox="0 0 '.$vbw.' '.$vbh.'"', $svgimg);
5457                                  }
5458                              }
5459                              $img->readImageBlob($svgimg);
5460                          } else {
5461                              $img->readImage($file);
5462                          }
5463                          if ($resize) {
5464                              $img->resizeImage($neww, $newh, 10, 1, false);
5465                          }
5466                          $img->setCompressionQuality($this->jpeg_quality);
5467                          $img->setImageFormat('jpeg');
5468                          $tempname = tempnam(K_PATH_CACHE, 'jpg_');
5469                          $img->writeImage($tempname);
5470                          $info = $this->_parsejpeg($tempname);
5471                          unlink($tempname);
5472                          $img->destroy();
5473                      } else {
5474                          return;
5475                      }
5476                  }
5477                  if ($info === false) {
5478                      //If false, we cannot process image
5479                      return;
5480                  }
5481                  $this->set_mqr($mqr);
5482                  if ($ismask) {
5483                      // force grayscale
5484                      $info['cs'] = 'DeviceGray';
5485                  }
5486                  $info['i'] = $this->numimages;
5487                  if (!in_array($file, $this->imagekeys)) {
5488                      ++$info['i'];
5489                  }
5490                  if ($imgmask !== false) {
5491                      $info['masked'] = $imgmask;
5492                  }
5493                  // add image to document
5494                  $this->setImageBuffer($file, $info);
5495              }
5496              // set alignment
5497              $this->img_rb_y = $y + $h;
5498              // set alignment
5499              if ($this->rtl) {
5500                  if ($palign == 'L') {
5501                      $ximg = $this->lMargin;
5502                  } elseif ($palign == 'C') {
5503                      $ximg = ($this->w - $w) / 2;
5504                  } elseif ($palign == 'R') {
5505                      $ximg = $this->w - $this->rMargin - $w;
5506                  } else {
5507                      $ximg = $this->w - $x - $w;
5508                  }
5509                  $this->img_rb_x = $ximg;
5510              } else {
5511                  if ($palign == 'L') {
5512                      $ximg = $this->lMargin;
5513                  } elseif ($palign == 'C') {
5514                      $ximg = ($this->w - $w) / 2;
5515                  } elseif ($palign == 'R') {
5516                      $ximg = $this->w - $this->rMargin - $w;
5517                  } else {
5518                      $ximg = $x;
5519                  }
5520                  $this->img_rb_x = $ximg + $w;
5521              }
5522              if ($ismask OR $hidden) {
5523                  // image is not displayed
5524                  return $info['i'];
5525              }
5526              $xkimg = $ximg * $this->k;
5527              $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']));
5528              if (!empty($border)) {
5529                  $bx = $x;
5530                  $by = $y;
5531                  $this->x = $ximg;
5532                  if ($this->rtl) {
5533                      $this->x += $w;
5534                  }
5535                  $this->y = $y;
5536                  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
5537                  $this->x = $bx;
5538                  $this->y = $by;
5539              }
5540              if ($link) {
5541                  $this->Link($ximg, $y, $w, $h, $link, 0);
5542              }
5543              // set pointer to align the successive text/objects
5544              switch($align) {
5545                  case 'T': {
5546                      $this->y = $y;
5547                      $this->x = $this->img_rb_x;
5548                      break;
5549                  }
5550                  case 'M': {
5551                      $this->y = $y + round($h/2);
5552                      $this->x = $this->img_rb_x;
5553                      break;
5554                  }
5555                  case 'B': {
5556                      $this->y = $this->img_rb_y;
5557                      $this->x = $this->img_rb_x;
5558                      break;
5559                  }
5560                  case 'N': {
5561                      $this->SetY($this->img_rb_y);
5562                      break;
5563                  }
5564                  default:{
5565                      break;
5566                  }
5567              }
5568              $this->endlinex = $this->img_rb_x;
5569              return $info['i'];
5570          }
5571  
5572          /**
5573           * Sets the current active configuration setting of magic_quotes_runtime (if the set_magic_quotes_runtime function exist)
5574           * @param boolean $mqr FALSE for off, TRUE for on.
5575           * @since 4.6.025 (2009-08-17)
5576           */
5577  		public function set_mqr($mqr) {
5578              if(!defined('PHP_VERSION_ID')) {
5579                  $version = PHP_VERSION;
5580                  define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
5581              }
5582              if (PHP_VERSION_ID < 50300) {
5583                  @set_magic_quotes_runtime($mqr);
5584              }
5585          }
5586  
5587          /**
5588           * Gets the current active configuration setting of magic_quotes_runtime (if the get_magic_quotes_runtime function exist)
5589           * @return Returns 0 if magic quotes runtime is off or get_magic_quotes_runtime doesn't exist, 1 otherwise.
5590           * @since 4.6.025 (2009-08-17)
5591           */
5592  		public function get_mqr() {
5593              if(!defined('PHP_VERSION_ID')) {
5594                  $version = PHP_VERSION;
5595                  define('PHP_VERSION_ID', (($version{0} * 10000) + ($version{2} * 100) + $version{4}));
5596              }
5597              if (PHP_VERSION_ID < 50300) {
5598                  return @get_magic_quotes_runtime();
5599              }
5600              return 0;
5601          }
5602  
5603          /**
5604           * Convert the loaded image to a JPEG and then return a structure for the PDF creator.
5605           * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
5606           * @param string $file Image file name.
5607           * @param image $image Image object.
5608           * return image JPEG image object.
5609           * @access protected
5610           */
5611  		protected function _toJPEG($image) {
5612              $tempname = tempnam(K_PATH_CACHE, 'jpg_');
5613              imagejpeg($image, $tempname, $this->jpeg_quality);
5614              imagedestroy($image);
5615              $retvars = $this->_parsejpeg($tempname);
5616              // tidy up by removing temporary image
5617              unlink($tempname);
5618              return $retvars;
5619          }
5620  
5621          /**
5622           * Convert the loaded image to a PNG and then return a structure for the PDF creator.
5623           * This function requires GD library and write access to the directory defined on K_PATH_CACHE constant.
5624           * @param string $file Image file name.
5625           * @param image $image Image object.
5626           * return image PNG image object.
5627           * @access protected
5628           * @since 4.9.016 (2010-04-20)
5629           */
5630  		protected function _toPNG($image) {
5631              $tempname = tempnam(K_PATH_CACHE, 'jpg_');
5632              imagepng($image, $tempname);
5633              imagedestroy($image);
5634              $retvars = $this->_parsepng($tempname);
5635              // tidy up by removing temporary image
5636              unlink($tempname);
5637              return $retvars;
5638          }
5639  
5640          /**
5641           * Set the transparency for the given GD image.
5642           * @param image $new_image GD image object
5643           * @param image $image GD image object.
5644           * return GD image object.
5645           * @access protected
5646           * @since 4.9.016 (2010-04-20)
5647           */
5648  		protected function _setGDImageTransparency($new_image, $image) {
5649              // transparency index
5650              $tid = imagecolortransparent($image);
5651              // default transparency color
5652              $tcol = array('red' => 255, 'green' => 255, 'blue' => 255);
5653              if ($tid >= 0) {
5654                  // get the colors for the transparency index
5655                  $tcol = imagecolorsforindex($image, $tid);
5656              }
5657              $tid = imagecolorallocate($new_image, $tcol['red'], $tcol['green'], $tcol['blue']);
5658              imagefill($new_image, 0, 0, $tid);
5659              imagecolortransparent($new_image, $tid);
5660              return $new_image;
5661          }
5662  
5663          /**
5664           * Extract info from a JPEG file without using the GD library.
5665           * @param string $file image file to parse
5666           * @return array structure containing the image data
5667           * @access protected
5668           */
5669  		protected function _parsejpeg($file) {
5670              $a = getimagesize($file);
5671              if (empty($a)) {
5672                  $this->Error('Missing or incorrect image file: '.$file);
5673              }
5674              if ($a[2] != 2) {
5675                  $this->Error('Not a JPEG file: '.$file);
5676              }
5677              if ((!isset($a['channels'])) OR ($a['channels'] == 3)) {
5678                  $colspace = 'DeviceRGB';
5679              } elseif ($a['channels'] == 4) {
5680                  $colspace = 'DeviceCMYK';
5681              } else {
5682                  $colspace = 'DeviceGray';
5683              }
5684              $bpc = isset($a['bits']) ? $a['bits'] : 8;
5685              $data = file_get_contents($file);
5686              return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
5687          }
5688  
5689          /**
5690           * Extract info from a PNG file without using the GD library.
5691           * @param string $file image file to parse
5692           * @return array structure containing the image data
5693           * @access protected
5694           */
5695  		protected function _parsepng($file) {
5696              $f = fopen($file, 'rb');
5697              if ($f === false) {
5698                  $this->Error('Can\'t open image file: '.$file);
5699              }
5700              //Check signature
5701              if (fread($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) {
5702                  $this->Error('Not a PNG file: '.$file);
5703              }
5704              //Read header chunk
5705              fread($f, 4);
5706              if (fread($f, 4) != 'IHDR') {
5707                  $this->Error('Incorrect PNG file: '.$file);
5708              }
5709              $w = $this->_freadint($f);
5710              $h = $this->_freadint($f);
5711              $bpc = ord(fread($f, 1));
5712              if ($bpc > 8) {
5713                  //$this->Error('16-bit depth not supported: '.$file);
5714                  fclose($f);
5715                  return false;
5716              }
5717              $ct = ord(fread($f, 1));
5718              if ($ct == 0) {
5719                  $colspace = 'DeviceGray';
5720              } elseif ($ct == 2) {
5721                  $colspace = 'DeviceRGB';
5722              } elseif ($ct == 3) {
5723                  $colspace = 'Indexed';
5724              } else {
5725                  // alpha channel
5726                  fclose($f);
5727                  return 'pngalpha';
5728              }
5729              if (ord(fread($f, 1)) != 0) {
5730                  //$this->Error('Unknown compression method: '.$file);
5731                  fclose($f);
5732                  return false;
5733              }
5734              if (ord(fread($f, 1)) != 0) {
5735                  //$this->Error('Unknown filter method: '.$file);
5736                  fclose($f);
5737                  return false;
5738              }
5739              if (ord(fread($f, 1)) != 0) {
5740                  //$this->Error('Interlacing not supported: '.$file);
5741                  fclose($f);
5742                  return false;
5743              }
5744              fread($f, 4);
5745              $parms = '/DecodeParms <</Predictor 15 /Colors '.($ct==2 ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w.'>>';
5746              //Scan chunks looking for palette, transparency and image data
5747              $pal = '';
5748              $trns = '';
5749              $data = '';
5750              do {
5751                  $n = $this->_freadint($f);
5752                  $type = fread($f, 4);
5753                  if ($type == 'PLTE') {
5754                      //Read palette
5755                      $pal = $this->rfread($f, $n);
5756                      fread($f, 4);
5757                  } elseif ($type == 'tRNS') {
5758                      //Read transparency info
5759                      $t = $this->rfread($f, $n);
5760                      if ($ct == 0) {
5761                          $trns = array(ord(substr($t, 1, 1)));
5762                      } elseif ($ct == 2) {
5763                          $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
5764                      } else {
5765                          $pos = strpos($t, chr(0));
5766                          if ($pos !== false) {
5767                              $trns = array($pos);
5768                          }
5769                      }
5770                      fread($f, 4);
5771                  } elseif ($type == 'IDAT') {
5772                      //Read image data block
5773                      $data .= $this->rfread($f, $n);
5774                      fread($f, 4);
5775                  } elseif ($type == 'IEND') {
5776                      break;
5777                  } else {
5778                      $this->rfread($f, $n + 4);
5779                  }
5780              } while ($n);
5781              if (($colspace == 'Indexed') AND (empty($pal))) {
5782                  //$this->Error('Missing palette in '.$file);
5783                  fclose($f);
5784                  return false;
5785              }
5786              fclose($f);
5787              return array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
5788          }
5789  
5790          /**
5791           * Binary-safe and URL-safe file read.
5792           * Reads up to length  bytes from the file pointer referenced by handle. Reading stops as soon as one of the following conditions is met: length bytes have been read; EOF (end of file) is reached.
5793           * @param resource $handle
5794           * @param int $length
5795           * @return Returns the read string or FALSE in case of error.
5796           * @author Nicola Asuni
5797           * @access protected
5798           * @since 4.5.027 (2009-03-16)
5799           */
5800  		protected function rfread($handle, $length) {
5801              $data = fread($handle, $length);
5802              if ($data === false) {
5803                  return false;
5804              }
5805              $rest = $length - strlen($data);
5806              if ($rest > 0) {
5807                  $data .= $this->rfread($handle, $rest);
5808              }
5809              return $data;
5810          }
5811  
5812          /**
5813           * Extract info from a PNG image with alpha channel using the GD library.
5814           * @param string $file Name of the file containing the image.
5815           * @param float $x Abscissa of the upper-left corner.
5816           * @param float $y Ordinate of the upper-left corner.
5817           * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
5818           * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
5819           * @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.
5820           * @param mixed $link URL or identifier returned by AddLink().
5821           * @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>
5822           * @param boolean $resize If true resize (reduce) the image to fit $w and $h (requires GD library).
5823           * @param int $dpi dot-per-inch resolution used on resize
5824           * @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>
5825           * @author Valentin Schmidt, Nicola Asuni
5826           * @access protected
5827           * @since 4.3.007 (2008-12-04)
5828           * @see Image()
5829           */
5830  		protected function ImagePngAlpha($file, $x='', $y='', $w=0, $h=0, $type='', $link='', $align='', $resize=false, $dpi=300, $palign='') {
5831              // get image size
5832              list($wpx, $hpx) = getimagesize($file);
5833              // generate images
5834              $img = imagecreatefrompng($file);
5835              $imgalpha = imagecreate($wpx, $hpx);
5836              // generate gray scale pallete
5837              for ($c = 0; $c < 256; ++$c) {
5838                  ImageColorAllocate($imgalpha, $c, $c, $c);
5839              }
5840              // extract alpha channel
5841              for ($xpx = 0; $xpx < $wpx; ++$xpx) {
5842                  for ($ypx = 0; $ypx < $hpx; ++$ypx) {
5843                      $colorindex = imagecolorat($img, $xpx, $ypx);
5844                      $col = imagecolorsforindex($img, $colorindex);
5845                      imagesetpixel($imgalpha, $xpx, $ypx, $this->getGDgamma((127 - $col['alpha']) * 255 / 127));
5846                  }
5847              }
5848              // create temp alpha file
5849              $tempfile_alpha = tempnam(K_PATH_CACHE, 'mska_');
5850              imagepng($imgalpha, $tempfile_alpha);
5851              imagedestroy($imgalpha);
5852              // extract image without alpha channel
5853              $imgplain = imagecreatetruecolor($wpx, $hpx);
5854              imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
5855              // create temp image file
5856              $tempfile_plain = tempnam(K_PATH_CACHE, 'mskp_');
5857              imagepng($imgplain, $tempfile_plain);
5858              imagedestroy($imgplain);
5859              // embed mask image
5860              $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
5861              // embed image, masked with previously embedded mask
5862              $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
5863              // remove temp files
5864              unlink($tempfile_alpha);
5865              unlink($tempfile_plain);
5866          }
5867  
5868          /**
5869           * Correct the gamma value to be used with GD library
5870           * @param float $v the gamma value to be corrected
5871           * @access protected
5872           * @since 4.3.007 (2008-12-04)
5873           */
5874  		protected function getGDgamma($v) {
5875              return (pow(($v / 255), 2.2) * 255);
5876          }
5877  
5878          /**
5879           * Performs a line break.
5880           * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
5881           * @param float $h The height of the break. By default, the value equals the height of the last printed cell.
5882           * @param boolean $cell if true add a cMargin to the x coordinate
5883           * @access public
5884           * @since 1.0
5885           * @see Cell()
5886           */
5887          public function Ln($h='', $cell=false) {
5888              if (($this->num_columns > 0) AND ($this->y == $this->columns[$this->current_column]['y']) AND isset($this->columns[$this->current_column]['x']) AND ($this->x == $this->columns[$this->current_column]['x'])) {
5889                  // revove vertical space from the top of the column
5890                  return;
5891              }
5892              if ($cell) {
5893                  $cellmargin = $this->cMargin;
5894              } else {
5895                  $cellmargin = 0;
5896              }
5897              if ($this->rtl) {
5898                  $this->x = $this->w - $this->rMargin - $cellmargin;
5899              } else {
5900                  $this->x = $this->lMargin + $cellmargin;
5901              }
5902              if (is_string($h)) {
5903                  $this->y += $this->lasth;
5904              } else {
5905                  $this->y += $h;
5906              }
5907              $this->newline = true;
5908          }
5909  
5910          /**
5911           * Returns the relative X value of current position.
5912           * The value is relative to the left border for LTR languages and to the right border for RTL languages.
5913           * @return float
5914           * @access public
5915           * @since 1.2
5916           * @see SetX(), GetY(), SetY()
5917           */
5918  		public function GetX() {
5919              //Get x position
5920              if ($this->rtl) {
5921                  return ($this->w - $this->x);
5922              } else {
5923                  return $this->x;
5924              }
5925          }
5926  
5927          /**
5928           * Returns the absolute X value of current position.
5929           * @return float
5930           * @access public
5931           * @since 1.2
5932           * @see SetX(), GetY(), SetY()
5933           */
5934  		public function GetAbsX() {
5935              return $this->x;
5936          }
5937  
5938          /**
5939           * Returns the ordinate of the current position.
5940           * @return float
5941           * @access public
5942           * @since 1.0
5943           * @see SetY(), GetX(), SetX()
5944           */
5945  		public function GetY() {
5946              return $this->y;
5947          }
5948  
5949          /**
5950           * Defines the abscissa of the current position.
5951           * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
5952           * @param float $x The value of the abscissa.
5953           * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
5954           * @access public
5955           * @since 1.2
5956           * @see GetX(), GetY(), SetY(), SetXY()
5957           */
5958  		public function SetX($x, $rtloff=false) {
5959              if (!$rtloff AND $this->rtl) {
5960                  if ($x >= 0) {
5961                      $this->x = $this->w - $x;
5962                  } else {
5963                      $this->x = abs($x);
5964                  }
5965              } else {
5966                  if ($x >= 0) {
5967                      $this->x = $x;
5968                  } else {
5969                      $this->x = $this->w + $x;
5970                  }
5971              }
5972              if ($this->x < 0) {
5973                  $this->x = 0;
5974              }
5975              if ($this->x > $this->w) {
5976                  $this->x = $this->w;
5977              }
5978          }
5979  
5980          /**
5981           * Moves the current abscissa back to the left margin and sets the ordinate.
5982           * If the passed value is negative, it is relative to the bottom of the page.
5983           * @param float $y The value of the ordinate.
5984           * @param bool $resetx if true (default) reset the X position.
5985           * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
5986           * @access public
5987           * @since 1.0
5988           * @see GetX(), GetY(), SetY(), SetXY()
5989           */
5990  		public function SetY($y, $resetx=true, $rtloff=false) {
5991              if ($resetx) {
5992                  //reset x
5993                  if (!$rtloff AND $this->rtl) {
5994                      $this->x = $this->w - $this->rMargin;
5995                  } else {
5996                      $this->x = $this->lMargin;
5997                  }
5998              }
5999              if ($y >= 0) {
6000                  $this->y = $y;
6001              } else {
6002                  $this->y = $this->h + $y;
6003              }
6004              if ($this->y < 0) {
6005                  $this->y = 0;
6006              }
6007              if ($this->y > $this->h) {
6008                  $this->y = $this->h;
6009              }
6010          }
6011  
6012          /**
6013           * Defines the abscissa and ordinate of the current position.
6014           * If the passed values are negative, they are relative respectively to the right and bottom of the page.
6015           * @param float $x The value of the abscissa.
6016           * @param float $y The value of the ordinate.
6017           * @param boolean $rtloff if true always uses the page top-left corner as origin of axis.
6018           * @access public
6019           * @since 1.2
6020           * @see SetX(), SetY()
6021           */
6022  		public function SetXY($x, $y, $rtloff=false) {
6023              $this->SetY($y, false, $rtloff);
6024              $this->SetX($x, $rtloff);
6025          }
6026  
6027          /**
6028           * Send the document to a given destination: string, local file or browser.
6029           * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
6030           * The method first calls Close() if necessary to terminate the document.
6031           * @param string $name The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
6032           * @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 server file with the name given by name.</li><li>S: return the document as a string. name is ignored.</li><li>FI: equivalent to F + I option</li><li>FD: equivalent to F + D option</li></ul>
6033           * @access public
6034           * @since 1.0
6035           * @see Close()
6036           */
6037  		public function Output($name='doc.pdf', $dest='I') {
6038              //Output PDF to some destination
6039              //Finish document if necessary
6040              $this->lastpage();
6041              if ($this->state < 3) {
6042                  $this->Close();
6043              }
6044              //Normalize parameters
6045              if (is_bool($dest)) {
6046                  $dest = $dest ? 'D' : 'F';
6047              }
6048              $dest = strtoupper($dest);
6049              if ($dest != 'F') {
6050                  $name = preg_replace('/[\s]+/', '_', $name);
6051                  $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
6052              }
6053              if ($this->sign) {
6054                  // *** apply digital signature to the document ***
6055                  // get the document content
6056                  $pdfdoc = $this->getBuffer();
6057                  // remove last newline
6058                  $pdfdoc = substr($pdfdoc, 0, -1);
6059                  // Remove the original buffer
6060                  if (isset($this->diskcache) AND $this->diskcache) {
6061                      // remove buffer file from cache
6062                      unlink($this->buffer);
6063                  }
6064                  unset($this->buffer);
6065                  // remove filler space
6066                  $byterange_string_len = strlen($this->byterange_string);
6067                  // define the ByteRange
6068                  $byte_range = array();
6069                  $byte_range[0] = 0;
6070                  $byte_range[1] = strpos($pdfdoc, $this->byterange_string) + $byterange_string_len + 10;
6071                  $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
6072                  $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
6073                  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
6074                  // replace the ByteRange
6075                  $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
6076                  $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
6077                  $pdfdoc = str_replace($this->byterange_string, $byterange, $pdfdoc);
6078                  // write the document to a temporary folder
6079                  $tempdoc = tempnam(K_PATH_CACHE, 'tmppdf_');
6080                  $f = fopen($tempdoc, 'wb');
6081                  if (!$f) {
6082                      $this->Error('Unable to create temporary file: '.$tempdoc);
6083                  }
6084                  $pdfdoc_length = strlen($pdfdoc);
6085                  fwrite($f, $pdfdoc, $pdfdoc_length);
6086                  fclose($f);
6087                  // get digital signature via openssl library
6088                  $tempsign = tempnam(K_PATH_CACHE, 'tmpsig_');
6089                  if (empty($this->signature_data['extracerts'])) {
6090                      openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
6091                  } else {
6092                      openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED, $this->signature_data['extracerts']);
6093                  }
6094                  unlink($tempdoc);
6095                  // read signature
6096                  $signature = file_get_contents($tempsign, false, null, $pdfdoc_length);
6097                  unlink($tempsign);
6098                  // extract signature
6099                  $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
6100                  $tmparr = explode("\n\n", $signature);
6101                  $signature = $tmparr[1];
6102                  unset($tmparr);
6103                  // decode signature
6104                  $signature = base64_decode(trim($signature));
6105                  // convert signature to hex
6106                  $signature = current(unpack('H*', $signature));
6107                  $signature = str_pad($signature, $this->signature_max_length, '0');
6108                  // Add signature to the document
6109                  $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, ($byte_range[1]));
6110                  $this->diskcache = false;
6111                  $this->buffer = &$pdfdoc;
6112                  $this->bufferlen = strlen($pdfdoc);
6113              }
6114              switch($dest) {
6115                  case 'I': {
6116                      // Send PDF to the standard output
6117                      if (ob_get_contents()) {
6118                          $this->Error('Some data has already been output, can\'t send PDF file');
6119                      }
6120                      if (php_sapi_name() != 'cli') {
6121                          //We send to a browser
6122                          header('Content-Type: application/pdf');
6123                          if (headers_sent()) {
6124                              $this->Error('Some data has already been output to browser, can\'t send PDF file');
6125                          }
6126                          header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
6127                          header('Pragma: public');
6128                          header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
6129                          header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
6130                          header('Content-Length: '.$this->bufferlen);
6131                          header('Content-Disposition: inline; filename="'.basename($name).'";');
6132                      }
6133                      echo $this->getBuffer();
6134                      break;
6135                  }
6136                  case 'D': {
6137                      // Download PDF as file
6138                      if (ob_get_contents()) {
6139                          $this->Error('Some data has already been output, can\'t send PDF file');
6140                      }
6141                      header('Content-Description: File Transfer');
6142                      if (headers_sent()) {
6143                          $this->Error('Some data has already been output to browser, can\'t send PDF file');
6144                      }
6145                      header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
6146                      header('Pragma: public');
6147                      header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
6148                      header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
6149                      // force download dialog
6150                      header('Content-Type: application/force-download');
6151                      header('Content-Type: application/octet-stream', false);
6152                      header('Content-Type: application/download', false);
6153                      header('Content-Type: application/pdf', false);
6154                      // use the Content-Disposition header to supply a recommended filename
6155                      header('Content-Disposition: attachment; filename="'.basename($name).'";');
6156                      header('Content-Transfer-Encoding: binary');
6157                      header('Content-Length: '.$this->bufferlen);
6158                      echo $this->getBuffer();
6159                      break;
6160                  }
6161                  case 'F':
6162                  case 'FI':
6163                  case 'FD': {
6164                      // Save PDF to a local file
6165                      if ($this->diskcache) {
6166                          copy($this->buffer, $name);
6167                      } else {
6168                          $f = fopen($name, 'wb');
6169                          if (!$f) {
6170                              $this->Error('Unable to create output file: '.$name);
6171                          }
6172                          fwrite($f, $this->getBuffer(), $this->bufferlen);
6173                          fclose($f);
6174                      }
6175                      if ($dest == 'FI') {
6176                          // send headers to browser
6177                          header('Content-Type: application/pdf');
6178                          header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
6179                          header('Pragma: public');
6180                          header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
6181                          header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
6182                          header('Content-Length: '.filesize($name));
6183                          header('Content-Disposition: inline; filename="'.basename($name).'";');
6184                          // send document to the browser
6185                          echo file_get_contents($name);
6186                      } elseif ($dest == 'FD') {
6187                          // send headers to browser
6188                          if (ob_get_contents()) {
6189                              $this->Error('Some data has already been output, can\'t send PDF file');
6190                          }
6191                          header('Content-Description: File Transfer');
6192                          if (headers_sent()) {
6193                              $this->Error('Some data has already been output to browser, can\'t send PDF file');
6194                          }
6195                          header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
6196                          header('Pragma: public');
6197                          header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
6198                          header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
6199                          // force download dialog
6200                          header('Content-Type: application/force-download');
6201                          header('Content-Type: application/octet-stream', false);
6202                          header('Content-Type: application/download', false);
6203                          header('Content-Type: application/pdf', false);
6204                          // use the Content-Disposition header to supply a recommended filename
6205                          header('Content-Disposition: attachment; filename="'.basename($name).'";');
6206                          header('Content-Transfer-Encoding: binary');
6207                          header('Content-Length: '.filesize($name));
6208                          // send document to the browser
6209                          echo file_get_contents($name);
6210                      }
6211                      break;
6212                  }
6213                  case 'S': {
6214                      // Returns PDF as a string
6215                      return $this->getBuffer();
6216                  }
6217                  default: {
6218                      $this->Error('Incorrect output destination: '.$dest);
6219                  }
6220              }
6221              return '';
6222          }
6223  
6224          /**
6225           * Unset all class variables except the following critical variables: internal_encoding, state, bufferlen, buffer and diskcache.
6226           * @param boolean $destroyall if true destroys all class variables, otherwise preserves critical variables.
6227           * @param boolean $preserve_objcopy if true preserves the objcopy variable
6228           * @access public
6229           * @since 4.5.016 (2009-02-24)
6230           */
6231  		public function _destroy($destroyall=false, $preserve_objcopy=false) {
6232              if ($destroyall AND isset($this->diskcache) AND $this->diskcache AND (!$preserve_objcopy) AND (!$this->empty_string($this->buffer))) {
6233                  // remove buffer file from cache
6234                  unlink($this->buffer);
6235              }
6236              foreach (array_keys(get_object_vars($this)) as $val) {
6237                  if ($destroyall OR (
6238                      ($val != 'internal_encoding')
6239                      AND ($val != 'state')
6240                      AND ($val != 'bufferlen')
6241                      AND ($val != 'buffer')
6242                      AND ($val != 'diskcache')
6243                      AND ($val != 'sign')
6244                      AND ($val != 'signature_data')
6245                      AND ($val != 'signature_max_length')
6246                      AND ($val != 'byterange_string')
6247                      )) {
6248                      if ((!$preserve_objcopy OR ($val != 'objcopy')) AND isset($this->$val)) {
6249                          unset($this->$val);
6250                      }
6251                  }
6252              }
6253          }
6254  
6255          /**
6256           * Check for locale-related bug
6257           * @access protected
6258           */
6259  		protected function _dochecks() {
6260              //Check for locale-related bug
6261              if (1.1 == 1) {
6262                  $this->Error('Don\'t alter the locale before including class file');
6263              }
6264              //Check for decimal separator
6265              if (sprintf('%.1F', 1.0) != '1.0') {
6266                  setlocale(LC_NUMERIC, 'C');
6267              }
6268          }
6269  
6270          /**
6271           * Return fonts path
6272           * @return string
6273           * @access protected
6274           */
6275  		protected function _getfontpath() {
6276              if (!defined('K_PATH_FONTS') AND is_dir(dirname(__FILE__).'/fonts')) {
6277                  define('K_PATH_FONTS', dirname(__FILE__).'/fonts/');
6278              }
6279              return defined('K_PATH_FONTS') ? K_PATH_FONTS : '';
6280          }
6281  
6282          /**
6283           * Output pages.
6284           * @access protected
6285           */
6286  		protected function _putpages() {
6287              $nb = $this->numpages;
6288              if (!empty($this->AliasNbPages)) {
6289                  $nbs = $this->formatPageNumber($nb);
6290                  $nbu = $this->UTF8ToUTF16BE($nbs, false); // replacement for unicode font
6291                  $alias_a = $this->_escape($this->AliasNbPages);
6292                  $alias_au = $this->_escape('{'.$this->AliasNbPages.'}');
6293                  if ($this->isunicode) {
6294                      $alias_b = $this->_escape($this->UTF8ToLatin1($this->AliasNbPages));
6295                      $alias_bu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNbPages.'}'));
6296                      $alias_c = $this->_escape($this->utf8StrRev($this->AliasNbPages, false, $this->tmprtl));
6297                      $alias_cu = $this->_escape($this->utf8StrRev('{'.$this->AliasNbPages.'}', false, $this->tmprtl));
6298                  }
6299              }
6300              if (!empty($this->AliasNumPage)) {
6301                  $alias_pa = $this->_escape($this->AliasNumPage);
6302                  $alias_pau = $this->_escape('{'.$this->AliasNumPage.'}');
6303                  if ($this->isunicode) {
6304                      $alias_pb = $this->_escape($this->UTF8ToLatin1($this->AliasNumPage));
6305                      $alias_pbu = $this->_escape($this->UTF8ToLatin1('{'.$this->AliasNumPage.'}'));
6306                      $alias_pc = $this->_escape($this->utf8StrRev($this->AliasNumPage, false, $this->tmprtl));
6307                      $alias_pcu = $this->_escape($this->utf8StrRev('{'.$this->AliasNumPage.'}', false, $this->tmprtl));
6308                  }
6309              }
6310              $pagegroupnum = 0;
6311              $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
6312              for ($n=1; $n <= $nb; ++$n) {
6313                  $temppage = $this->getPageBuffer($n);
6314                  if (!empty($this->pagegroups)) {
6315                      if(isset($this->newpagegroup[$n])) {
6316                          $pagegroupnum = 0;
6317                      }
6318                      ++$pagegroupnum;
6319                      foreach ($this->pagegroups as $k => $v) {
6320                          // replace total pages group numbers
6321                          $vs = $this->formatPageNumber($v);
6322                          $vu = $this->UTF8ToUTF16BE($vs, false);
6323                          $alias_ga = $this->_escape($k);
6324                          $alias_gau = $this->_escape('{'.$k.'}');
6325                          if ($this->isunicode) {
6326                              $alias_gb = $this->_escape($this->UTF8ToLatin1($k));
6327                              $alias_gbu = $this->_escape($this->UTF8ToLatin1('{'.$k.'}'));
6328                              $alias_gc = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
6329                              $alias_gcu = $this->_escape($this->utf8StrRev('{'.$k.'}', false, $this->tmprtl));
6330                          }
6331                          $temppage = str_replace($alias_gau, $vu, $temppage);
6332                          if ($this->isunicode) {
6333                              $temppage = str_replace($alias_gbu, $vu, $temppage);
6334                              $temppage = str_replace($alias_gcu, $vu, $temppage);
6335                              $temppage = str_replace($alias_gb, $vs, $temppage);
6336                              $temppage = str_replace($alias_gc, $vs, $temppage);
6337                          }
6338                          $temppage = str_replace($alias_ga, $vs, $temppage);
6339                          // replace page group numbers
6340                          $pvs = $this->formatPageNumber($pagegroupnum);
6341                          $pvu = $this->UTF8ToUTF16BE($pvs, false);
6342                          $pk = str_replace('{nb', '{pnb', $k);
6343                          $alias_pga = $this->_escape($pk);
6344                          $alias_pgau = $this->_escape('{'.$pk.'}');
6345                          if ($this->isunicode) {
6346                              $alias_pgb = $this->_escape($this->UTF8ToLatin1($pk));
6347                              $alias_pgbu = $this->_escape($this->UTF8ToLatin1('{'.$pk.'}'));
6348                              $alias_pgc = $this->_escape($this->utf8StrRev($pk, false, $this->tmprtl));
6349                              $alias_pgcu = $this->_escape($this->utf8StrRev('{'.$pk.'}', false, $this->tmprtl));
6350                          }
6351                          $temppage = str_replace($alias_pgau, $pvu, $temppage);
6352                          if ($this->isunicode) {
6353                              $temppage = str_replace($alias_pgbu, $pvu, $temppage);
6354                              $temppage = str_replace($alias_pgcu, $pvu, $temppage);
6355                              $temppage = str_replace($alias_pgb, $pvs, $temppage);
6356                              $temppage = str_replace($alias_pgc, $pvs, $temppage);
6357                          }
6358                          $temppage = str_replace($alias_pga, $pvs, $temppage);
6359                      }
6360                  }
6361                  if (!empty($this->AliasNbPages)) {
6362                      // replace total pages number
6363                      $temppage = str_replace($alias_au, $nbu, $temppage);
6364                      if ($this->isunicode) {
6365                          $temppage = str_replace($alias_bu, $nbu, $temppage);
6366                          $temppage = str_replace($alias_cu, $nbu, $temppage);
6367                          $temppage = str_replace($alias_b, $nbs, $temppage);
6368                          $temppage = str_replace($alias_c, $nbs, $temppage);
6369                      }
6370                      $temppage = str_replace($alias_a, $nbs, $temppage);
6371                  }
6372                  if (!empty($this->AliasNumPage)) {
6373                      // replace page number
6374                      $pnbs = $this->formatPageNumber($n);
6375                      $pnbu = $this->UTF8ToUTF16BE($pnbs, false); // replacement for unicode font
6376                      $temppage = str_replace($alias_pau, $pnbu, $temppage);
6377                      if ($this->isunicode) {
6378                          $temppage = str_replace($alias_pbu, $pnbu, $temppage);
6379                          $temppage = str_replace($alias_pcu, $pnbu, $temppage);
6380                          $temppage = str_replace($alias_pb, $pnbs, $temppage);
6381                          $temppage = str_replace($alias_pc, $pnbs, $temppage);
6382                      }
6383                      $temppage = str_replace($alias_pa, $pnbs, $temppage);
6384                  }
6385                  $temppage = str_replace($this->epsmarker, '', $temppage);
6386                  //Page
6387                  $this->page_obj_id[$n] = $this->_newobj();
6388                  $out = '<</Type /Page';
6389                  $out .= ' /Parent 1 0 R';
6390                  $out .= ' '.sprintf('/MediaBox [0 0 %.2F %.2F]', $this->pagedim[$n]['w'], $this->pagedim[$n]['h']);
6391                  $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
6392                  $out .= ' /Resources 2 0 R';
6393                  $this->_out($out);
6394                  $this->_putannotsrefs($n);
6395                  $this->_out('/Contents '.($this->n + 1).' 0 R>> endobj');
6396                  //Page content
6397                  $p = ($this->compress) ? gzcompress($temppage) : $temppage;
6398                  $this->_newobj();
6399                  $this->_out('<<'.$filter.'/Length '.strlen($p).'>> '.$this->_getstream($p).' endobj');
6400                  if ($this->diskcache) {
6401                      // remove temporary files
6402                      unlink($this->pages[$n]);
6403                  }
6404              }
6405              //Pages root
6406              $this->offsets[1] = $this->bufferlen;
6407              $out = '1 0 obj <</Type /Pages  /Kids [';
6408              foreach($this->page_obj_id as $page_obj) {
6409                  $out .= ' '.$page_obj.' 0 R';
6410              }
6411              $out .= ' ] /Count '.$nb.' >>  endobj';
6412              $this->_out($out);
6413          }
6414  
6415          /**
6416           * Output references to page annotations
6417           * @param int $n page number
6418           * @access protected
6419           * @author Nicola Asuni
6420           * @since 4.7.000 (2008-08-29)
6421           */
6422  		protected function _putannotsrefs($n) {
6423              if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
6424                  return;
6425              }
6426              $out = '/Annots [';
6427              if (isset($this->PageAnnots[$n])) {
6428                  $num_annots = count($this->PageAnnots[$n]);
6429                  for ($i = 0; $i < $num_annots; ++$i) {
6430                      ++$this->curr_annot_obj_id;
6431                      if (!in_array($this->curr_annot_obj_id, $this->radio_groups)) {
6432                          $out .= ' '.$this->curr_annot_obj_id.' 0 R';
6433                      } else {
6434                          ++$num_annots;
6435                      }
6436                  }
6437              }
6438              if (($n==1) AND $this->sign AND isset($this->signature_data['cert_type'])) {
6439                  // set reference for signature object
6440                  $out .= ' '.$this->sig_annot_ref;
6441              }
6442              $out .= ' ]';
6443              $this->_out($out);
6444          }
6445  
6446          /**
6447           * Output annotations objects for all pages.
6448           * !!! THIS METHOD IS NOT YET COMPLETED !!!
6449           * See section 12.5 of PDF 32000_2008 reference.
6450           * @access protected
6451           * @author Nicola Asuni
6452           * @since 4.0.018 (2008-08-06)
6453           */
6454  		protected function _putannotsobjs() {
6455              // reset object counter
6456              $this->annot_obj_id = $this->annots_start_obj_id;
6457              for ($n=1; $n <= $this->numpages; ++$n) {
6458                  if (isset($this->PageAnnots[$n])) {
6459                      // set page annotations
6460                      foreach ($this->PageAnnots[$n] as $key => $pl) {
6461                          // create annotation object for grouping radiobuttons
6462                          if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
6463                              $annots = '<<';
6464                              $annots .= ' /Type /Annot';
6465                              $annots .= ' /Subtype /Widget';
6466                              $annots .= ' /T '.$this->_dataannobjstring($pl['txt']);
6467                              $annots .= ' /FT /Btn';
6468                              $annots .= ' /Ff 49152';
6469                              $annots .= ' /Kids [';
6470                              foreach ($this->radiobutton_groups[$n][$pl['txt']] as $data) {
6471                                  $annots .= ' '.$data['kid'].' 0 R';
6472                                  if ($data['def'] !== 'Off') {
6473                                      $defval = $data['def'];
6474                                  }
6475                              }
6476                              $annots .= ' ]';
6477                              if (isset($defval)) {
6478                                  $annots .= ' /V /'.$defval;
6479                              }
6480                              $annots .= ' >>';
6481                              ++$this->annot_obj_id;
6482                              $this->offsets[$this->annot_obj_id] = $this->bufferlen;
6483                              $this->_out($this->annot_obj_id.' 0 obj '.$annots.' endobj');
6484                              $this->form_obj_id[] = $this->annot_obj_id;
6485                              // store object id to be used on Parent entry of Kids
6486                              $this->radiobutton_groups[$n][$pl['txt']] = $this->annot_obj_id;
6487                          }
6488                          $formfield = false;
6489                          $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
6490                          $a = $pl['x'] * $this->k;
6491                          $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h'])  * $this->k);
6492                          $c = $pl['w'] * $this->k;
6493                          $d = $pl['h'] * $this->k;
6494                          $rect = sprintf('%.2F %.2F %.2F %.2F', $a, $b, $a+$c, $b+$d);
6495                          // create new annotation object
6496                          $annots = '<</Type /Annot';
6497                          $annots .= ' /Subtype /'.$pl['opt']['subtype'];
6498                          $annots .= ' /Rect ['.$rect.']';
6499                          $ft = array('Btn', 'Tx', 'Ch', 'Sig');
6500                          if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
6501                              $annots .= ' /FT /'.$pl['opt']['ft'];
6502                              $formfield = true;
6503                          }
6504                          $annots .= ' /Contents '.$this->_textannobjstring($pl['txt']);
6505                          $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
6506                          $annots .= ' /NM '.$this->_dataannobjstring(sprintf('%04u-%04u', $n, $key));
6507                          $annots .= ' /M '.$this->_datestring();
6508                          if (isset($pl['opt']['f'])) {
6509                              $val = 0;
6510                              if (is_array($pl['opt']['f'])) {
6511                                  foreach ($pl['opt']['f'] as $f) {
6512                                      switch (strtolower($f)) {
6513                                          case 'invisible': {
6514                                              $val += 1 << 0;
6515                                              break;
6516                                          }
6517                                          case 'hidden': {
6518                                              $val += 1 << 1;
6519                                              break;
6520                                          }
6521                                          case 'print': {
6522                                              $val += 1 << 2;
6523                                              break;
6524                                          }
6525                                          case 'nozoom': {
6526                                              $val += 1 << 3;
6527                                              break;
6528                                          }
6529                                          case 'norotate': {
6530                                              $val += 1 << 4;
6531                                              break;
6532                                          }
6533                                          case 'noview': {
6534                                              $val += 1 << 5;
6535                                              break;
6536                                          }
6537                                          case 'readonly': {
6538                                              $val += 1 << 6;
6539                                              break;
6540                                          }
6541                                          case 'locked': {
6542                                              $val += 1 << 8;
6543                                              break;
6544                                          }
6545                                          case 'togglenoview': {
6546                                              $val += 1 << 9;
6547                                              break;
6548                                          }
6549                                          case 'lockedcontents': {
6550                                              $val += 1 << 10;
6551                                              break;
6552                                          }
6553                                          default: {
6554                                              break;
6555                                          }
6556                                      }
6557                                  }
6558                              } else {
6559                                  $val = intval($pl['opt']['f']);
6560                              }
6561                              $annots .= ' /F '.intval($val);
6562                          }
6563                          if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
6564                              $annots .= ' /AS /'.$pl['opt']['as'];
6565                          }
6566                          if (isset($pl['opt']['ap'])) {
6567                              // appearance stream
6568                              $annots .= ' /AP <<';
6569                              if (is_array($pl['opt']['ap'])) {
6570                                  foreach ($pl['opt']['ap'] as $apmode => $apdef) {
6571                                      // $apmode can be: n = normal; r = rollover; d = down;
6572                                      $annots .= ' /'.strtoupper($apmode);
6573                                      if (is_array($apdef)) {
6574                                          $annots .= ' <<';
6575                                          foreach ($apdef as $apstate => $stream) {
6576                                              // reference to XObject that define the appearance for this mode-state
6577                                              $apsobjid = $this->_putAPXObject($c, $d, $stream);
6578                                              $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
6579                                          }
6580                                          $annots .= ' >>';
6581                                      } else {
6582                                          // reference to XObject that define the appearance for this mode
6583                                          $apsobjid = $this->_putAPXObject($c, $d, $apdef);
6584                                          $annots .= ' '.$apsobjid.' 0 R';
6585                                      }
6586                                  }
6587                              } else {
6588                                  $annots .= $pl['opt']['ap'];
6589                              }
6590                              $annots .= ' >>';
6591                          }
6592                          if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
6593                              $annots .= ' /BS <<';
6594                              $annots .= ' /Type /Border';
6595                              if (isset($pl['opt']['bs']['w'])) {
6596                                  $annots .= ' /W '.intval($pl['opt']['bs']['w']);
6597                              }
6598                              $bstyles = array('S', 'D', 'B', 'I', 'U');
6599                              if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
6600                                  $annots .= ' /S /'.$pl['opt']['bs']['s'];
6601                              }
6602                              if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
6603                                  $annots .= ' /D [';
6604                                  foreach ($pl['opt']['bs']['d'] as $cord) {
6605                                      $annots .= ' '.intval($cord);
6606                                  }
6607                                  $annots .= ']';
6608                              }
6609                              $annots .= ' >>';
6610                          } else {
6611                              $annots .= ' /Border [';
6612                              if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
6613                                  $annots .= intval($pl['opt']['border'][0]).' ';
6614                                  $annots .= intval($pl['opt']['border'][1]).' ';
6615                                  $annots .= intval($pl['opt']['border'][2]);
6616                                  if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
6617                                      $annots .= ' [';
6618                                      foreach ($pl['opt']['border'][3] as $dash) {
6619                                          $annots .= intval($dash).' ';
6620                                      }
6621                                      $annots .= ']';
6622                                  }
6623                              } else {
6624                                  $annots .= '0 0 0';
6625                              }
6626                              $annots .= ']';
6627                          }
6628                          if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
6629                              $annots .= ' /BE <<';
6630                              $bstyles = array('S', 'C');
6631                              if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $markups)) {
6632                                  $annots .= ' /S /'.$pl['opt']['bs']['s'];
6633                              } else {
6634                                  $annots .= ' /S /S';
6635                              }
6636                              if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
6637                                  $annots .= ' /I '.sprintf(' %.4F', $pl['opt']['be']['i']);
6638                              }
6639                              $annots .= '>>';
6640                          }
6641                          if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
6642                              $annots .= ' /C [';
6643                              foreach ($pl['opt']['c'] as $col) {
6644                                  $col = intval($col);
6645                                  $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
6646                                  $annots .= sprintf(' %.4F', $color);
6647                              }
6648                              $annots .= ']';
6649                          }
6650                          //$annots .= ' /StructParent ';
6651                          //$annots .= ' /OC ';
6652                          $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
6653                          if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
6654                              // this is a markup type
6655                              if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
6656                                  $annots .= ' /T '.$this->_textannobjstring($pl['opt']['t']);
6657                              }
6658                              //$annots .= ' /Popup ';
6659                              if (isset($pl['opt']['ca'])) {
6660                                  $annots .= ' /CA '.sprintf('%.4F', floatval($pl['opt']['ca']));
6661                              }
6662                              if (isset($pl['opt']['rc'])) {
6663                                  $annots .= ' /RC '.$this->_textannobjstring($pl['opt']['rc']);
6664                              }
6665                              $annots .= ' /CreationDate '.$this->_datestring();
6666                              //$annots .= ' /IRT ';
6667                              if (isset($pl['opt']['subj'])) {
6668                                  $annots .= ' /Subj '.$this->_textannobjstring($pl['opt']['subj']);
6669                              }
6670                              //$annots .= ' /RT ';
6671                              //$annots .= ' /IT ';
6672                              //$annots .= ' /ExData ';
6673                          }
6674                          $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
6675                          switch (strtolower($pl['opt']['subtype'])) {
6676                              case 'text': {
6677                                  if (isset($pl['opt']['open'])) {
6678                                      $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
6679                                  }
6680                                  $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
6681                                  if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
6682                                      $annots .= ' /Name /'.$pl['opt']['name'];
6683                                  } else {
6684                                      $annots .= ' /Name /Note';
6685                                  }
6686                                  $statemodels = array('Marked', 'Review');
6687                                  if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
6688                                      $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
6689                                  } else {
6690                                      $pl['opt']['statemodel'] = 'Marked';
6691                                      $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
6692                                  }
6693                                  if ($pl['opt']['statemodel'] == 'Marked') {
6694                                      $states = array('Accepted', 'Unmarked');
6695                                  } else {
6696                                      $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
6697                                  }
6698                                  if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
6699                                      $annots .= ' /State /'.$pl['opt']['state'];
6700                                  } else {
6701                                      if ($pl['opt']['statemodel'] == 'Marked') {
6702                                          $annots .= ' /State /Unmarked';
6703                                      } else {
6704                                          $annots .= ' /State /None';
6705                                      }
6706                                  }
6707                                  break;
6708                              }
6709                              case 'link': {
6710                                  if(is_string($pl['txt'])) {
6711                                      // external URI link
6712                                      $annots .= ' /A <</S /URI /URI '.$this->_dataannobjstring($this->unhtmlentities($pl['txt'])).'>>';
6713                                  } else {
6714                                      // internal link
6715                                      $l = $this->links[$pl['txt']];
6716                                      $annots .= sprintf(' /Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $l[0])), ($this->pagedim[$l[0]]['h'] - ($l[1] * $this->k)));
6717                                  }
6718                                  $hmodes = array('N', 'I', 'O', 'P');
6719                                  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
6720                                      $annots .= ' /H /'.$pl['opt']['h'];
6721                                  } else {
6722                                      $annots .= ' /H /I';
6723                                  }
6724                                  //$annots .= ' /PA ';
6725                                  //$annots .= ' /Quadpoints ';
6726                                  break;
6727                              }
6728                              case 'freetext': {
6729                                  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
6730                                      $annots .= ' /DA ('.$pl['opt']['da'].')';
6731                                  }
6732                                  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
6733                                      $annots .= ' /Q '.intval($pl['opt']['q']);
6734                                  }
6735                                  if (isset($pl['opt']['rc'])) {
6736                                      $annots .= ' /RC '.$this->_textannobjstring($pl['opt']['rc']);
6737                                  }
6738                                  if (isset($pl['opt']['ds'])) {
6739                                      $annots .= ' /DS '.$this->_textannobjstring($pl['opt']['ds']);
6740                                  }
6741                                  if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
6742                                      $annots .= ' /CL [';
6743                                      foreach ($pl['opt']['cl'] as $cl) {
6744                                          $annots .= sprintf('%.4F ', $cl * $this->k);
6745                                      }
6746                                      $annots .= ']';
6747                                  }
6748                                  $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
6749                                  if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
6750                                      $annots .= ' /IT '.$pl['opt']['it'];
6751                                  }
6752                                  if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
6753                                      $l = $pl['opt']['rd'][0] * $this->k;
6754                                      $r = $pl['opt']['rd'][1] * $this->k;
6755                                      $t = $pl['opt']['rd'][2] * $this->k;
6756                                      $b = $pl['opt']['rd'][3] * $this->k;
6757                                      $annots .= ' /RD ['.sprintf('%.2F %.2F %.2F %.2F', $l, $r, $t, $b).']';
6758                                  }
6759                                  if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
6760                                      $annots .= ' /LE /'.$pl['opt']['le'];
6761                                  }
6762                                  break;
6763                              }
6764                              case 'line': {
6765                                  break;
6766                              }
6767                              case 'square': {
6768                                  break;
6769                              }
6770                              case 'circle': {
6771                                  break;
6772                              }
6773                              case 'polygon': {
6774                                  break;
6775                              }
6776                              case 'polyline': {
6777                                  break;
6778                              }
6779                              case 'highlight': {
6780                                  break;
6781                              }
6782                              case 'underline': {
6783                                  break;
6784                              }
6785                              case 'squiggly': {
6786                                  break;
6787                              }
6788                              case 'strikeout': {
6789                                  break;
6790                              }
6791                              case 'stamp': {
6792                                  break;
6793                              }
6794                              case 'caret': {
6795                                  break;
6796                              }
6797                              case 'ink': {
6798                                  break;
6799                              }
6800                              case 'popup': {
6801                                  break;
6802                              }
6803                              case 'fileattachment': {
6804                                  if (!isset($pl['opt']['fs'])) {
6805                                      break;
6806                                  }
6807                                  $filename = basename($pl['opt']['fs']);
6808                                  if (isset($this->embeddedfiles[$filename]['n'])) {
6809                                      $annots .= ' /FS <</Type /Filespec /F '.$this->_dataannobjstring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
6810                                      $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
6811                                      if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
6812                                          $annots .= ' /Name /'.$pl['opt']['name'];
6813                                      } else {
6814                                          $annots .= ' /Name /PushPin';
6815                                      }
6816                                  }
6817                                  break;
6818                              }
6819                              case 'sound': {
6820                                  if (!isset($pl['opt']['fs'])) {
6821                                      break;
6822                                  }
6823                                  $filename = basename($pl['opt']['fs']);
6824                                  if (isset($this->embeddedfiles[$filename]['n'])) {
6825                                      // ... TO BE COMPLETED ...
6826                                      // /R /C /B /E /CO /CP
6827                                      $annots .= ' /Sound <</Type /Filespec /F '.$this->_dataannobjstring($filename).' /EF <</F '.$this->embeddedfiles[$filename]['n'].' 0 R>> >>';
6828                                      $iconsapp = array('Speaker', 'Mic');
6829                                      if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
6830                                          $annots .= ' /Name /'.$pl['opt']['name'];
6831                                      } else {
6832                                          $annots .= ' /Name /Speaker';
6833                                      }
6834                                  }
6835                                  break;
6836                              }
6837                              case 'movie': {
6838                                  break;
6839                              }
6840                              case 'widget': {
6841                                  $hmode = array('N', 'I', 'O', 'P', 'T');
6842                                  if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
6843                                      $annots .= ' /H /'.$pl['opt']['h'];
6844                                  }
6845                                   if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
6846                                       $annots .= ' /MK <<';
6847                                       if (isset($pl['opt']['mk']['r'])) {
6848                                           $annots .= ' /R '.$pl['opt']['mk']['r'];
6849                                       }
6850                                       if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
6851                                           $annots .= ' /BC [';
6852                                           foreach($pl['opt']['mk']['bc'] AS $col) {
6853                                               $col = intval($col);
6854                                              $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
6855                                               $annots .= sprintf(' %.2F', $color);
6856                                           }
6857                                           $annots .= ']';
6858                                       }
6859                                       if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
6860                                           $annots .= ' /BG [';
6861                                           foreach($pl['opt']['mk']['bg'] AS $col) {
6862                                               $col = intval($col);
6863                                              $color = $col <= 0 ? 0 : ($col >= 255 ? 1 : $col / 255);
6864                                               $annots .= sprintf(' %.2F', $color);
6865                                           }
6866                                           $annots .= ']';
6867                                       }
6868                                       if (isset($pl['opt']['mk']['ca'])) {
6869                                           $annots .= ' /CA '.$pl['opt']['mk']['ca'].'';
6870                                       }
6871                                       if (isset($pl['opt']['mk']['rc'])) {
6872                                           $annots .= ' /RC '.$pl['opt']['mk']['ca'].'';
6873                                       }
6874                                       if (isset($pl['opt']['mk']['ac'])) {
6875                                           $annots .= ' /AC '.$pl['opt']['mk']['ca'].'';
6876                                       }
6877                                       if (isset($pl['opt']['mk']['i'])) {
6878                                           $info = $this->getImageBuffer($pl['opt']['mk']['i']);
6879                                           if ($info !== false) {
6880                                               $annots .= ' /I '.$info['n'].' 0 R';
6881                                           }
6882                                       }
6883                                       if (isset($pl['opt']['mk']['ri'])) {
6884                                           $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
6885                                           if ($info !== false) {
6886                                               $annots .= ' /RI '.$info['n'].' 0 R';
6887                                           }
6888                                       }
6889                                       if (isset($pl['opt']['mk']['ix'])) {
6890                                           $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
6891                                           if ($info !== false) {
6892                                               $annots .= ' /IX '.$info['n'].' 0 R';
6893                                           }
6894                                       }
6895                                       if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
6896                                           $annots .= ' /IF <<';
6897                                           $if_sw = array('A', 'B', 'S', 'N');
6898                                          if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
6899                                              $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
6900                                          }
6901                                           $if_s = array('A', 'P');
6902                                          if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
6903                                              $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
6904                                          }
6905                                          if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
6906                                              $annots .= sprintf(' /A [%.2F  %.2F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
6907                                          }
6908                                          if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
6909                                              $annots .= ' /FB true';
6910                                          }
6911                                           $annots .= '>>';
6912                                       }
6913                                       if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
6914                                           $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
6915                                       } else {
6916                                           $annots .= ' /TP 0';
6917                                       }
6918                                       $annots .= '>>';
6919                                   } // end MK
6920                                   // --- Entries for field dictionaries ---
6921                                   if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
6922                                       // set parent
6923                                       $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
6924                                   }
6925                                   if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
6926                                      $annots .= ' /T '.$this->_dataannobjstring($pl['opt']['t']);
6927                                  }
6928                                  if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
6929                                      $annots .= ' /TU '.$this->_dataannobjstring($pl['opt']['tu']);
6930                                  }
6931                                  if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
6932                                      $annots .= ' /TM '.$this->_dataannobjstring($pl['opt']['tm']);
6933                                  }
6934                                  if (isset($pl['opt']['ff'])) {
6935                                      if (is_array($pl['opt']['ff'])) {
6936                                          // array of bit settings
6937                                          $flag = 0;
6938                                          foreach($pl['opt']['ff'] as $val) {
6939                                              $flag += 1 << ($val - 1);
6940                                          }
6941                                      } else {
6942                                          $flag = intval($pl['opt']['ff']);
6943                                      }
6944                                      $annots .= ' /Ff '.$flag;
6945                                  }
6946                                  if (isset($pl['opt']['maxlen'])) {
6947                                      $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
6948                                  }
6949                                  if (isset($pl['opt']['v'])) {
6950                                      $annots .= ' /V';
6951                                      if (is_array($pl['opt']['v'])) {
6952                                          foreach ($pl['opt']['v'] AS $optval) {
6953                                              if (is_float($optval)) {
6954                                                  $optval = sprintf('%.2F', $optval);
6955                                              }
6956                                              $annots .= ' '.$optval;
6957                                          }
6958                                      } else {
6959                                          $annots .= ' '.$this->_textannobjstring($pl['opt']['v']);
6960                                      }
6961                                  }
6962                                  if (isset($pl['opt']['dv'])) {
6963                                      $annots .= ' /DV';
6964                                      if (is_array($pl['opt']['dv'])) {
6965                                          foreach ($pl['opt']['dv'] AS $optval) {
6966                                              if (is_float($optval)) {
6967                                                  $optval = sprintf('%.2F', $optval);
6968                                              }
6969                                              $annots .= ' '.$optval;
6970                                          }
6971                                      } else {
6972                                          $annots .= ' '.$this->_textannobjstring($pl['opt']['dv']);
6973                                      }
6974                                  }
6975                                  if (isset($pl['opt']['rv'])) {
6976                                      $annots .= ' /RV';
6977                                      if (is_array($pl['opt']['rv'])) {
6978                                          foreach ($pl['opt']['rv'] AS $optval) {
6979                                              if (is_float($optval)) {
6980                                                  $optval = sprintf('%.2F', $optval);
6981                                              }
6982                                              $annots .= ' '.$optval;
6983                                          }
6984                                      } else {
6985                                          $annots .= ' '.$this->_textannobjstring($pl['opt']['rv']);
6986                                      }
6987                                  }
6988                                  if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
6989                                      $annots .= ' /A << '.$pl['opt']['a'].' >>';
6990                                  }
6991                                  if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
6992                                      $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
6993                                  }
6994                                  if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
6995                                      $annots .= ' /DA ('.$pl['opt']['da'].')';
6996                                  }
6997                                  if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
6998                                      $annots .= ' /Q '.intval($pl['opt']['q']);
6999                                  }
7000                                  if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
7001                                       $annots .= ' /Opt [';
7002                                       foreach($pl['opt']['opt'] AS $copt) {
7003                                           if (is_array($copt)) {
7004                                               $annots .= ' ['.$this->_textannobjstring($copt[0]).' '.$this->_textannobjstring($copt[1]).']';
7005                                           } else {
7006                                               $annots .= ' '.$this->_textannobjstring($copt);
7007                                           }
7008                                       }
7009                                       $annots .= ']';
7010                                   }
7011                                   if (isset($pl['opt']['ti'])) {
7012                                       $annots .= ' /TI '.intval($pl['opt']['ti']);
7013                                   }
7014                                   if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
7015                                       $annots .= ' /I [';
7016                                       foreach($pl['opt']['i'] AS $copt) {
7017                                           $annots .= intval($copt).' ';
7018                                       }
7019                                       $annots .= ']';
7020                                   }
7021                                  break;
7022                              }
7023                              case 'screen': {
7024                                  break;
7025                              }
7026                              case 'printermark': {
7027                                  break;
7028                              }
7029                              case 'trapnet': {
7030                                  break;
7031                              }
7032                              case 'watermark': {
7033                                  break;
7034                              }
7035                              case '3d': {
7036                                  break;
7037                              }
7038                              default: {
7039                                  break;
7040                              }
7041                          }
7042                          $annots .= '>>';
7043                          // create new annotation object
7044                          ++$this->annot_obj_id;
7045                          $this->offsets[$this->annot_obj_id] = $this->bufferlen;
7046                          $this->_out($this->annot_obj_id.' 0 obj '.$annots.' endobj');
7047                          if ($formfield AND ! isset($this->radiobutton_groups[$n][$pl['txt']])) {
7048                              // store reference of form object
7049                              $this->form_obj_id[] = $this->annot_obj_id;
7050                          }
7051                      }
7052                  }
7053              } // end for each page
7054          }
7055  
7056          /**
7057           * Put appearance streams XObject used to define annotation's appearance states
7058           * @param int $w annotation width
7059           * @param int $h annotation height
7060           * @param string $stream appearance stream
7061           * @return int object ID
7062           * @access protected
7063           * @since 4.8.001 (2009-09-09)
7064           */
7065  		protected function _putAPXObject($w=0, $h=0, $stream='') {
7066              $stream = trim($stream);
7067              ++$this->apxo_obj_id;
7068              $this->offsets[$this->apxo_obj_id] = $this->bufferlen;
7069              $out = $this->apxo_obj_id.' 0 obj';
7070              $out .= ' <<';
7071              $out .= ' /Type /XObject';
7072              $out .= ' /Subtype /Form';
7073              $out .= ' /FormType 1';
7074              if ($this->compress) {
7075                  $stream = gzcompress($stream);
7076                  $out .= ' /Filter /FlateDecode';
7077              }
7078              $rect = sprintf('%.2F %.2F', $w, $h);
7079              $out .= ' /BBox [0 0 '.$rect.']';
7080              $out .= ' /Matrix [1 0 0 1 0 0]';
7081              $out .= ' /Resources <</ProcSet [/PDF]>>';
7082              $out .= ' /Length '.strlen($stream);
7083              $out .= ' >>';
7084              $out .= ' '.$this->_getstream($stream);
7085              $out .= ' endobj';
7086              $this->_out($out);
7087              return $this->apxo_obj_id;
7088          }
7089  
7090          /**
7091           * Output fonts.
7092           * @access protected
7093           */
7094  		protected function _putfonts() {
7095              $nf = $this->n;
7096              foreach ($this->diffs as $diff) {
7097                  //Encodings
7098                  $this->_newobj();
7099                  $this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.']>> endobj');
7100              }
7101              $mqr = $this->get_mqr();
7102              $this->set_mqr(false);
7103              foreach ($this->FontFiles as $file => $info) {
7104                  // search and get font file to embedd
7105                  $fontdir = $info['fontdir'];
7106                  $file = strtolower($file);
7107                  $fontfile = '';
7108                  // search files on various directories
7109                  if (($fontdir !== false) AND file_exists($fontdir.$file)) {
7110                      $fontfile = $fontdir.$file;
7111                  } elseif (file_exists($this->_getfontpath().$file)) {
7112                      $fontfile = $this->_getfontpath().$file;
7113                  } elseif (file_exists($file)) {
7114                      $fontfile = $file;
7115                  }
7116                  if (!$this->empty_string($fontfile)) {
7117                      $font = file_get_contents($fontfile);
7118                      $compressed = (substr($file, -2) == '.z');
7119                      if ((!$compressed) AND (isset($info['length2']))) {
7120                          $header = (ord($font{0}) == 128);
7121                          if ($header) {
7122                              //Strip first binary header
7123                              $font = substr($font, 6);
7124                          }
7125                          if ($header AND (ord($font{$info['length1']}) == 128)) {
7126                              //Strip second binary header
7127                              $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
7128                          }
7129                      }
7130                      $this->_newobj();
7131                      $this->FontFiles[$file]['n'] = $this->n;
7132                      $out = '<</Length '.strlen($font);
7133                      if ($compressed) {
7134                          $out .= ' /Filter /FlateDecode';
7135                      }
7136                      $out .= ' /Length1 '.$info['length1'];
7137                      if (isset($info['length2'])) {
7138                          $out .= ' /Length2 '.$info['length2'].' /Length3 0';
7139                      }
7140                      $out .= ' >>';
7141                      $out .= ' '.$this->_getstream($font);
7142                      $out .= ' endobj';
7143                      $this->_out($out);
7144                  }
7145              }
7146              $this->set_mqr($mqr);
7147              foreach ($this->fontkeys as $k) {
7148                  //Font objects
7149                  $this->setFontSubBuffer($k, 'n', $this->n + 1);
7150                  $font = $this->getFontBuffer($k);
7151                  $type = $font['type'];
7152                  $name = $font['name'];
7153                  if ($type == 'core') {
7154                      //Standard font
7155                      $obj_id = $this->_newobj();
7156                      $out = '<</Type /Font';
7157                      $out .= ' /Subtype /Type1';
7158                      $out .= ' /BaseFont /'.$name;
7159                      $out .= ' /Name /F'.$font['i'];
7160                      if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
7161                          $out .= ' /Encoding /WinAnsiEncoding';
7162                      }
7163                      if (strtolower($name) == 'helvetica') {
7164                          // add default font for annotations
7165                          $this->annotation_fonts['helvetica'] = $k;
7166                      }
7167                      $out .= ' >> endobj';
7168                      $this->_out($out);
7169                  } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
7170                      //Additional Type1 or TrueType font
7171                      $obj_id = $this->_newobj();
7172                      $out = '<</Type /Font';
7173                      $out .= ' /Subtype /'.$type;
7174                      $out .= ' /BaseFont /'.$name;
7175                      $out .= ' /Name /F'.$font['i'];
7176                      $out .= ' /FirstChar 32 /LastChar 255';
7177                      $out .= ' /Widths '.($this->n + 1).' 0 R';
7178                      $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
7179                      if ($font['enc']) {
7180                          if (isset($font['diff'])) {
7181                              $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
7182                          } else {
7183                              $out .= ' /Encoding /WinAnsiEncoding';
7184                          }
7185                      }
7186                      $out .= ' >> endobj';
7187                      $this->_out($out);
7188                      // Widths
7189                      $this->_newobj();
7190                      $cw = &$font['cw'];
7191                      $s = '[';
7192                      for ($i = 32; $i < 256; ++$i) {
7193                          $s .= $cw[$i].' ';
7194                      }
7195                      $this->_out($s.'] endobj');
7196                      //Descriptor
7197                      $this->_newobj();
7198                      $s = '<</Type /FontDescriptor /FontName /'.$name;
7199                      foreach ($font['desc'] as $fdk => $fdv) {
7200                          if(is_float($fdv)) {
7201                              $fdv = sprintf('%.3F', $fdv);
7202                          }
7203                          $s .= ' /'.$fdk.' '.$fdv.'';
7204                      }
7205                      if (!$this->empty_string($font['file'])) {
7206                          $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
7207                      }
7208                      $this->_out($s.'>> endobj');
7209                  } else {
7210                      //Allow for additional types
7211                      $mtd = '_put'.strtolower($type);
7212                      if (!method_exists($this, $mtd)) {
7213                          $this->Error('Unsupported font type: '.$type);
7214                      }
7215                      $obj_id = $this->$mtd($font);
7216                  }
7217                  // store object ID for current font
7218                  $this->font_obj_ids[$k] = $obj_id;
7219              }
7220          }
7221  
7222          /**
7223           * Outputs font widths
7224           * @parameter array $font font data
7225           * @parameter int $cidoffset offset for CID values
7226           * @return PDF command string for font widths
7227           * @author Nicola Asuni
7228           * @access protected
7229           * @since 4.4.000 (2008-12-07)
7230           */
7231  		protected function _putfontwidths($font, $cidoffset=0) {
7232              ksort($font['cw']);
7233              $rangeid = 0;
7234              $range = array();
7235              $prevcid = -2;
7236              $prevwidth = -1;
7237              $interval = false;
7238              // for each character
7239              foreach ($font['cw'] as $cid => $width) {
7240                  $cid -= $cidoffset;
7241                  if ($width != $font['dw']) {
7242                      if ($cid == ($prevcid + 1)) {
7243                          // consecutive CID
7244                          if ($width == $prevwidth) {
7245                              if ($width == $range[$rangeid][0]) {
7246                                  $range[$rangeid][] = $width;
7247                              } else {
7248                                  array_pop($range[$rangeid]);
7249                                  // new range
7250                                  $rangeid = $prevcid;
7251                                  $range[$rangeid] = array();
7252                                  $range[$rangeid][] = $prevwidth;
7253                                  $range[$rangeid][] = $width;
7254                              }
7255                              $interval = true;
7256                              $range[$rangeid]['interval'] = true;
7257                          } else {
7258                              if ($interval) {
7259                                  // new range
7260                                  $rangeid = $cid;
7261                                  $range[$rangeid] = array();
7262                                  $range[$rangeid][] = $width;
7263                              } else {
7264                                  $range[$rangeid][] = $width;
7265                              }
7266                              $interval = false;
7267                          }
7268                      } else {
7269                          // new range
7270                          $rangeid = $cid;
7271                          $range[$rangeid] = array();
7272                          $range[$rangeid][] = $width;
7273                          $interval = false;
7274                      }
7275                      $prevcid = $cid;
7276                      $prevwidth = $width;
7277                  }
7278              }
7279              // optimize ranges
7280              $prevk = -1;
7281              $nextk = -1;
7282              $prevint = false;
7283              foreach ($range as $k => $ws) {
7284                  $cws = count($ws);
7285                  if (($k == $nextk) AND (!$prevint) AND ((!isset($ws['interval'])) OR ($cws < 4))) {
7286                      if (isset($range[$k]['interval'])) {
7287                          unset($range[$k]['interval']);
7288                      }
7289                      $range[$prevk] = array_merge($range[$prevk], $range[$k]);
7290                      unset($range[$k]);
7291                  } else {
7292                      $prevk = $k;
7293                  }
7294                  $nextk = $k + $cws;
7295                  if (isset($ws['interval'])) {
7296                      if ($cws > 3) {
7297                          $prevint = true;
7298                      } else {
7299                          $prevint = false;
7300                      }
7301                      unset($range[$k]['interval']);
7302                      --$nextk;
7303                  } else {
7304                      $prevint = false;
7305                  }
7306              }
7307              // output data
7308              $w = '';
7309              foreach ($range as $k => $ws) {
7310                  if (count(array_count_values($ws)) == 1) {
7311                      // interval mode is more compact
7312                      $w .= ' '.$k.' '.($k + count($ws) - 1).' '.$ws[0];
7313                  } else {
7314                      // range mode
7315                      $w .= ' '.$k.' [ '.implode(' ', $ws).' ]';
7316                  }
7317              }
7318              return '/W ['.$w.' ]';
7319          }
7320  
7321          /**
7322           * Adds unicode fonts.<br>
7323           * Based on PDF Reference 1.3 (section 5)
7324           * @parameter array $font font data
7325           * @return int font object ID
7326           * @access protected
7327           * @author Nicola Asuni
7328           * @since 1.52.0.TC005 (2005-01-05)
7329           */
7330  		protected function _puttruetypeunicode($font) {
7331              // Type0 Font
7332              // A composite font composed of other fonts, organized hierarchically
7333              $obj_id = $this->_newobj();
7334              $out = '<</Type /Font';
7335              $out .= ' /Subtype /Type0';
7336              $out .= ' /BaseFont /'.$font['name'].'';
7337              $out .= ' /Name /F'.$font['i'];
7338              $out .= ' /Encoding /'.$font['enc'];
7339              $out .= ' /ToUnicode /Identity-H';
7340              $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
7341              $out .= ' >>';
7342              $out .= ' endobj';
7343              $this->_out($out);
7344              // CIDFontType2
7345              // A CIDFont whose glyph descriptions are based on TrueType font technology
7346              $this->_newobj();
7347              $out = '<</Type /Font';
7348              $out .= ' /Subtype /CIDFontType2';
7349              $out .= ' /BaseFont /'.$font['name'];
7350              // A dictionary containing entries that define the character collection of the CIDFont.
7351              $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
7352              $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
7353              $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
7354              $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
7355              $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
7356              $out .= ' /DW '.$font['dw']; // default width
7357              $out .= "\n".$this->_putfontwidths($font, 0);
7358              $out .= ' /CIDToGIDMap '.($this->n + 2).' 0 R >> endobj';
7359              $this->_out($out);
7360              // Font descriptor
7361              // A font descriptor describing the CIDFont default metrics other than its glyph widths
7362              $this->_newobj();
7363              $out = '<</Type /FontDescriptor';
7364              $out .= ' /FontName /'.$font['name'];
7365              foreach ($font['desc'] as $key => $value) {
7366                  if(is_float($value)) {
7367                      $value = sprintf('%.3F', $value);
7368                  }
7369                  $out .= ' /'.$key.' '.$value;
7370              }
7371              $fontdir = false;
7372              if (!$this->empty_string($font['file'])) {
7373                  // A stream containing a TrueType font
7374                  $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
7375                  $fontdir = $this->FontFiles[$font['file']]['fontdir'];
7376              }
7377              $out .= ' >> endobj';
7378              $this->_out($out);
7379              if (isset($font['ctg']) AND (!$this->empty_string($font['ctg']))) {
7380                  $this->_newobj();
7381                  // Embed CIDToGIDMap
7382                  // A specification of the mapping from CIDs to glyph indices
7383                  // search and get CTG font file to embedd
7384                  $ctgfile = strtolower($font['ctg']);
7385                  // search and get ctg font file to embedd
7386                  $fontfile = '';
7387                  // search files on various directories
7388                  if (($fontdir !== false) AND file_exists($fontdir.$ctgfile)) {
7389                      $fontfile = $fontdir.$ctgfile;
7390                  } elseif (file_exists($this->_getfontpath().$ctgfile)) {
7391                      $fontfile = $this->_getfontpath().$ctgfile;
7392                  } elseif (file_exists($ctgfile)) {
7393                      $fontfile = $ctgfile;
7394                  }
7395                  if ($this->empty_string($fontfile)) {
7396                      $this->Error('Font file not found: '.$ctgfile);
7397                  }
7398                  $size = filesize($fontfile);
7399                  $out = '<</Length '.$size.'';
7400                  if (substr($fontfile, -2) == '.z') { // check file extension
7401                      // Decompresses data encoded using the public-domain
7402                      // zlib/deflate compression method, reproducing the
7403                      // original text or binary data
7404                      $out .= ' /Filter /FlateDecode';
7405                  }
7406                  $out .= ' >>';
7407                  $out .= ' '.$this->_getstream(file_get_contents($fontfile));
7408                  $out .= ' endobj';
7409                  $this->_out($out);
7410              }
7411              return $obj_id;
7412          }
7413  
7414          /**
7415           * Output CID-0 fonts.
7416           * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
7417           * @param array $font font data
7418           * @return int font object ID
7419           * @access protected
7420           * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
7421           * @since 3.2.000 (2008-06-23)
7422           */
7423  		protected function _putcidfont0($font) {
7424              $cidoffset = 0;
7425              if (!isset($font['cw'][1])) {
7426                  $cidoffset = 31;
7427              }
7428              if (isset($font['cidinfo']['uni2cid'])) {
7429                  // convert unicode to cid.
7430                  $uni2cid = $font['cidinfo']['uni2cid'];
7431                  $cw = array();
7432                  foreach ($font['cw'] as $uni => $width) {
7433                      if (isset($uni2cid[$uni])) {
7434                          $cw[($uni2cid[$uni] + $cidoffset)] = $width;
7435                      } elseif ($uni < 256) {
7436                          $cw[$uni] = $width;
7437                      } // else unknown character
7438                  }
7439                  $font = array_merge($font, array('cw' => $cw));
7440              }
7441              $name = $font['name'];
7442              $enc = $font['enc'];
7443              if ($enc) {
7444                  $longname = $name.'-'.$enc;
7445              } else {
7446                  $longname = $name;
7447              }
7448              $obj_id = $this->_newobj();
7449              $out = '<</Type /Font';
7450              $out .= ' /Subtype /Type0';
7451              $out .= ' /BaseFont /'.$longname;
7452              $out .= ' /Name /F'.$font['i'];
7453              if ($enc) {
7454                  $out .= ' /Encoding /'.$enc;
7455              }
7456              $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
7457              $out .= ' >> endobj';
7458              $this->_out($out);
7459              $this->_newobj();
7460              $out = '<</Type /Font';
7461              $out .= ' /Subtype /CIDFontType0';
7462              $out .= ' /BaseFont /'.$name;
7463              $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry']);
7464              $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering']);
7465              $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
7466              $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
7467              $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
7468              $out .= ' /DW '.$font['dw'];
7469              $out .= "\n".$this->_putfontwidths($font, $cidoffset);
7470              $out .= ' >> endobj';
7471              $this->_out($out);
7472              $this->_newobj();
7473              $s = '<</Type /FontDescriptor /FontName /'.$name;
7474              foreach ($font['desc'] as $k => $v) {
7475                  if ($k != 'Style') {
7476                      if(is_float($v)) {
7477                          $v = sprintf('%.3F', $v);
7478                      }
7479                      $s .= ' /'.$k.' '.$v.'';
7480                  }
7481              }
7482              $this->_out($s.'>> endobj');
7483              return $obj_id;
7484          }
7485  
7486          /**
7487           * Output images.
7488           * @access protected
7489           */
7490  		protected function _putimages() {
7491              $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7492              foreach ($this->imagekeys as $file) {
7493                  $info = $this->getImageBuffer($file);
7494                  $this->_newobj();
7495                  $this->setImageSubBuffer($file, 'n', $this->n);
7496                  $out = '<</Type /XObject';
7497                  $out .= ' /Subtype /Image';
7498                  $out .= ' /Width '.$info['w'];
7499                  $out .= ' /Height '.$info['h'];
7500                  if (array_key_exists('masked', $info)) {
7501                      $out .= ' /SMask '.($this->n - 1).' 0 R';
7502                  }
7503                  if ($info['cs'] == 'Indexed') {
7504                      $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
7505                  } else {
7506                      $out .= ' /ColorSpace /'.$info['cs'];
7507                      if ($info['cs'] == 'DeviceCMYK') {
7508                          $out .= ' /Decode [1 0 1 0 1 0 1 0]';
7509                      }
7510                  }
7511                  $out .= ' /BitsPerComponent '.$info['bpc'];
7512                  if (isset($info['f'])) {
7513                      $out .= ' /Filter /'.$info['f'];
7514                  }
7515                  if (isset($info['parms'])) {
7516                      $out .= ' '.$info['parms'];
7517                  }
7518                  if (isset($info['trns']) AND is_array($info['trns'])) {
7519                      $trns='';
7520                      $count_info = count($info['trns']);
7521                      for ($i=0; $i < $count_info; ++$i) {
7522                          $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
7523                      }
7524                      $out .= ' /Mask ['.$trns.']';
7525                  }
7526                  $out .= ' /Length '.strlen($info['data']).' >>';
7527                  $out .= ' '.$this->_getstream($info['data']);
7528                  $out .= ' endobj';
7529                  $this->_out($out);
7530                  //Palette
7531                  if ($info['cs'] == 'Indexed') {
7532                      $this->_newobj();
7533                      $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
7534                      $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> '.$this->_getstream($pal).' endobj');
7535                  }
7536              }
7537          }
7538  
7539          /**
7540           * Output Spot Colors Resources.
7541           * @access protected
7542           * @since 4.0.024 (2008-09-12)
7543           */
7544  		protected function _putspotcolors() {
7545              foreach ($this->spot_colors as $name => $color) {
7546                  $this->_newobj();
7547                  $this->spot_colors[$name]['n'] = $this->n;
7548                  $out = '[/Separation /'.str_replace(' ', '#20', $name);
7549                  $out .= ' /DeviceCMYK <<';
7550                  $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
7551                  $out .= ' '.sprintf('/C1 [%.4F %.4F %.4F %.4F] ', $color['c']/100, $color['m']/100, $color['y']/100, $color['k']/100);
7552                  $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
7553                  $out .= ' endobj';
7554                  $this->_out($out);
7555              }
7556          }
7557  
7558          /**
7559           * Output Resources Dictionary.
7560           * @access protected
7561           */
7562  		protected function _putresourcedict() {
7563              $out = '2 0 obj';
7564              $out .= ' << /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
7565              $out .= ' /Font <<';
7566              foreach ($this->fontkeys as $fontkey) {
7567                  $font = $this->getFontBuffer($fontkey);
7568                  $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
7569              }
7570              $out .= ' >>';
7571              $out .= ' /XObject <<';
7572              foreach ($this->imagekeys as $file) {
7573                  $info = $this->getImageBuffer($file);
7574                  $out .= ' /I'.$info['i'].' '.$info['n'].' 0 R';
7575              }
7576              $out .= ' >>';
7577              // visibility
7578              $out .= ' /Properties <</OC1 '.$this->n_ocg_print.' 0 R /OC2 '.$this->n_ocg_view.' 0 R>>';
7579              // transparency
7580              $out .= ' /ExtGState <<';
7581              foreach ($this->extgstates as $k => $extgstate) {
7582                  if (isset($extgstate['name'])) {
7583                      $out .= ' /'.$extgstate['name'];
7584                  } else {
7585                      $out .= ' /GS'.$k;
7586                  }
7587                  $out .= ' '.$extgstate['n'].' 0 R';
7588              }
7589              $out .= ' >>';
7590              // gradient patterns
7591              if (isset($this->gradients) AND (count($this->gradients) > 0)) {
7592                  $out .= ' /Pattern <<';
7593                  foreach ($this->gradients as $id => $grad) {
7594                      $out .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
7595                  }
7596                  $out .= ' >>';
7597              }
7598              // gradient shadings
7599              if (isset($this->gradients) AND (count($this->gradients) > 0)) {
7600                  $out .= ' /Shading <<';
7601                  foreach ($this->gradients as $id => $grad) {
7602                      $out .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
7603                  }
7604                  $out .= ' >>';
7605              }
7606              // spot colors
7607              if (isset($this->spot_colors) AND (count($this->spot_colors) > 0)) {
7608                  $out .= ' /ColorSpace <<';
7609                  foreach ($this->spot_colors as $color) {
7610                      $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
7611                  }
7612                  $out .= ' >>';
7613              }
7614              $out .= ' >> endobj';
7615              $this->_out($out);
7616          }
7617  
7618          /**
7619           * Output Resources.
7620           * @access protected
7621           */
7622  		protected function _putresources() {
7623              $this->_putextgstates();
7624              $this->_putocg();
7625              $this->_putfonts();
7626              $this->_putimages();
7627              $this->_putspotcolors();
7628              $this->_putshaders();
7629              //Resource dictionary
7630              $this->offsets[2] = $this->bufferlen;
7631              $this->_putresourcedict();
7632              $this->_putbookmarks();
7633              $this->_putEmbeddedFiles();
7634              $this->_putannotsobjs();
7635              $this->_putjavascript();
7636              // encryption
7637              $this->_putencryption();
7638          }
7639  
7640          /**
7641           * Adds some Metadata information (Document Information Dictionary)
7642           * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
7643           * @access protected
7644           */
7645  		protected function _putinfo() {
7646              $this->_newobj();
7647              $out = '<<';
7648              if (!$this->empty_string($this->title)) {
7649                  // The document's title.
7650                  $out .= ' /Title '.$this->_textstring($this->title);
7651              }
7652              if (!$this->empty_string($this->author)) {
7653                  // The name of the person who created the document.
7654                  $out .= ' /Author '.$this->_textstring($this->author);
7655              }
7656              if (!$this->empty_string($this->subject)) {
7657                  // The subject of the document.
7658                  $out .= ' /Subject '.$this->_textstring($this->subject);
7659              }
7660              if (!$this->empty_string($this->keywords)) {
7661                  // Keywords associated with the document.
7662                  $out .= ' /Keywords '.$this->_textstring($this->keywords.' TCP'.'DF');
7663              }
7664              if (!$this->empty_string($this->creator)) {
7665                  // If the document was converted to PDF from another format, the name of the conforming product that created the original document from which it was converted.
7666                  $out .= ' /Creator '.$this->_textstring($this->creator);
7667              }
7668              if (defined('PDF_PRODUCER')) {
7669                  // If the document was converted to PDF from another format, the name of the conforming product that converted it to PDF.
7670                  $out .= ' /Producer '.$this->_textstring(PDF_PRODUCER.' (TCP'.'DF)');
7671              } else {
7672                  // default producer
7673                  $out .= ' /Producer '.$this->_textstring('TCP'.'DF');
7674              }
7675              // The date and time the document was created, in human-readable form
7676              $out .= ' /CreationDate '.$this->_datestring();
7677              // The date and time the document was most recently modified, in human-readable form
7678              $out .= ' /ModDate '.$this->_datestring();
7679              // A name object indicating whether the document has been modified to include trapping information
7680              //$out .= ' /Trapped /False');
7681              $out .= ' >> endobj';
7682              $this->_out($out);
7683          }
7684  
7685          /**
7686           * Output Catalog.
7687           * @access protected
7688           */
7689  		protected function _putcatalog() {
7690              $this->_newobj();
7691              $out = '<< /Type /Catalog';
7692              $out .= ' /Pages 1 0 R';
7693              if ($this->ZoomMode == 'fullpage') {
7694                  $out .= ' /OpenAction [3 0 R /Fit]';
7695              } elseif ($this->ZoomMode == 'fullwidth') {
7696                  $out .= ' /OpenAction [3 0 R /FitH null]';
7697              } elseif ($this->ZoomMode == 'real') {
7698                  $out .= ' /OpenAction [3 0 R /XYZ null null 1]';
7699              } elseif (!is_string($this->ZoomMode)) {
7700                  $out .= ' /OpenAction [3 0 R /XYZ null null '.($this->ZoomMode / 100).']';
7701              }
7702              if (isset($this->LayoutMode) AND (!$this->empty_string($this->LayoutMode))) {
7703                  $out .= ' /PageLayout /'.$this->LayoutMode;
7704              }
7705              if (isset($this->PageMode) AND (!$this->empty_string($this->PageMode))) {
7706                  $out .= ' /PageMode /'.$this->PageMode;
7707              }
7708              if (isset($this->l['a_meta_language'])) {
7709                  $out .= ' /Lang /'.$this->l['a_meta_language'];
7710              }
7711              $out .= ' /Names <<';
7712              if ((!empty($this->javascript)) OR (!empty($this->js_objects))) {
7713                  $out .= ' /JavaScript '.($this->n_js).' 0 R';
7714              }
7715              $out .= ' >>';
7716              if (count($this->outlines) > 0) {
7717                  $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
7718                  $out .= ' /PageMode /UseOutlines';
7719              }
7720              $out .= ' '.$this->_putviewerpreferences();
7721              $p = $this->n_ocg_print.' 0 R';
7722              $v = $this->n_ocg_view.' 0 R';
7723              $as = '<</Event /Print /OCGs ['.$p.' '.$v.'] /Category [/Print]>> <</Event /View /OCGs ['.$p.' '.$v.'] /Category [/View]>>';
7724              $out .= ' /OCProperties <</OCGs ['.$p.' '.$v.'] /D <</ON ['.$p.'] /OFF ['.$v.'] /AS ['.$as.']>>>>';
7725              // AcroForm
7726              if (!empty($this->form_obj_id) OR ($this->sign AND isset($this->signature_data['cert_type']))) {
7727                  $out .= ' /AcroForm<<';
7728                  $objrefs = '';
7729                  if ($this->sign AND isset($this->signature_data['cert_type'])) {
7730                      $objrefs .= $this->sig_obj_id.' 0 R';
7731                  }
7732                  if (!empty($this->form_obj_id)) {
7733                      foreach($this->form_obj_id as $objid) {
7734                          $objrefs .= ' '.$objid.' 0 R';
7735                      }
7736                  }
7737                  $out .= ' /Fields ['.$objrefs.']';
7738                  $out .= ' /NeedAppearances '.(empty($this->form_obj_id)?'false':'true');
7739                  if ($this->sign AND isset($this->signature_data['cert_type'])) {
7740                      $out .= ' /SigFlags 3';
7741                  }
7742                  //$out .= ' /CO ';
7743                  if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
7744                      $out .= ' /DR <<';
7745                      $out .= ' /Font <<';
7746                      foreach ($this->annotation_fonts as $font => $fontkey) {
7747                          $out .= ' /F'.($fontkey + 1).' '.$this->font_obj_ids[$font].' 0 R';
7748                      }
7749                      $out .= ' >> >>';
7750                  }
7751                  $out .= ' /DA (/F'.(array_search('helvetica', $this->fontkeys) + 1).' 0 Tf 0 g)';
7752                  $out .= ' /Q '.(($this->rtl)?'2':'0');
7753                  //$out .= ' /XFA ';
7754                  $out .= ' >>';
7755                  // signatures
7756                  if ($this->sign AND isset($this->signature_data['cert_type'])) {
7757                      if ($this->signature_data['cert_type'] > 0) {
7758                          $out .= ' /Perms<</DocMDP '.($this->sig_obj_id + 1).' 0 R>>';
7759                      } else {
7760                          $out .= ' /Perms<</UR3 '.($this->sig_obj_id + 1).' 0 R>>';
7761                      }
7762                  }
7763              }
7764              $out .= ' >> endobj';
7765              $this->_out($out);
7766          }
7767  
7768          /**
7769           * Output viewer preferences.
7770           * @return string for viewer preferences
7771           * @author Nicola asuni
7772           * @since 3.1.000 (2008-06-09)
7773           * @access protected
7774           */
7775  		protected function _putviewerpreferences() {
7776              $out = '/ViewerPreferences <<';
7777              if ($this->rtl) {
7778                  $out .= ' /Direction /R2L';
7779              } else {
7780                  $out .= ' /Direction /L2R';
7781              }
7782              if (isset($this->viewer_preferences['HideToolbar']) AND ($this->viewer_preferences['HideToolbar'])) {
7783                  $out .= ' /HideToolbar true';
7784              }
7785              if (isset($this->viewer_preferences['HideMenubar']) AND ($this->viewer_preferences['HideMenubar'])) {
7786                  $out .= ' /HideMenubar true';
7787              }
7788              if (isset($this->viewer_preferences['HideWindowUI']) AND ($this->viewer_preferences['HideWindowUI'])) {
7789                  $out .= ' /HideWindowUI true';
7790              }
7791              if (isset($this->viewer_preferences['FitWindow']) AND ($this->viewer_preferences['FitWindow'])) {
7792                  $out .= ' /FitWindow true';
7793              }
7794              if (isset($this->viewer_preferences['CenterWindow']) AND ($this->viewer_preferences['CenterWindow'])) {
7795                  $out .= ' /CenterWindow true';
7796              }
7797              if (isset($this->viewer_preferences['DisplayDocTitle']) AND ($this->viewer_preferences['DisplayDocTitle'])) {
7798                  $out .= ' /DisplayDocTitle true';
7799              }
7800              if (isset($this->viewer_preferences['NonFullScreenPageMode'])) {
7801                  $out .= ' /NonFullScreenPageMode /'.$this->viewer_preferences['NonFullScreenPageMode'];
7802              }
7803              if (isset($this->viewer_preferences['ViewArea'])) {
7804                  $out .= ' /ViewArea /'.$this->viewer_preferences['ViewArea'];
7805              }
7806              if (isset($this->viewer_preferences['ViewClip'])) {
7807                  $out .= ' /ViewClip /'.$this->viewer_preferences['ViewClip'];
7808              }
7809              if (isset($this->viewer_preferences['PrintArea'])) {
7810                  $out .= ' /PrintArea /'.$this->viewer_preferences['PrintArea'];
7811              }
7812              if (isset($this->viewer_preferences['PrintClip'])) {
7813                  $out .= ' /PrintClip /'.$this->viewer_preferences['PrintClip'];
7814              }
7815              if (isset($this->viewer_preferences['PrintScaling'])) {
7816                  $out .= ' /PrintScaling /'.$this->viewer_preferences['PrintScaling'];
7817              }
7818              if (isset($this->viewer_preferences['Duplex']) AND (!$this->empty_string($this->viewer_preferences['Duplex']))) {
7819                  $out .= ' /Duplex /'.$this->viewer_preferences['Duplex'];
7820              }
7821              if (isset($this->viewer_preferences['PickTrayByPDFSize'])) {
7822                  if ($this->viewer_preferences['PickTrayByPDFSize']) {
7823                      $out .= ' /PickTrayByPDFSize true';
7824                  } else {
7825                      $out .= ' /PickTrayByPDFSize false';
7826                  }
7827              }
7828              if (isset($this->viewer_preferences['PrintPageRange'])) {
7829                  $PrintPageRangeNum = '';
7830                  foreach ($this->viewer_preferences['PrintPageRange'] as $k => $v) {
7831                      $PrintPageRangeNum .= ' '.($v - 1).'';
7832                  }
7833                  $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
7834              }
7835              if (isset($this->viewer_preferences['NumCopies'])) {
7836                  $out .= ' /NumCopies '.intval($this->viewer_preferences['NumCopies']);
7837              }
7838              $out .= ' >>';
7839              return $out;
7840          }
7841  
7842          /**
7843           * Output trailer.
7844           * @access protected
7845           */
7846  		protected function _puttrailer() {
7847              $out = 'trailer <<';
7848              $out .= ' /Size '.($this->n + 1);
7849              $out .= ' /Root '.$this->n.' 0 R';
7850              $out .= ' /Info '.($this->n - 1).' 0 R';
7851              if ($this->encrypted) {
7852                  $out .= ' /Encrypt '.$this->enc_obj_id.' 0 R';
7853                  $out .= ' /ID [()()]';
7854              }
7855              $out .= ' >>';
7856              $this->_out($out);
7857          }
7858  
7859          /**
7860           * Output PDF header.
7861           * @access protected
7862           */
7863  		protected function _putheader() {
7864              $this->_out('%PDF-'.$this->PDFVersion);
7865          }
7866  
7867          /**
7868           * Output end of document (EOF).
7869           * @access protected
7870           */
7871  		protected function _enddoc() {
7872              $this->state = 1;
7873              $this->_putheader();
7874              $this->_putpages();
7875              $this->_putresources();
7876              // Signature
7877              if ($this->sign AND isset($this->signature_data['cert_type'])) {
7878                  // widget annotation for signature
7879                  $this->sig_obj_id = $this->_newobj();
7880                  // --- replace signature ID on the first page ---
7881                  // get the document content
7882                  $pdfdoc = $this->getBuffer();
7883                  // Remove the original buffer
7884                  if (isset($this->diskcache) AND $this->diskcache) {
7885                      // remove buffer file from cache
7886                      unlink($this->buffer);
7887                  }
7888                  unset($this->buffer);
7889                  $signature_widget_ref = sprintf('%u 0 R', $this->sig_obj_id);
7890                  $signature_widget_ref .= str_repeat(' ', (strlen($this->sig_annot_ref) - strlen($signature_widget_ref)));
7891                  $pdfdoc = str_replace($this->sig_annot_ref, $signature_widget_ref, $pdfdoc);
7892                  $this->diskcache = false;
7893                  $this->buffer = &$pdfdoc;
7894                  $this->bufferlen = strlen($pdfdoc);
7895                  // ---
7896                  $out = '<< /Type /Annot /Subtype /Widget /Rect [0 0 0 0]';
7897                  $out .= ' /P 3 0 R'; // link to first page object
7898                  $out .= ' /FT /Sig';
7899                  $out .= ' /T '.$this->_textstring('Signature');
7900                  $out .= ' /Ff 0';
7901                  $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
7902                  $out .= ' >> endobj';
7903                  $this->_out($out);
7904                  // signature
7905                  $this->_putsignature();
7906              }
7907              // Info
7908              $this->_putinfo();
7909              // Catalog
7910              $this->_putcatalog();
7911              // Cross-ref
7912              $o = $this->bufferlen;
7913              $this->_out('xref');
7914              $this->_out('0 '.($this->n + 1));
7915              $this->_out('0000000000 65535 f ');
7916              for ($i=1; $i <= $this->n; ++$i) {
7917                  $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
7918              }
7919              // Embedded Files
7920              if (isset($this->embeddedfiles) AND count($this->embeddedfiles) > 0) {
7921                  $this->_out($this->embedded_start_obj_id.' '.count($this->embeddedfiles));
7922                  foreach ($this->embeddedfiles as $filename => $filedata) {
7923                      $this->_out(sprintf('%010d 00000 n ', $this->offsets[$filedata['n']]));
7924                  }
7925              }
7926              // Annotation Objects
7927              if ($this->annot_obj_id > $this->annots_start_obj_id) {
7928                  $this->_out(($this->annots_start_obj_id + 1).' '.($this->annot_obj_id - $this->annots_start_obj_id));
7929                  for ($i = ($this->annots_start_obj_id + 1); $i <= $this->annot_obj_id; ++$i) {
7930                      $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
7931                  }
7932              }
7933              // Javascript Objects
7934              if ($this->js_obj_id > $this->js_start_obj_id) {
7935                  $this->_out(($this->js_start_obj_id + 1).' '.($this->js_obj_id - $this->js_start_obj_id));
7936                  for ($i = ($this->js_start_obj_id + 1); $i <= $this->js_obj_id; ++$i) {
7937                      $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
7938                  }
7939              }
7940              // Appearance streams XObjects
7941              if ($this->apxo_obj_id > $this->apxo_start_obj_id) {
7942                  $this->_out(($this->apxo_start_obj_id + 1).' '.($this->apxo_obj_id - $this->apxo_start_obj_id));
7943                  for ($i = ($this->apxo_start_obj_id + 1); $i <= $this->apxo_obj_id; ++$i) {
7944                      $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
7945                  }
7946              }
7947              //Trailer
7948              $this->_puttrailer();
7949              $this->_out('startxref');
7950              $this->_out($o);
7951              $this->_out('%%EOF');
7952              $this->state = 3; // end-of-doc
7953              if ($this->diskcache) {
7954                  // remove temporary files used for images
7955                  foreach ($this->imagekeys as $key) {
7956                      // remove temporary files
7957                      unlink($this->images[$key]);
7958                  }
7959                  foreach ($this->fontkeys as $key) {
7960                      // remove temporary files
7961                      unlink($this->fonts[$key]);
7962                  }
7963              }
7964          }
7965  
7966          /**
7967           * Initialize a new page.
7968           * @param string $orientation page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
7969           * @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>
7970           * @access protected
7971           */
7972  		protected function _beginpage($orientation='', $format='') {
7973              ++$this->page;
7974              $this->setPageBuffer($this->page, '');
7975              // initialize array for graphics tranformation positions inside a page buffer
7976              $this->transfmrk[$this->page] = array();
7977              $this->state = 2;
7978              if ($this->empty_string($orientation)) {
7979                  if (isset($this->CurOrientation)) {
7980                      $orientation = $this->CurOrientation;
7981                  } else {
7982                      $orientation = 'P';
7983                  }
7984              }
7985              if ($this->empty_string($format)) {
7986                  $this->setPageOrientation($orientation);
7987              } else {
7988                  $this->setPageFormat($format, $orientation);
7989              }
7990              if ($this->rtl) {
7991                  $this->x = $this->w - $this->rMargin;
7992              } else {
7993                  $this->x = $this->lMargin;
7994              }
7995              $this->y = $this->tMargin;
7996              if (isset($this->newpagegroup[$this->page])) {
7997                  // start a new group
7998                  $n = sizeof($this->pagegroups) + 1;
7999                  $alias = '{nb'.$n.'}';
8000                  $this->pagegroups[$alias] = 1;
8001                  $this->currpagegroup = $alias;
8002              } elseif ($this->currpagegroup) {
8003                  ++$this->pagegroups[$this->currpagegroup];
8004              }
8005          }
8006  
8007          /**
8008           * Mark end of page.
8009           * @access protected
8010           */
8011  		protected function _endpage() {
8012              $this->setVisibility('all');
8013              $this->state = 1;
8014          }
8015  
8016          /**
8017           * Begin a new object and return the object number.
8018           * @return int object number
8019           * @access protected
8020           */
8021  		protected function _newobj() {
8022              ++$this->n;
8023              $this->offsets[$this->n] = $this->bufferlen;
8024              $this->_out($this->n.' 0 obj');
8025              return $this->n;
8026          }
8027  
8028          /**
8029           * Underline text.
8030           * @param int $x X coordinate
8031           * @param int $y Y coordinate
8032           * @param string $txt text to underline
8033           * @access protected
8034           */
8035  		protected function _dounderline($x, $y, $txt) {
8036              $w = $this->GetStringWidth($txt);
8037              return $this->_dounderlinew($x, $y, $w);
8038          }
8039  
8040          /**
8041           * Underline for rectangular text area.
8042           * @param int $x X coordinate
8043           * @param int $y Y coordinate
8044           * @param int $w width to underline
8045           * @access protected
8046           * @since 4.8.008 (2009-09-29)
8047           */
8048  		protected function _dounderlinew($x, $y, $w) {
8049              $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
8050              return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - $y + ($linew / 2)) * $this->k, $w * $this->k, $linew);
8051          }
8052  
8053          /**
8054           * Line through text.
8055           * @param int $x X coordinate
8056           * @param int $y Y coordinate
8057           * @param string $txt text to linethrough
8058           * @access protected
8059           */
8060  		protected function _dolinethrough($x, $y, $txt) {
8061              $w = $this->GetStringWidth($txt);
8062              return $this->_dolinethroughw($x, $y, $w);
8063          }
8064  
8065          /**
8066           * Line through for rectangular text area.
8067           * @param int $x X coordinate
8068           * @param int $y Y coordinate
8069           * @param string $txt text to linethrough
8070           * @access protected
8071           * @since 4.9.008 (2009-09-29)
8072           */
8073  		protected function _dolinethroughw($x, $y, $w) {
8074              $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
8075              return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - $y + ($this->FontSize / 3) + ($linew / 2)) * $this->k, $w * $this->k, $linew);
8076          }
8077  
8078          /**
8079           * Overline text.
8080           * @param int $x X coordinate
8081           * @param int $y Y coordinate
8082           * @param string $txt text to overline
8083           * @access protected
8084           * @since 4.9.015 (2010-04-19)
8085           */
8086  		protected function _dooverline($x, $y, $txt) {
8087              $w = $this->GetStringWidth($txt);
8088              return $this->_dooverlinew($x, $y, $w);
8089          }
8090  
8091          /**
8092           * Overline for rectangular text area.
8093           * @param int $x X coordinate
8094           * @param int $y Y coordinate
8095           * @param int $w width to overline
8096           * @access protected
8097           * @since 4.9.015 (2010-04-19)
8098           */
8099  		protected function _dooverlinew($x, $y, $w) {
8100              $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
8101              return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - $y + $this->FontAscent - ($linew / 2)) * $this->k, $w * $this->k, $linew);
8102  
8103          }
8104  
8105          /**
8106           * Read a 4-byte integer from file.
8107           * @param string $f file name.
8108           * @return 4-byte integer
8109           * @access protected
8110           */
8111  		protected function _freadint($f) {
8112              $a = unpack('Ni', fread($f, 4));
8113              return $a['i'];
8114          }
8115  
8116          /**
8117           * Add "\" before "\", "(" and ")"
8118           * @param string $s string to escape.
8119           * @return string escaped string.
8120           * @access protected
8121           */
8122  		protected function _escape($s) {
8123              // the chr(13) substitution fixes the Bugs item #1421290.
8124              return strtr($s, array(')' => '\\)', '(' => '\\(', '\\' => '\\\\', chr(13) => '\r'));
8125          }
8126  
8127          /**
8128           * Format a data string for meta information
8129           * @param string $s data string to escape.
8130           * @return string escaped string.
8131           * @access protected
8132           */
8133  		protected function _datastring($s) {
8134              if ($this->encrypted) {
8135                  $s = $this->_RC4($this->_objectkey($this->n), $s);
8136              }
8137              return '('. $this->_escape($s).')';
8138          }
8139  
8140          /**
8141           * Format a data string for annotation objects
8142           * @param string $s data string to escape.
8143           * @return string escaped string.
8144           * @access protected
8145           */
8146  		protected function _dataannobjstring($s) {
8147              if ($this->encrypted) {
8148                  $s = $this->_RC4($this->_objectkey($this->annot_obj_id + 1), $s);
8149              }
8150              return '('. $this->_escape($s).')';
8151          }
8152  
8153          /**
8154           * Returns a formatted date for meta information
8155           * @return string escaped date string.
8156           * @access protected
8157           * @since 4.6.028 (2009-08-25)
8158           */
8159  		protected function _datestring() {
8160              $current_time = substr_replace(date('YmdHisO'), '\'', (0 - 2), 0).'\'';
8161              return $this->_datastring('D:'.$current_time);
8162          }
8163  
8164          /**
8165           * Format a text string for meta information
8166           * @param string $s string to escape.
8167           * @return string escaped string.
8168           * @access protected
8169           */
8170  		protected function _textstring($s) {
8171              if ($this->isunicode) {
8172                  //Convert string to UTF-16BE
8173                  $s = $this->UTF8ToUTF16BE($s, true);
8174              }
8175              return $this->_datastring($s);
8176          }
8177  
8178          /**
8179           * Format a UTF-8 text string for meta information on annotations
8180           * @param string $s string to escape.
8181           * @return string escaped string.
8182           * @access protected
8183           */
8184  		protected function _textannobjstring($s) {
8185              if ($this->isunicode) {
8186                  //Convert string to UTF-16BE
8187                  $s = $this->UTF8ToUTF16BE($s, true);
8188              }
8189              return $this->_dataannobjstring($s);
8190          }
8191  
8192          /**
8193           * Format a text string
8194           * @param string $s string to escape.
8195           * @return string escaped string.
8196           * @access protected
8197           */
8198  		protected function _escapetext($s) {
8199              if ($this->isunicode) {
8200                  if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
8201                      $s = $this->UTF8ToLatin1($s);
8202                  } else {
8203                      //Convert string to UTF-16BE and reverse RTL language
8204                      $s = $this->utf8StrRev($s, false, $this->tmprtl);
8205                  }
8206              }
8207              return $this->_escape($s);
8208          }
8209  
8210          /**
8211           * Format output stream.
8212           * @param string $s string to output.
8213           * @param int $n object reference for encryption mode
8214           * @access protected
8215           */
8216  		protected function _getstream($s, $n=0) {
8217              if ($this->encrypted) {
8218                  if ($n <= 0) {
8219                      // default to current object
8220                      $n = $this->n;
8221                  }
8222                  $s = $this->_RC4($this->_objectkey($n), $s);
8223              }
8224              return "stream\n".$s."\nendstream";
8225          }
8226  
8227          /**
8228           * Output a stream.
8229           * @param string $s string to output.
8230           * @param int $n object reference for encryption mode
8231           * @access protected
8232           */
8233  		protected function _putstream($s, $n=0) {
8234              $this->_out($this->_getstream($s, $n));
8235          }
8236  
8237          /**
8238           * Output a string to the document.
8239           * @param string $s string to output.
8240           * @access protected
8241           */
8242  		protected function _out($s) {
8243              if ($this->state == 2) {
8244                  if ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
8245                      // puts data before page footer
8246                      $pagebuff = $this->getPageBuffer($this->page);
8247                      $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
8248                      $footer = substr($pagebuff, -$this->footerlen[$this->page]);
8249                      $this->setPageBuffer($this->page, $page.$s."\n".$footer);
8250                      // update footer position
8251                      $this->footerpos[$this->page] += strlen($s."\n");
8252                  } else {
8253                      $this->setPageBuffer($this->page, $s."\n", true);
8254                  }
8255              } else {
8256                  $this->setBuffer($s."\n");
8257              }
8258          }
8259  
8260          /**
8261           * Converts UTF-8 strings to codepoints array.<br>
8262           * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
8263           * Based on: http://www.faqs.org/rfcs/rfc3629.html
8264           * <pre>
8265           *    Char. number range  |        UTF-8 octet sequence
8266           *       (hexadecimal)    |              (binary)
8267           *    --------------------+-----------------------------------------------
8268           *    0000 0000-0000 007F | 0xxxxxxx
8269           *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
8270           *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
8271           *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
8272           *    ---------------------------------------------------------------------
8273           *
8274           *   ABFN notation:
8275           *   ---------------------------------------------------------------------
8276           *   UTF8-octets = *( UTF8-char )
8277           *   UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
8278           *   UTF8-1      = %x00-7F
8279           *   UTF8-2      = %xC2-DF UTF8-tail
8280           *
8281           *   UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
8282           *                 %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
8283           *   UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
8284           *                 %xF4 %x80-8F 2( UTF8-tail )
8285           *   UTF8-tail   = %x80-BF
8286           *   ---------------------------------------------------------------------
8287           * </pre>
8288           * @param string $str string to process.
8289           * @return array containing codepoints (UTF-8 characters values)
8290           * @access protected
8291           * @author Nicola Asuni
8292           * @since 1.53.0.TC005 (2005-01-05)
8293           */
8294  		protected function UTF8StringToArray($str) {
8295              if (isset($this->cache_UTF8StringToArray['_'.$str])) {
8296                  // return cached value
8297                  return($this->cache_UTF8StringToArray['_'.$str]);
8298              }
8299              // check cache size
8300              if ($this->cache_size_UTF8StringToArray >= $this->cache_maxsize_UTF8StringToArray) {
8301                  // remove first element
8302                  array_shift($this->cache_UTF8StringToArray);
8303              }
8304              ++$this->cache_size_UTF8StringToArray;
8305              if (!$this->isunicode) {
8306                  // split string into array of equivalent codes
8307                  $strarr = array();
8308                  $strlen = strlen($str);
8309                  for ($i=0; $i < $strlen; ++$i) {
8310                      $strarr[] = ord($str{$i});
8311                  }
8312                  // insert new value on cache
8313                  $this->cache_UTF8StringToArray['_'.$str] = $strarr;
8314                  return $strarr;
8315              }
8316              $unicode = array(); // array containing unicode values
8317              $bytes  = array(); // array containing single character byte sequences
8318              $numbytes  = 1; // number of octetc needed to represent the UTF-8 character
8319              $str .= ''; // force $str to be a string
8320              $length = strlen($str);
8321              for ($i = 0; $i < $length; ++$i) {
8322                  $char = ord($str{$i}); // get one string character at time
8323                  if (count($bytes) == 0) { // get starting octect
8324                      if ($char <= 0x7F) {
8325                          $unicode[] = $char; // use the character "as is" because is ASCII
8326                          $numbytes = 1;
8327                      } elseif (($char >> 0x05) == 0x06) { // 2 bytes character (0x06 = 110 BIN)
8328                          $bytes[] = ($char - 0xC0) << 0x06;
8329                          $numbytes = 2;
8330                      } elseif (($char >> 0x04) == 0x0E) { // 3 bytes character (0x0E = 1110 BIN)
8331                          $bytes[] = ($char - 0xE0) << 0x0C;
8332                          $numbytes = 3;
8333                      } elseif (($char >> 0x03) == 0x1E) { // 4 bytes character (0x1E = 11110 BIN)
8334                          $bytes[] = ($char - 0xF0) << 0x12;
8335                          $numbytes = 4;
8336                      } else {
8337                          // use replacement character for other invalid sequences
8338                          $unicode[] = 0xFFFD;
8339                          $bytes = array();
8340                          $numbytes = 1;
8341                      }
8342                  } elseif (($char >> 0x06) == 0x02) { // bytes 2, 3 and 4 must start with 0x02 = 10 BIN
8343                      $bytes[] = $char - 0x80;
8344                      if (count($bytes) == $numbytes) {
8345                          // compose UTF-8 bytes to a single unicode value
8346                          $char = $bytes[0];
8347                          for ($j = 1; $j < $numbytes; ++$j) {
8348                              $char += ($bytes[$j] << (($numbytes - $j - 1) * 0x06));
8349                          }
8350                          if ((($char >= 0xD800) AND ($char <= 0xDFFF)) OR ($char >= 0x10FFFF)) {
8351                              /* The definition of UTF-8 prohibits encoding character numbers between
8352                              U+D800 and U+DFFF, which are reserved for use with the UTF-16
8353                              encoding form (as surrogate pairs) and do not directly represent
8354                              characters. */
8355                              $unicode[] = 0xFFFD; // use replacement character
8356                          } else {
8357                              $unicode[] = $char; // add char to array
8358                          }
8359                          // reset data for next char
8360                          $bytes = array();
8361                          $numbytes = 1;
8362                      }
8363                  } else {
8364                      // use replacement character for other invalid sequences
8365                      $unicode[] = 0xFFFD;
8366                      $bytes = array();
8367                      $numbytes = 1;
8368                  }
8369              }
8370              // insert new value on cache
8371              $this->cache_UTF8StringToArray['_'.$str] = $unicode;
8372              return $unicode;
8373          }
8374  
8375          /**
8376           * Converts UTF-8 strings to UTF16-BE.<br>
8377           * @param string $str string to process.
8378           * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
8379           * @return string
8380           * @access protected
8381           * @author Nicola Asuni
8382           * @since 1.53.0.TC005 (2005-01-05)
8383           * @uses UTF8StringToArray(), arrUTF8ToUTF16BE()
8384           */
8385  		protected function UTF8ToUTF16BE($str, $setbom=true) {
8386              if (!$this->isunicode) {
8387                  return $str; // string is not in unicode
8388              }
8389              $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
8390              return $this->arrUTF8ToUTF16BE($unicode, $setbom);
8391          }
8392  
8393          /**
8394           * Converts UTF-8 strings to Latin1 when using the standard 14 core fonts.<br>
8395           * @param string $str string to process.
8396           * @return string
8397           * @author Andrew Whitehead, Nicola Asuni
8398           * @access protected
8399           * @since 3.2.000 (2008-06-23)
8400           */
8401  		protected function UTF8ToLatin1($str) {
8402              global $utf8tolatin;
8403              if (!$this->isunicode) {
8404                  return $str; // string is not in unicode
8405              }
8406              $outstr = ''; // string to be returned
8407              $unicode = $this->UTF8StringToArray($str); // array containing UTF-8 unicode values
8408              foreach ($unicode as $char) {
8409                  if ($char < 256) {
8410                      $outstr .= chr($char);
8411                  } elseif (array_key_exists($char, $utf8tolatin)) {
8412                      // map from UTF-8
8413                      $outstr .= chr($utf8tolatin[$char]);
8414                  } elseif ($char == 0xFFFD) {
8415                      // skip
8416                  } else {
8417                      $outstr .= '?';
8418                  }
8419              }
8420              return $outstr;
8421          }
8422  
8423          /**
8424           * Converts UTF-8 characters array to array of Latin1 characters<br>
8425           * @param array $unicode array containing UTF-8 unicode values
8426           * @return array
8427           * @author Nicola Asuni
8428           * @access protected
8429           * @since 4.8.023 (2010-01-15)
8430           */
8431  		protected function UTF8ArrToLatin1($unicode) {
8432              global $utf8tolatin;
8433              if ((!$this->isunicode) OR ($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
8434                  return $unicode; // string is not in unicode
8435              }
8436              $outarr = array(); // array to be returned
8437              foreach ($unicode as $char) {
8438                  if ($char < 256) {
8439                      $outarr[] = $char;
8440                  } elseif (array_key_exists($char, $utf8tolatin)) {
8441                      // map from UTF-8
8442                      $outarr[] = $utf8tolatin[$char];
8443                  } elseif ($char == 0xFFFD) {
8444                      // skip
8445                  } else {
8446                      $outarr[] = 63; // '?' character
8447                  }
8448              }
8449              return $outarr;
8450          }
8451  
8452          /**
8453           * Converts array of UTF-8 characters to UTF16-BE string.<br>
8454           * Based on: http://www.faqs.org/rfcs/rfc2781.html
8455            * <pre>
8456           *   Encoding UTF-16:
8457           *
8458            *   Encoding of a single character from an ISO 10646 character value to
8459           *    UTF-16 proceeds as follows. Let U be the character number, no greater
8460           *    than 0x10FFFF.
8461           *
8462           *    1) If U < 0x10000, encode U as a 16-bit unsigned integer and
8463           *       terminate.
8464           *
8465           *    2) Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
8466           *       U' must be less than or equal to 0xFFFFF. That is, U' can be
8467           *       represented in 20 bits.
8468           *
8469           *    3) Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
8470           *       0xDC00, respectively. These integers each have 10 bits free to
8471           *       encode the character value, for a total of 20 bits.
8472           *
8473           *    4) Assign the 10 high-order bits of the 20-bit U' to the 10 low-order
8474           *       bits of W1 and the 10 low-order bits of U' to the 10 low-order
8475           *       bits of W2. Terminate.
8476           *
8477           *    Graphically, steps 2 through 4 look like:
8478           *    U' = yyyyyyyyyyxxxxxxxxxx
8479           *    W1 = 110110yyyyyyyyyy
8480           *    W2 = 110111xxxxxxxxxx
8481           * </pre>
8482           * @param array $unicode array containing UTF-8 unicode values
8483           * @param boolean $setbom if true set the Byte Order Mark (BOM = 0xFEFF)
8484           * @return string
8485           * @access protected
8486           * @author Nicola Asuni
8487           * @since 2.1.000 (2008-01-08)
8488           * @see UTF8ToUTF16BE()
8489           */
8490  		protected function arrUTF8ToUTF16BE($unicode, $setbom=true) {
8491              $outstr = ''; // string to be returned
8492              if ($setbom) {
8493                  $outstr .= "\xFE\xFF"; // Byte Order Mark (BOM)
8494              }
8495              foreach ($unicode as $char) {
8496                  if ($char == 0xFFFD) {
8497                      $outstr .= "\xFF\xFD"; // replacement character
8498                  } elseif ($char < 0x10000) {
8499                      $outstr .= chr($char >> 0x08);
8500                      $outstr .= chr($char & 0xFF);
8501                  } else {
8502                      $char -= 0x10000;
8503                      $w1 = 0xD800 | ($char >> 0x10);
8504                      $w2 = 0xDC00 | ($char & 0x3FF);
8505                      $outstr .= chr($w1 >> 0x08);
8506                      $outstr .= chr($w1 & 0xFF);
8507                      $outstr .= chr($w2 >> 0x08);
8508                      $outstr .= chr($w2 & 0xFF);
8509                  }
8510              }
8511              return $outstr;
8512          }
8513          // ====================================================
8514  
8515          /**
8516            * Set header font.
8517           * @param array $font font
8518           * @access public
8519           * @since 1.1
8520           */
8521  		public function setHeaderFont($font) {
8522              $this->header_font = $font;
8523          }
8524  
8525          /**
8526            * Get header font.
8527            * @return array()
8528           * @access public
8529           * @since 4.0.012 (2008-07-24)
8530           */
8531  		public function getHeaderFont() {
8532              return $this->header_font;
8533          }
8534  
8535          /**
8536            * Set footer font.
8537           * @param array $font font
8538           * @access public
8539           * @since 1.1
8540           */
8541  		public function setFooterFont($font) {
8542              $this->footer_font = $font;
8543          }
8544  
8545          /**
8546            * Get Footer font.
8547            * @return array()
8548           * @access public
8549           * @since 4.0.012 (2008-07-24)
8550           */
8551  		public function getFooterFont() {
8552              return $this->footer_font;
8553          }
8554  
8555          /**
8556            * Set language array.
8557           * @param array $language
8558           * @access public
8559           * @since 1.1
8560           */
8561  		public function setLanguageArray($language) {
8562              $this->l = $language;
8563              if (isset($this->l['a_meta_dir'])) {
8564                  $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
8565              } else {
8566                  $this->rtl = false;
8567              }
8568          }
8569  
8570          /**
8571           * Returns the PDF data.
8572           * @access public
8573           */
8574  		public function getPDFData() {
8575              if ($this->state < 3) {
8576                  $this->Close();
8577              }
8578              return $this->buffer;
8579          }
8580  
8581          /**
8582           * Output anchor link.
8583           * @param string $url link URL or internal link (i.e.: &lt;a href="#23"&gt;link to page 23&lt;/a&gt;)
8584           * @param string $name link name
8585           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
8586           * @param boolean $firstline if true prints only the first line and return the remaining string.
8587           * @param array $color array of RGB text color
8588           * @param string $style font style (U, D, B, I)
8589           * @param boolean $firstblock if true the string is the starting of a line.
8590           * @return the number of cells used or the remaining text if $firstline = true;
8591           * @access public
8592           */
8593  		public function addHtmlLink($url, $name, $fill=0, $firstline=false, $color='', $style=-1, $firstblock=false) {
8594              if (!$this->empty_string($url) AND ($url{0} == '#')) {
8595                  // convert url to internal link
8596                  $page = intval(substr($url, 1));
8597                  $url = $this->AddLink();
8598                  $this->SetLink($url, 0, $page);
8599              }
8600              // store current settings
8601              $prevcolor = $this->fgcolor;
8602              $prevstyle = $this->FontStyle;
8603              if (empty($color)) {
8604                  $this->SetTextColorArray($this->htmlLinkColorArray);
8605              } else {
8606                  $this->SetTextColorArray($color);
8607              }
8608              if ($style == -1) {
8609                  $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
8610              } else {
8611                  $this->SetFont('', $this->FontStyle.$style);
8612              }
8613              $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
8614              // restore settings
8615              $this->SetFont('', $prevstyle);
8616              $this->SetTextColorArray($prevcolor);
8617              return $ret;
8618          }
8619  
8620          /**
8621           * 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).
8622           * @param string $color html color
8623           * @return array RGB color or false in case of error.
8624           * @access public
8625           */
8626  		public function convertHTMLColorToDec($color='#FFFFFF') {
8627              global $webcolor;
8628              $returncolor = false;
8629              $color = preg_replace('/[\s]*/', '', $color); // remove extra spaces
8630              $color = strtolower($color);
8631              if (($dotpos = strpos($color, '.')) !== false) {
8632                  // remove class parent (i.e.: color.red)
8633                  $color = substr($color, ($dotpos + 1));
8634              }
8635              if (strlen($color) == 0) {
8636                  return false;
8637              }
8638              if (substr($color, 0, 3) == 'rgb') {
8639                  $codes = substr($color, 4);
8640                  $codes = str_replace(')', '', $codes);
8641                  $returncolor = explode(',', $codes, 3);
8642                  return $returncolor;
8643              }
8644              if (substr($color, 0, 1) != '#') {
8645                  // decode color name
8646                  if (isset($webcolor[$color])) {
8647                      $color_code = $webcolor[$color];
8648                  } else {
8649                      return false;
8650                  }
8651              } else {
8652                  $color_code = substr($color, 1);
8653              }
8654              switch (strlen($color_code)) {
8655                  case 3: {
8656                      // three-digit hexadecimal representation
8657                      $r = substr($color_code, 0, 1);
8658                      $g = substr($color_code, 1, 1);
8659                      $b = substr($color_code, 2, 1);
8660                      $returncolor['R'] = hexdec($r.$r);
8661                      $returncolor['G'] = hexdec($g.$g);
8662                      $returncolor['B'] = hexdec($b.$b);
8663                      break;
8664                  }
8665                  case 6: {
8666                      // six-digit hexadecimal representation
8667                      $returncolor['R'] = hexdec(substr($color_code, 0, 2));
8668                      $returncolor['G'] = hexdec(substr($color_code, 2, 2));
8669                      $returncolor['B'] = hexdec(substr($color_code, 4, 2));
8670                      break;
8671                  }
8672              }
8673              return $returncolor;
8674          }
8675  
8676          /**
8677           * Converts pixels to User's Units.
8678           * @param int $px pixels
8679           * @return float value in user's unit
8680           * @access public
8681           * @see setImageScale(), getImageScale()
8682           */
8683  		public function pixelsToUnits($px) {
8684              return ($px / ($this->imgscale * $this->k));
8685          }
8686  
8687          /**
8688           * Reverse function for htmlentities.
8689           * Convert entities in UTF-8.
8690           * @param $text_to_convert Text to convert.
8691           * @return string converted
8692           * @access public
8693           */
8694  		public function unhtmlentities($text_to_convert) {
8695              return html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
8696          }
8697  
8698          // ENCRYPTION METHODS ----------------------------------
8699          // SINCE 2.0.000 (2008-01-02)
8700  
8701          /**
8702           * Compute encryption key depending on object number where the encrypted data is stored
8703           * @param int $n object number
8704           * @access protected
8705           * @since 2.0.000 (2008-01-02)
8706           */
8707  		protected function _objectkey($n) {
8708              return substr($this->_md5_16($this->encryption_key.pack('VXxx', $n)), 0, 10);
8709          }
8710  
8711          /**
8712           * Put encryption on PDF document.
8713           * @access protected
8714           * @since 2.0.000 (2008-01-02)
8715           */
8716  		protected function _putencryption() {
8717              if (!$this->encrypted) {
8718                  return;
8719              }
8720              $this->_newobj();
8721              $this->enc_obj_id = $this->n;
8722              $out = '<< /Filter /Standard /V 1 /R 2';
8723              $out .= ' /O ('.$this->_escape($this->Ovalue).')';
8724              $out .= ' /U ('.$this->_escape($this->Uvalue).')';
8725              $out .= ' /P '.$this->Pvalue;
8726              $out .= ' >> endobj';
8727              $this->_out($out);
8728          }
8729  
8730          /**
8731           * Returns the input text exrypted using RC4 algorithm and the specified key.
8732           * RC4 is the standard encryption algorithm used in PDF format
8733           * @param string $key encryption key
8734           * @param String $text input text to be encrypted
8735           * @return String encrypted text
8736           * @access protected
8737           * @since 2.0.000 (2008-01-02)
8738           * @author Klemen Vodopivec
8739           */
8740  		protected function _RC4($key, $text) {
8741              if ($this->last_rc4_key != $key) {
8742                  $k = str_repeat($key, ((256 / strlen($key)) + 1));
8743                  $rc4 = range(0, 255);
8744                  $j = 0;
8745                  for ($i = 0; $i < 256; ++$i) {
8746                      $t = $rc4[$i];
8747                      $j = ($j + $t + ord($k{$i})) % 256;
8748                      $rc4[$i] = $rc4[$j];
8749                      $rc4[$j] = $t;
8750                  }
8751                  $this->last_rc4_key = $key;
8752                  $this->last_rc4_key_c = $rc4;
8753              } else {
8754                  $rc4 = $this->last_rc4_key_c;
8755              }
8756              $len = strlen($text);
8757              $a = 0;
8758              $b = 0;
8759              $out = '';
8760              for ($i = 0; $i < $len; ++$i) {
8761                  $a = ($a + 1) % 256;
8762                  $t = $rc4[$a];
8763                  $b = ($b + $t) % 256;
8764                  $rc4[$a] = $rc4[$b];
8765                  $rc4[$b] = $t;
8766                  $k = $rc4[($rc4[$a] + $rc4[$b]) % 256];
8767                  $out .= chr(ord($text{$i}) ^ $k);
8768              }
8769              return $out;
8770          }
8771  
8772          /**
8773           * Encrypts a string using MD5 and returns it's value as a binary string.
8774           * @param string $str input string
8775           * @return String MD5 encrypted binary string
8776           * @access protected
8777           * @since 2.0.000 (2008-01-02)
8778           * @author Klemen Vodopivec
8779           */
8780  		protected function _md5_16($str) {
8781              return pack('H*', md5($str));
8782          }
8783  
8784          /**
8785           * Compute O value (used for RC4 encryption)
8786           * @param String $user_pass user password
8787           * @param String $owner_pass user password
8788           * @return String O value
8789           * @access protected
8790           * @since 2.0.000 (2008-01-02)
8791           * @author Klemen Vodopivec
8792           */
8793  		protected function _Ovalue($user_pass, $owner_pass) {
8794              $tmp = $this->_md5_16($owner_pass);
8795              $owner_RC4_key = substr($tmp, 0, 5);
8796              return $this->_RC4($owner_RC4_key, $user_pass);
8797          }
8798  
8799          /**
8800           * Compute U value (used for RC4 encryption)
8801           * @return String U value
8802           * @access protected
8803           * @since 2.0.000 (2008-01-02)
8804           * @author Klemen Vodopivec
8805           */
8806  		protected function _Uvalue() {
8807              return $this->_RC4($this->encryption_key, $this->padding);
8808          }
8809  
8810          /**
8811           * Compute encryption key
8812           * @param String $user_pass user password
8813           * @param String $owner_pass user password
8814           * @param String $protection protection type
8815           * @access protected
8816           * @since 2.0.000 (2008-01-02)
8817           * @author Klemen Vodopivec
8818           */
8819  		protected function _generateencryptionkey($user_pass, $owner_pass, $protection) {
8820              // Pad passwords
8821              $user_pass = substr($user_pass.$this->padding, 0, 32);
8822              $owner_pass = substr($owner_pass.$this->padding, 0, 32);
8823              // Compute O value
8824              $this->Ovalue = $this->_Ovalue($user_pass, $owner_pass);
8825              // Compute encyption key
8826              $tmp = $this->_md5_16($user_pass.$this->Ovalue.chr($protection)."\xFF\xFF\xFF");
8827              $this->encryption_key = substr($tmp, 0, 5);
8828              // Compute U value
8829              $this->Uvalue = $this->_Uvalue();
8830              // Compute P value
8831              $this->Pvalue = -(($protection^255) + 1);
8832          }
8833  
8834          /**
8835           * Set document protection
8836           * The permission array is composed of values taken from the following ones:
8837           * - copy: copy text and images to the clipboard
8838           * - print: print the document
8839           * - modify: modify it (except for annotations and forms)
8840           * - annot-forms: add annotations and forms
8841           * Remark: the protection against modification is for people who have the full Acrobat product.
8842           * 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.
8843           * 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.
8844           * @param Array $permissions the set of permissions. Empty by default (only viewing is allowed). (print, modify, copy, annot-forms)
8845           * @param String $user_pass user password. Empty by default.
8846           * @param String $owner_pass owner password. If not specified, a random value is used.
8847           * @access public
8848           * @since 2.0.000 (2008-01-02)
8849           * @author Klemen Vodopivec
8850           */
8851  		public function SetProtection($permissions=array(), $user_pass='', $owner_pass=null) {
8852              $options = array('print' => 4, 'modify' => 8, 'copy' => 16, 'annot-forms' => 32);
8853              $protection = 192;
8854              foreach ($permissions as $permission) {
8855                  if (!isset($options[$permission])) {
8856                      $this->Error('Incorrect permission: '.$permission);
8857                  }
8858                  $protection += $options[$permission];
8859              }
8860              if ($owner_pass === null) {
8861                  $owner_pass = uniqid(rand());
8862              }
8863              $this->encrypted = true;
8864              $this->_generateencryptionkey($user_pass, $owner_pass, $protection);
8865          }
8866  
8867          // END OF ENCRYPTION FUNCTIONS -------------------------
8868  
8869          // START TRANSFORMATIONS SECTION -----------------------
8870  
8871          /**
8872           * Starts a 2D tranformation saving current graphic state.
8873           * This function must be called before scaling, mirroring, translation, rotation and skewing.
8874           * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
8875           * @access public
8876           * @since 2.1.000 (2008-01-07)
8877           * @see StartTransform(), StopTransform()
8878           */
8879  		public function StartTransform() {
8880              $this->_out('q');
8881              $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
8882              ++$this->transfmatrix_key;
8883              $this->transfmatrix[$this->transfmatrix_key] = array();
8884          }
8885  
8886          /**
8887           * Stops a 2D tranformation restoring previous graphic state.
8888           * This function must be called after scaling, mirroring, translation, rotation and skewing.
8889           * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
8890           * @access public
8891           * @since 2.1.000 (2008-01-07)
8892           * @see StartTransform(), StopTransform()
8893           */
8894  		public function StopTransform() {
8895              $this->_out('Q');
8896              if (isset($this->transfmatrix[$this->transfmatrix_key])) {
8897                  array_pop($this->transfmatrix[$this->transfmatrix_key]);
8898                  --$this->transfmatrix_key;
8899              }
8900              array_pop($this->transfmrk[$this->page]);
8901          }
8902          /**
8903           * Horizontal Scaling.
8904           * @param float $s_x scaling factor for width as percent. 0 is not allowed.
8905           * @param int $x abscissa of the scaling center. Default is current x position
8906           * @param int $y ordinate of the scaling center. Default is current y position
8907           * @access public
8908           * @since 2.1.000 (2008-01-07)
8909           * @see StartTransform(), StopTransform()
8910           */
8911  		public function ScaleX($s_x, $x='', $y='') {
8912              $this->Scale($s_x, 100, $x, $y);
8913          }
8914  
8915          /**
8916           * Vertical Scaling.
8917           * @param float $s_y scaling factor for height as percent. 0 is not allowed.
8918           * @param int $x abscissa of the scaling center. Default is current x position
8919           * @param int $y ordinate of the scaling center. Default is current y position
8920           * @access public
8921           * @since 2.1.000 (2008-01-07)
8922           * @see StartTransform(), StopTransform()
8923           */
8924  		public function ScaleY($s_y, $x='', $y='') {
8925              $this->Scale(100, $s_y, $x, $y);
8926          }
8927  
8928          /**
8929           * Vertical and horizontal proportional Scaling.
8930           * @param float $s scaling factor for width and height as percent. 0 is not allowed.
8931           * @param int $x abscissa of the scaling center. Default is current x position
8932           * @param int $y ordinate of the scaling center. Default is current y position
8933           * @access public
8934           * @since 2.1.000 (2008-01-07)
8935           * @see StartTransform(), StopTransform()
8936           */
8937  		public function ScaleXY($s, $x='', $y='') {
8938              $this->Scale($s, $s, $x, $y);
8939          }
8940  
8941          /**
8942           * Vertical and horizontal non-proportional Scaling.
8943           * @param float $s_x scaling factor for width as percent. 0 is not allowed.
8944           * @param float $s_y scaling factor for height as percent. 0 is not allowed.
8945           * @param int $x abscissa of the scaling center. Default is current x position
8946           * @param int $y ordinate of the scaling center. Default is current y position
8947           * @access public
8948           * @since 2.1.000 (2008-01-07)
8949           * @see StartTransform(), StopTransform()
8950           */
8951  		public function Scale($s_x, $s_y, $x='', $y='') {
8952              if ($x === '') {
8953                  $x = $this->x;
8954              }
8955              if ($y === '') {
8956                  $y = $this->y;
8957              }
8958              if (($s_x == 0) OR ($s_y == 0)) {
8959                  $this->Error('Please do not use values equal to zero for scaling');
8960              }
8961              $y = ($this->h - $y) * $this->k;
8962              $x *= $this->k;
8963              //calculate elements of transformation matrix
8964              $s_x /= 100;
8965              $s_y /= 100;
8966              $tm[0] = $s_x;
8967              $tm[1] = 0;
8968              $tm[2] = 0;
8969              $tm[3] = $s_y;
8970              $tm[4] = $x * (1 - $s_x);
8971              $tm[5] = $y * (1 - $s_y);
8972              //scale the coordinate system
8973              $this->Transform($tm);
8974          }
8975  
8976          /**
8977           * Horizontal Mirroring.
8978           * @param int $x abscissa of the point. Default is current x position
8979           * @access public
8980           * @since 2.1.000 (2008-01-07)
8981           * @see StartTransform(), StopTransform()
8982           */
8983  		public function MirrorH($x='') {
8984              $this->Scale(-100, 100, $x);
8985          }
8986  
8987          /**
8988           * Verical Mirroring.
8989           * @param int $y ordinate of the point. Default is current y position
8990           * @access public
8991           * @since 2.1.000 (2008-01-07)
8992           * @see StartTransform(), StopTransform()
8993           */
8994  		public function MirrorV($y='') {
8995              $this->Scale(100, -100, '', $y);
8996          }
8997  
8998          /**
8999           * Point reflection mirroring.
9000           * @param int $x abscissa of the point. Default is current x position
9001           * @param int $y ordinate of the point. Default is current y position
9002           * @access public
9003           * @since 2.1.000 (2008-01-07)
9004           * @see StartTransform(), StopTransform()
9005           */
9006  		public function MirrorP($x='',$y='') {
9007              $this->Scale(-100, -100, $x, $y);
9008          }
9009  
9010          /**
9011           * Reflection against a straight line through point (x, y) with the gradient angle (angle).
9012           * @param float $angle gradient angle of the straight line. Default is 0 (horizontal line).
9013           * @param int $x abscissa of the point. Default is current x position
9014           * @param int $y ordinate of the point. Default is current y position
9015           * @access public
9016           * @since 2.1.000 (2008-01-07)
9017           * @see StartTransform(), StopTransform()
9018           */
9019  		public function MirrorL($angle=0, $x='',$y='') {
9020              $this->Scale(-100, 100, $x, $y);
9021              $this->Rotate(-2*($angle-90), $x, $y);
9022          }
9023  
9024          /**
9025           * Translate graphic object horizontally.
9026           * @param int $t_x movement to the right (or left for RTL)
9027           * @access public
9028           * @since 2.1.000 (2008-01-07)
9029           * @see StartTransform(), StopTransform()
9030           */
9031  		public function TranslateX($t_x) {
9032              $this->Translate($t_x, 0);
9033          }
9034  
9035          /**
9036           * Translate graphic object vertically.
9037           * @param int $t_y movement to the bottom
9038           * @access public
9039           * @since 2.1.000 (2008-01-07)
9040           * @see StartTransform(), StopTransform()
9041           */
9042  		public function TranslateY($t_y) {
9043              $this->Translate(0, $t_y);
9044          }
9045  
9046          /**
9047           * Translate graphic object horizontally and vertically.
9048           * @param int $t_x movement to the right
9049           * @param int $t_y movement to the bottom
9050           * @access public
9051           * @since 2.1.000 (2008-01-07)
9052           * @see StartTransform(), StopTransform()
9053           */
9054  		public function Translate($t_x, $t_y) {
9055              //calculate elements of transformation matrix
9056              $tm[0] = 1;
9057              $tm[1] = 0;
9058              $tm[2] = 0;
9059              $tm[3] = 1;
9060              $tm[4] = $t_x * $this->k;
9061              $tm[5] = -$t_y * $this->k;
9062              //translate the coordinate system
9063              $this->Transform($tm);
9064          }
9065  
9066          /**
9067           * Rotate object.
9068           * @param float $angle angle in degrees for counter-clockwise rotation
9069           * @param int $x abscissa of the rotation center. Default is current x position
9070           * @param int $y ordinate of the rotation center. Default is current y position
9071           * @access public
9072           * @since 2.1.000 (2008-01-07)
9073           * @see StartTransform(), StopTransform()
9074           */
9075  		public function Rotate($angle, $x='', $y='') {
9076              if ($x === '') {
9077                  $x = $this->x;
9078              }
9079              if ($y === '') {
9080                  $y = $this->y;
9081              }
9082              $y = ($this->h - $y) * $this->k;
9083              $x *= $this->k;
9084              //calculate elements of transformation matrix
9085              $tm[0] = cos(deg2rad($angle));
9086              $tm[1] = sin(deg2rad($angle));
9087              $tm[2] = -$tm[1];
9088              $tm[3] = $tm[0];
9089              $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
9090              $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
9091              //rotate the coordinate system around ($x,$y)
9092              $this->Transform($tm);
9093          }
9094  
9095          /**
9096           * Skew horizontally.
9097           * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
9098           * @param int $x abscissa of the skewing center. default is current x position
9099           * @param int $y ordinate of the skewing center. default is current y position
9100           * @access public
9101           * @since 2.1.000 (2008-01-07)
9102           * @see StartTransform(), StopTransform()
9103           */
9104  		public function SkewX($angle_x, $x='', $y='') {
9105              $this->Skew($angle_x, 0, $x, $y);
9106          }
9107  
9108          /**
9109           * Skew vertically.
9110           * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
9111           * @param int $x abscissa of the skewing center. default is current x position
9112           * @param int $y ordinate of the skewing center. default is current y position
9113           * @access public
9114           * @since 2.1.000 (2008-01-07)
9115           * @see StartTransform(), StopTransform()
9116           */
9117  		public function SkewY($angle_y, $x='', $y='') {
9118              $this->Skew(0, $angle_y, $x, $y);
9119          }
9120  
9121          /**
9122           * Skew.
9123           * @param float $angle_x angle in degrees between -90 (skew to the left) and 90 (skew to the right)
9124           * @param float $angle_y angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
9125           * @param int $x abscissa of the skewing center. default is current x position
9126           * @param int $y ordinate of the skewing center. default is current y position
9127           * @access public
9128           * @since 2.1.000 (2008-01-07)
9129           * @see StartTransform(), StopTransform()
9130           */
9131  		public function Skew($angle_x, $angle_y, $x='', $y='') {
9132              if ($x === '') {
9133                  $x = $this->x;
9134              }
9135              if ($y === '') {
9136                  $y = $this->y;
9137              }
9138              if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
9139                  $this->Error('Please use values between -90 and +90 degrees for Skewing.');
9140              }
9141              $x *= $this->k;
9142              $y = ($this->h - $y) * $this->k;
9143              //calculate elements of transformation matrix
9144              $tm[0] = 1;
9145              $tm[1] = tan(deg2rad($angle_y));
9146              $tm[2] = tan(deg2rad($angle_x));
9147              $tm[3] = 1;
9148              $tm[4] = -$tm[2] * $y;
9149              $tm[5] = -$tm[1] * $x;
9150              //skew the coordinate system
9151              $this->Transform($tm);
9152          }
9153  
9154          /**
9155           * Apply graphic transformations.
9156           * @param array $tm transformation matrix
9157           * @access protected
9158           * @since 2.1.000 (2008-01-07)
9159           * @see StartTransform(), StopTransform()
9160           */
9161  		protected function Transform($tm) {
9162              $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
9163              // add tranformation matrix
9164              $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
9165              // update tranformation mark
9166              if (end($this->transfmrk[$this->page]) !== false) {
9167                  $key = key($this->transfmrk[$this->page]);
9168                  $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
9169              }
9170          }
9171  
9172          // END TRANSFORMATIONS SECTION -------------------------
9173  
9174  
9175          // START GRAPHIC FUNCTIONS SECTION ---------------------
9176          // The following section is based on the code provided by David Hernandez Sanz
9177  
9178          /**
9179           * 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.
9180           * @param float $width The width.
9181           * @access public
9182           * @since 1.0
9183           * @see Line(), Rect(), Cell(), MultiCell()
9184           */
9185  		public function SetLineWidth($width) {
9186              //Set line width
9187              $this->LineWidth = $width;
9188              $this->linestyleWidth = sprintf('%.2F w', ($width * $this->k));
9189              if ($this->page > 0) {
9190                  $this->_out($this->linestyleWidth);
9191              }
9192          }
9193  
9194          /**
9195           * Returns the current the line width.
9196           * @return int Line width
9197           * @access public
9198           * @since 2.1.000 (2008-01-07)
9199           * @see Line(), SetLineWidth()
9200           */
9201  		public function GetLineWidth() {
9202              return $this->LineWidth;
9203          }
9204  
9205          /**
9206           * Set line style.
9207           * @param array $style Line style. Array with keys among the following:
9208           * <ul>
9209           *     <li>width (float): Width of the line in user units.</li>
9210           *     <li>cap (string): Type of cap to put on the line. Possible values are:
9211           * butt, round, square. The difference between "square" and "butt" is that
9212           * "square" projects a flat end past the end of the line.</li>
9213           *     <li>join (string): Type of join. Possible values are: miter, round,
9214           * bevel.</li>
9215           *     <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
9216           * series of length values, which are the lengths of the on and off dashes.
9217           * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
9218           * 1 off, 2 on, 1 off, ...</li>
9219           *     <li>phase (integer): Modifier on the dash pattern which is used to shift
9220           * the point at which the pattern starts.</li>
9221           *     <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K).</li>
9222           * </ul>
9223           * @access public
9224           * @since 2.1.000 (2008-01-08)
9225           */
9226  		public function SetLineStyle($style) {
9227              if (!is_array($style)) {
9228                  return;
9229              }
9230              extract($style);
9231              if (isset($width)) {
9232                  $width_prev = $this->LineWidth;
9233                  $this->SetLineWidth($width);
9234                  $this->LineWidth = $width_prev;
9235              }
9236              if (isset($cap)) {
9237                  $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
9238                  if (isset($ca[$cap])) {
9239                      $this->linestyleCap = $ca[$cap].' J';
9240                      $this->_out($this->linestyleCap);
9241                  }
9242              }
9243              if (isset($join)) {
9244                  $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
9245                  if (isset($ja[$join])) {
9246                      $this->linestyleJoin = $ja[$join].' j';
9247                      $this->_out($this->linestyleJoin);
9248                  }
9249              }
9250              if (isset($dash)) {
9251                  $dash_string = '';
9252                  if ($dash) {
9253                      if (preg_match('/^.+,/', $dash) > 0) {
9254                          $tab = explode(',', $dash);
9255                      } else {
9256                          $tab = array($dash);
9257                      }
9258                      $dash_string = '';
9259                      foreach ($tab as $i => $v) {
9260                          if ($i) {
9261                              $dash_string .= ' ';
9262                          }
9263                          $dash_string .= sprintf('%.2F', $v);
9264                      }
9265                  }
9266                  if (!isset($phase) OR !$dash) {
9267                      $phase = 0;
9268                  }
9269                  $this->linestyleDash = sprintf('[%s] %.2F d', $dash_string, $phase);
9270                  $this->_out($this->linestyleDash);
9271              }
9272              if (isset($color)) {
9273                  $this->SetDrawColorArray($color);
9274              }
9275          }
9276  
9277          /**
9278           * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
9279           * @param float $x Abscissa of point.
9280           * @param float $y Ordinate of point.
9281           * @access protected
9282           * @since 2.1.000 (2008-01-08)
9283           */
9284  		protected function _outPoint($x, $y) {
9285              $this->_out(sprintf('%.2F %.2F m', $x * $this->k, ($this->h - $y) * $this->k));
9286          }
9287  
9288          /**
9289           * Append a straight line segment from the current point to the point (x, y).
9290           * The new current point shall be (x, y).
9291           * @param float $x Abscissa of end point.
9292           * @param float $y Ordinate of end point.
9293           * @access protected
9294           * @since 2.1.000 (2008-01-08)
9295           */
9296  		protected function _outLine($x, $y) {
9297              $this->_out(sprintf('%.2F %.2F l', $x * $this->k, ($this->h - $y) * $this->k));
9298          }
9299  
9300          /**
9301           * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
9302           * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
9303           * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
9304           * @param float $w Width.
9305           * @param float $h Height.
9306           * @param string $op options
9307           * @access protected
9308           * @since 2.1.000 (2008-01-08)
9309           */
9310  		protected function _outRect($x, $y, $w, $h, $op) {
9311              $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
9312          }
9313  
9314          /**
9315           * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x2, y2) as the Bézier control points.
9316           * The new current point shall be (x3, y3).
9317           * @param float $x1 Abscissa of control point 1.
9318           * @param float $y1 Ordinate of control point 1.
9319           * @param float $x2 Abscissa of control point 2.
9320           * @param float $y2 Ordinate of control point 2.
9321           * @param float $x3 Abscissa of end point.
9322           * @param float $y3 Ordinate of end point.
9323           * @access protected
9324           * @since 2.1.000 (2008-01-08)
9325           */
9326  		protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
9327              $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));
9328          }
9329  
9330          /**
9331           * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using the current point and (x2, y2) as the Bézier control points.
9332           * The new current point shall be (x3, y3).
9333           * @param float $x2 Abscissa of control point 2.
9334           * @param float $y2 Ordinate of control point 2.
9335           * @param float $x3 Abscissa of end point.
9336           * @param float $y3 Ordinate of end point.
9337           * @access protected
9338           * @since 4.9.019 (2010-04-26)
9339           */
9340  		protected function _outCurveV($x2, $y2, $x3, $y3) {
9341              $this->_out(sprintf('%.2F %.2F %.2F %.2F v', $x2 * $this->k, ($this->h - $y2) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
9342          }
9343  
9344          /**
9345           * Append a cubic Bézier curve to the current path. The curve shall extend from the current point to the point (x3, y3), using (x1, y1) and (x3, y3) as the Bézier control points.
9346           * The new current point shall be (x3, y3).
9347           * @param float $x1 Abscissa of control point 1.
9348           * @param float $y1 Ordinate of control point 1.
9349           * @param float $x2 Abscissa of control point 2.
9350           * @param float $y2 Ordinate of control point 2.
9351           * @param float $x3 Abscissa of end point.
9352           * @param float $y3 Ordinate of end point.
9353           * @access protected
9354           * @since 2.1.000 (2008-01-08)
9355           */
9356  		protected function _outCurveY($x1, $y1, $x3, $y3) {
9357              $this->_out(sprintf('%.2F %.2F %.2F %.2F y', $x1 * $this->k, ($this->h - $y1) * $this->k, $x3 * $this->k, ($this->h - $y3) * $this->k));
9358          }
9359  
9360          /**
9361           * Draws a line between two points.
9362           * @param float $x1 Abscissa of first point.
9363           * @param float $y1 Ordinate of first point.
9364           * @param float $x2 Abscissa of second point.
9365           * @param float $y2 Ordinate of second point.
9366           * @param array $style Line style. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9367           * @access public
9368           * @since 1.0
9369           * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
9370           */
9371  		public function Line($x1, $y1, $x2, $y2, $style=array()) {
9372              if (is_array($style)) {
9373                  $this->SetLineStyle($style);
9374              }
9375              $this->_outPoint($x1, $y1);
9376              $this->_outLine($x2, $y2);
9377              $this->_out('S');
9378          }
9379  
9380          /**
9381           * Draws a rectangle.
9382           * @param float $x Abscissa of upper-left corner (or upper-right corner for RTL language).
9383           * @param float $y Ordinate of upper-left corner (or upper-right corner for RTL language).
9384           * @param float $w Width.
9385           * @param float $h Height.
9386           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9387           * @param array $border_style Border style of rectangle. Array with keys among the following:
9388           * <ul>
9389           *     <li>all: Line style of all borders. Array like for {@link SetLineStyle SetLineStyle}.</li>
9390           *     <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for {@link SetLineStyle SetLineStyle}.</li>
9391           * </ul>
9392           * If a key is not present or is null, not draws the border. Default value: default line style (empty array).
9393           * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9394           * @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).
9395           * @access public
9396           * @since 1.0
9397           * @see SetLineStyle()
9398           */
9399  		public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
9400              if (!(false === strpos($style, 'F')) AND !empty($fill_color)) {
9401                  $this->SetFillColorArray($fill_color);
9402              }
9403              $op = $this->getPathPaintOperator($style);
9404              if ((!$border_style) OR (isset($border_style['all']))) {
9405                  if (isset($border_style['all']) AND $border_style['all']) {
9406                      $this->SetLineStyle($border_style['all']);
9407                      $border_style = array();
9408                  }
9409              }
9410              $this->_outRect($x, $y, $w, $h, $op);
9411  
9412              if ($border_style) {
9413                  $border_style2 = array();
9414                  foreach ($border_style as $line => $value) {
9415                      $length = strlen($line);
9416                      for ($i = 0; $i < $length; ++$i) {
9417                          $border_style2[$line[$i]] = $value;
9418                      }
9419                  }
9420                  $border_style = $border_style2;
9421                  if (isset($border_style['L']) AND $border_style['L']) {
9422                      $this->Line($x, $y, $x, $y + $h, $border_style['L']);
9423                  }
9424                  if (isset($border_style['T']) AND $border_style['T']) {
9425                      $this->Line($x, $y, $x + $w, $y, $border_style['T']);
9426                  }
9427                  if (isset($border_style['R']) AND $border_style['R']) {
9428                      $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
9429                  }
9430                  if (isset($border_style['B']) AND $border_style['B']) {
9431                      $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
9432                  }
9433              }
9434          }
9435  
9436          /**
9437           * Draws a Bezier curve.
9438           * The Bezier curve is a tangent to the line between the control points at
9439           * either end of the curve.
9440           * @param float $x0 Abscissa of start point.
9441           * @param float $y0 Ordinate of start point.
9442           * @param float $x1 Abscissa of control point 1.
9443           * @param float $y1 Ordinate of control point 1.
9444           * @param float $x2 Abscissa of control point 2.
9445           * @param float $y2 Ordinate of control point 2.
9446           * @param float $x3 Abscissa of end point.
9447           * @param float $y3 Ordinate of end point.
9448           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9449           * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9450           * @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).
9451           * @access public
9452           * @see SetLineStyle()
9453           * @since 2.1.000 (2008-01-08)
9454           */
9455  		public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
9456              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
9457                  $this->SetFillColorArray($fill_color);
9458              }
9459              $op = $this->getPathPaintOperator($style);
9460              if ($line_style) {
9461                  $this->SetLineStyle($line_style);
9462              }
9463              $this->_outPoint($x0, $y0);
9464              $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
9465              $this->_out($op);
9466          }
9467  
9468          /**
9469           * Draws a poly-Bezier curve.
9470           * Each Bezier curve segment is a tangent to the line between the control points at
9471           * either end of the curve.
9472           * @param float $x0 Abscissa of start point.
9473           * @param float $y0 Ordinate of start point.
9474           * @param float $segments An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
9475           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9476           * @param array $line_style Line style of curve. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9477           * @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).
9478           * @access public
9479           * @see SetLineStyle()
9480           * @since 3.0008 (2008-05-12)
9481           */
9482  		public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
9483              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
9484                  $this->SetFillColorArray($fill_color);
9485              }
9486              $op = $this->getPathPaintOperator($style);
9487              if ($op == 'f') {
9488                  $line_style = array();
9489              }
9490              if ($line_style) {
9491                  $this->SetLineStyle($line_style);
9492              }
9493              $this->_outPoint($x0, $y0);
9494              foreach ($segments as $segment) {
9495                  list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
9496                  $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
9497              }
9498              $this->_out($op);
9499          }
9500  
9501          /**
9502           * Draws an ellipse.
9503           * An ellipse is formed from n Bezier curves.
9504           * @param float $x0 Abscissa of center point.
9505           * @param float $y0 Ordinate of center point.
9506           * @param float $rx Horizontal radius.
9507           * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
9508           * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
9509           * @param float $astart: Angle start of draw line. Default value: 0.
9510           * @param float $afinish: Angle finish of draw line. Default value: 360.
9511           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9512           * @param array $line_style Line style of ellipse. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9513           * @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).
9514           * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse.
9515           * @author Nicola Asuni
9516           * @access public
9517           * @since 2.1.000 (2008-01-08)
9518           */
9519  		public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
9520              if ($this->empty_string($ry) OR ($ry == 0)) {
9521                  $ry = $rx;
9522              }
9523              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
9524                  $this->SetFillColorArray($fill_color);
9525              }
9526              $op = $this->getPathPaintOperator($style);
9527              if ($op == 'f') {
9528                  $line_style = array();
9529              }
9530              if ($line_style) {
9531                  $this->SetLineStyle($line_style);
9532              }
9533              $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc);
9534              $this->_out($op);
9535          }
9536  
9537          /**
9538           * Append an elliptical arc to the current path.
9539           * An ellipse is formed from n Bezier curves.
9540           * @param float $xc Abscissa of center point.
9541           * @param float $yc Ordinate of center point.
9542           * @param float $rx Horizontal radius.
9543           * @param float $ry Vertical radius (if ry = 0 then is a circle, see {@link Circle Circle}). Default value: 0.
9544           * @param float $xang: Angle between the X-axis and the major axis of the ellipse. Default value: 0.
9545           * @param float $angs: Angle start of draw line. Default value: 0.
9546           * @param float $angf: Angle finish of draw line. Default value: 360.
9547           * @param boolean $pie if true do not mark the border point (used to draw pie sectors).
9548           * @param integer $nc Number of curves used to draw a 90 degrees portion of ellipse.
9549           * @author Nicola Asuni
9550           * @access protected
9551           * @since 4.9.019 (2010-04-26)
9552           */
9553  		protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2) {
9554              $k = $this->k;
9555              if ($nc < 2) {
9556                  $nc = 2;
9557              }
9558              if ($pie) {
9559                  // center of the arc
9560                  $this->_outPoint($xc, $yc);
9561              }
9562              $xang = deg2rad((float) $xang);
9563              $angs = deg2rad((float) $angs);
9564              $angf = deg2rad((float) $angf);
9565              $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
9566              $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
9567              if ($as < 0) {
9568                  $as += (2 * M_PI);
9569              }
9570              if ($af < 0) {
9571                  $af += (2 * M_PI);
9572              }
9573              if ($as > $af) {
9574                  // reverse rotation go clockwise
9575                  $as -= (2 * M_PI);
9576              }
9577              $total_angle = ($af - $as);
9578              if ($nc < 2) {
9579                  $nc = 2;
9580              }
9581              // total arcs to draw
9582              $nc *= (2 * abs($total_angle) / M_PI);
9583              $nc = round($nc) + 1;
9584              // angle of each arc
9585              $arcang = $total_angle / $nc;
9586              // center point in PDF coordiantes
9587              $x0 = $xc;
9588              $y0 = ($this->h - $yc);
9589              // starting angle
9590              $ang = $as;
9591              $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
9592              $cos_xang = cos($xang);
9593              $sin_xang = sin($xang);
9594              $cos_ang = cos($ang);
9595              $sin_ang = sin($ang);
9596              // first arc point
9597              $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
9598              $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
9599              // first Bezier control point
9600              $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
9601              $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
9602              if ($pie) {
9603                  $this->_outLine($px1, $this->h - $py1);
9604              } else {
9605                  $this->_outPoint($px1, $this->h - $py1);
9606              }
9607              // draw arcs
9608              for ($i = 1; $i <= $nc; ++$i) {
9609                  // starting angle
9610                  $ang = $as + ($i * $arcang);
9611                  $cos_xang = cos($xang);
9612                  $sin_xang = sin($xang);
9613                  $cos_ang = cos($ang);
9614                  $sin_ang = sin($ang);
9615                  // second arc point
9616                  $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
9617                  $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
9618                  // second Bezier control point
9619                  $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
9620                  $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
9621                  // draw arc
9622                  $this->_outCurve(($px1 + $qx1), ($this->h - ($py1 + $qy1)), ($px2 - $qx2), ($this->h - ($py2 - $qy2)), $px2, ($this->h - $py2));
9623                  // move to next point
9624                  $px1 = $px2;
9625                  $py1 = $py2;
9626                  $qx1 = $qx2;
9627                  $qy1 = $qy2;
9628              }
9629              if ($pie) {
9630                  $this->_outLine($xc, $yc);
9631              }
9632          }
9633  
9634  
9635          /**
9636           * Draws a circle.
9637           * A circle is formed from n Bezier curves.
9638           * @param float $x0 Abscissa of center point.
9639           * @param float $y0 Ordinate of center point.
9640           * @param float $r Radius.
9641           * @param float $angstr: Angle start of draw line. Default value: 0.
9642           * @param float $angend: Angle finish of draw line. Default value: 360.
9643           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9644           * @param array $line_style Line style of circle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9645           * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
9646           * @param integer $nc Number of curves used to draw a 90 degrees portion of circle.
9647           * @access public
9648           * @since 2.1.000 (2008-01-08)
9649           */
9650  		public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
9651              $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
9652          }
9653  
9654          /**
9655           * Draws a polygonal line
9656           * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
9657           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9658           * @param array $line_style Line style of polygon. Array with keys among the following:
9659           * <ul>
9660           *     <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
9661           *     <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
9662           * </ul>
9663           * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
9664           * @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).
9665           * @param boolean $closed if true the polygon is closes, otherwise will remain open
9666           * @access public
9667           * @since 4.8.003 (2009-09-15)
9668           */
9669  		public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
9670              $this->Polygon($p, $style, $line_style, $fill_color, false);
9671          }
9672  
9673          /**
9674           * Draws a polygon.
9675           * @param array $p Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
9676           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9677           * @param array $line_style Line style of polygon. Array with keys among the following:
9678           * <ul>
9679           *     <li>all: Line style of all lines. Array like for {@link SetLineStyle SetLineStyle}.</li>
9680           *     <li>0 to ($np - 1): Line style of each line. Array like for {@link SetLineStyle SetLineStyle}.</li>
9681           * </ul>
9682           * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
9683           * @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).
9684           * @param boolean $closed if true the polygon is closes, otherwise will remain open
9685           * @access public
9686           * @since 2.1.000 (2008-01-08)
9687           */
9688  		public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
9689              $nc = count($p); // number of coordinates
9690              $np = $nc / 2; // number of points
9691              if ($closed) {
9692                  // close polygon by adding the first 2 points at the end (one line)
9693                  for ($i = 0; $i < 4; ++$i) {
9694                      $p[$nc + $i] = $p[$i];
9695                  }
9696                  // copy style for the last added line
9697                  if (isset($line_style[0])) {
9698                      $line_style[$np] = $line_style[0];
9699                  }
9700                  $nc += 4;
9701              }
9702              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
9703                  $this->SetFillColorArray($fill_color);
9704              }
9705              $op = $this->getPathPaintOperator($style);
9706              if ($op == 'f') {
9707                  $line_style = array();
9708              }
9709              $draw = true;
9710              if ($line_style) {
9711                  if (isset($line_style['all'])) {
9712                      $this->SetLineStyle($line_style['all']);
9713                  } else {
9714                      $draw = false;
9715                      if ($op == 'B') {
9716                          // draw fill
9717                          $op = 'f';
9718                          $this->_outPoint($p[0], $p[1]);
9719                          for ($i = 2; $i < $nc; $i = $i + 2) {
9720                              $this->_outLine($p[$i], $p[$i + 1]);
9721                          }
9722                          $this->_out($op);
9723                      }
9724                      // draw outline
9725                      $this->_outPoint($p[0], $p[1]);
9726                      for ($i = 2; $i < $nc; $i = $i + 2) {
9727                          $line_num = ($i / 2) - 1;
9728                          if (isset($line_style[$line_num])) {
9729                              if ($line_style[$line_num] != 0) {
9730                                  if (is_array($line_style[$line_num])) {
9731                                      $this->_out('S');
9732                                      $this->SetLineStyle($line_style[$line_num]);
9733                                      $this->_outPoint($p[$i - 2], $p[$i - 1]);
9734                                      $this->_outLine($p[$i], $p[$i + 1]);
9735                                      $this->_out('S');
9736                                      $this->_outPoint($p[$i], $p[$i + 1]);
9737                                  } else {
9738                                      $this->_outLine($p[$i], $p[$i + 1]);
9739                                  }
9740                              }
9741                          } else {
9742                              $this->_outLine($p[$i], $p[$i + 1]);
9743                          }
9744                      }
9745                      $this->_out($op);
9746                  }
9747              }
9748              if ($draw) {
9749                  $this->_outPoint($p[0], $p[1]);
9750                  for ($i = 2; $i < $nc; $i = $i + 2) {
9751                      $this->_outLine($p[$i], $p[$i + 1]);
9752                  }
9753                  $this->_out($op);
9754              }
9755          }
9756  
9757          /**
9758           * Draws a regular polygon.
9759           * @param float $x0 Abscissa of center point.
9760           * @param float $y0 Ordinate of center point.
9761           * @param float $r: Radius of inscribed circle.
9762           * @param integer $ns Number of sides.
9763           * @param float $angle Angle oriented (anti-clockwise). Default value: 0.
9764           * @param boolean $draw_circle Draw inscribed circle or not. Default value: false.
9765           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9766           * @param array $line_style Line style of polygon sides. Array with keys among the following:
9767           * <ul>
9768           *     <li>all: Line style of all sides. Array like for {@link SetLineStyle SetLineStyle}.</li>
9769           *     <li>0 to ($ns - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
9770           * </ul>
9771           * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
9772           * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
9773           * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
9774           * <ul>
9775           *     <li>D or empty string: Draw (default).</li>
9776           *     <li>F: Fill.</li>
9777           *     <li>DF or FD: Draw and fill.</li>
9778           *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
9779           *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
9780           * </ul>
9781           * @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).
9782           * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
9783           * @access public
9784           * @since 2.1.000 (2008-01-08)
9785           */
9786  		public 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()) {
9787              if (3 > $ns) {
9788                  $ns = 3;
9789              }
9790              if ($draw_circle) {
9791                  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
9792              }
9793              $p = array();
9794              for ($i = 0; $i < $ns; ++$i) {
9795                  $a = $angle + ($i * 360 / $ns);
9796                  $a_rad = deg2rad((float) $a);
9797                  $p[] = $x0 + ($r * sin($a_rad));
9798                  $p[] = $y0 + ($r * cos($a_rad));
9799              }
9800              $this->Polygon($p, $style, $line_style, $fill_color);
9801          }
9802  
9803          /**
9804           * Draws a star polygon
9805           * @param float $x0 Abscissa of center point.
9806           * @param float $y0 Ordinate of center point.
9807           * @param float $r Radius of inscribed circle.
9808           * @param integer $nv Number of vertices.
9809           * @param integer $ng Number of gap (if ($ng % $nv = 1) then is a regular polygon).
9810           * @param float $angle: Angle oriented (anti-clockwise). Default value: 0.
9811           * @param boolean $draw_circle: Draw inscribed circle or not. Default value is false.
9812           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9813           * @param array $line_style Line style of polygon sides. Array with keys among the following:
9814           * <ul>
9815           *     <li>all: Line style of all sides. Array like for
9816           * {@link SetLineStyle SetLineStyle}.</li>
9817           *     <li>0 to (n - 1): Line style of each side. Array like for {@link SetLineStyle SetLineStyle}.</li>
9818           * </ul>
9819           * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
9820           * @param array $fill_color Fill color. Format: array(red, green, blue). Default value: default color (empty array).
9821           * @param string $circle_style Style of rendering of inscribed circle (if draws). Possible values are:
9822           * <ul>
9823           *     <li>D or empty string: Draw (default).</li>
9824           *     <li>F: Fill.</li>
9825           *     <li>DF or FD: Draw and fill.</li>
9826           *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
9827           *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
9828           * </ul>
9829           * @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).
9830           * @param array $circle_fill_color Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
9831           * @access public
9832           * @since 2.1.000 (2008-01-08)
9833           */
9834  		public 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()) {
9835              if ($nv < 2) {
9836                  $nv = 2;
9837              }
9838              if ($draw_circle) {
9839                  $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
9840              }
9841              $p2 = array();
9842              $visited = array();
9843              for ($i = 0; $i < $nv; ++$i) {
9844                  $a = $angle + ($i * 360 / $nv);
9845                  $a_rad = deg2rad((float) $a);
9846                  $p2[] = $x0 + ($r * sin($a_rad));
9847                  $p2[] = $y0 + ($r * cos($a_rad));
9848                  $visited[] = false;
9849              }
9850              $p = array();
9851              $i = 0;
9852              do {
9853                  $p[] = $p2[$i * 2];
9854                  $p[] = $p2[($i * 2) + 1];
9855                  $visited[$i] = true;
9856                  $i += $ng;
9857                  $i %= $nv;
9858              } while (!$visited[$i]);
9859              $this->Polygon($p, $style, $line_style, $fill_color);
9860          }
9861  
9862          /**
9863           * Draws a rounded rectangle.
9864           * @param float $x Abscissa of upper-left corner.
9865           * @param float $y Ordinate of upper-left corner.
9866           * @param float $w Width.
9867           * @param float $h Height.
9868           * @param float $r the radius of the circle used to round off the corners of the rectangle.
9869           * @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").
9870           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9871           * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9872           * @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).
9873           * @access public
9874           * @since 2.1.000 (2008-01-08)
9875           */
9876  		public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
9877              $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
9878          }
9879  
9880          /**
9881           * Draws a rounded rectangle.
9882           * @param float $x Abscissa of upper-left corner.
9883           * @param float $y Ordinate of upper-left corner.
9884           * @param float $w Width.
9885           * @param float $h Height.
9886           * @param float $rx the x-axis radius of the ellipse used to round off the corners of the rectangle.
9887           * @param float $ry the y-axis radius of the ellipse used to round off the corners of the rectangle.
9888           * @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").
9889           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
9890           * @param array $border_style Border style of rectangle. Array like for {@link SetLineStyle SetLineStyle}. Default value: default line style (empty array).
9891           * @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).
9892           * @access public
9893           * @since 4.9.019 (2010-04-22)
9894           */
9895  		public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
9896              if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
9897                  // Not rounded
9898                  $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
9899                  return;
9900              }
9901              // Rounded
9902              if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
9903                  $this->SetFillColorArray($fill_color);
9904              }
9905              $op = $this->getPathPaintOperator($style);
9906              if ($op == 'f') {
9907                  $border_style = array();
9908              }
9909              if ($border_style) {
9910                  $this->SetLineStyle($border_style);
9911              }
9912              $MyArc = 4 / 3 * (sqrt(2) - 1);
9913              $this->_outPoint($x + $rx, $y);
9914              $xc = $x + $w - $rx;
9915              $yc = $y + $ry;
9916              $this->_outLine($xc, $y);
9917              if ($round_corner[0]) {
9918                  $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
9919              } else {
9920                  $this->_outLine($x + $w, $y);
9921              }
9922              $xc = $x + $w - $rx;
9923              $yc = $y + $h - $ry;
9924              $this->_outLine($x + $w, $yc);
9925              if ($round_corner[1]) {
9926                  $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
9927              } else {
9928                  $this->_outLine($x + $w, $y + $h);
9929              }
9930              $xc = $x + $rx;
9931              $yc = $y + $h - $ry;
9932              $this->_outLine($xc, $y + $h);
9933              if ($round_corner[2]) {
9934                  $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
9935              } else {
9936                  $this->_outLine($x, $y + $h);
9937              }
9938              $xc = $x + $rx;
9939              $yc = $y + $ry;
9940              $this->_outLine($x, $yc);
9941              if ($round_corner[3]) {
9942                  $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
9943              } else {
9944                  $this->_outLine($x, $y);
9945                  $this->_outLine($x + $rx, $y);
9946              }
9947              $this->_out($op);
9948          }
9949  
9950          /**
9951           * Draws a grahic arrow.
9952           * @parameter float $x0 Abscissa of first point.
9953           * @parameter float $y0 Ordinate of first point.
9954           * @parameter float $x0 Abscissa of second point.
9955           * @parameter float $y1 Ordinate of second point.
9956           * @parameter int $head_style (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
9957           * @parameter float $arm_size length of arrowhead arms
9958           * @parameter int $arm_angle angle between an arm and the shaft
9959           * @author Piotr Galecki, Nicola Asuni, Andy Meier
9960           * @since 4.6.018 (2009-07-10)
9961           */
9962  		public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
9963              // getting arrow direction angle
9964              // 0 deg angle is when both arms go along X axis. angle grows clockwise.
9965              $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
9966              if ($dir_angle < 0) {
9967                  $dir_angle += (2 * M_PI);
9968              }
9969              $arm_angle = deg2rad($arm_angle);
9970              $sx1 = $x1;
9971              $sy1 = $y1;
9972              if ($head_style > 0) {
9973                  // calculate the stopping point for the arrow shaft
9974                  $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
9975                  $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
9976              }
9977              // main arrow line / shaft
9978              $this->Line($x0, $y0, $sx1, $sy1);
9979              // left arrowhead arm tip
9980              $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
9981              $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
9982              // right arrowhead arm tip
9983              $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
9984              $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
9985              $mode = 'D';
9986              $style = array();
9987              switch ($head_style) {
9988                  case 0: {
9989                      // draw only arrowhead arms
9990                      $mode = 'D';
9991                      $style = array(1, 1, 0);
9992                      break;
9993                  }
9994                  case 1: {
9995                      // draw closed arrowhead, but no fill
9996                      $mode = 'D';
9997                      break;
9998                  }
9999                  case 2: {
10000                      // closed and filled arrowhead
10001                      $mode = 'DF';
10002                      break;
10003                  }
10004                  case 3: {
10005                      // filled arrowhead
10006                      $mode = 'F';
10007                      break;
10008                  }
10009              }
10010              $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
10011          }
10012  
10013          // END GRAPHIC FUNCTIONS SECTION -----------------------
10014  
10015          // BIDIRECTIONAL TEXT SECTION --------------------------
10016  
10017          /**
10018           * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
10019           * @param string $str string to manipulate.
10020           * @param bool $setbom  if true set the Byte Order Mark (BOM = 0xFEFF)
10021           * @param bool $forcertl if true forces RTL text direction
10022           * @return string
10023           * @access protected
10024           * @author Nicola Asuni
10025           * @since 2.1.000 (2008-01-08)
10026           */
10027  		protected function utf8StrRev($str, $setbom=false, $forcertl=false) {
10028              return $this->utf8StrArrRev($this->UTF8StringToArray($str), $str, $setbom, $forcertl);
10029          }
10030  
10031          /**
10032           * Reverse the RLT substrings array using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
10033           * @param array $arr array of unicode values.
10034           * @param string $str string to manipulate (or empty value).
10035           * @param bool $setbom  if true set the Byte Order Mark (BOM = 0xFEFF)
10036           * @param bool $forcertl if true forces RTL text direction
10037           * @return string
10038           * @access protected
10039           * @author Nicola Asuni
10040           * @since 4.9.000 (2010-03-27)
10041           */
10042  		protected function utf8StrArrRev($arr, $str='', $setbom=false, $forcertl=false) {
10043              return $this->arrUTF8ToUTF16BE($this->utf8Bidi($arr, $str, $forcertl), $setbom);
10044          }
10045  
10046          /**
10047           * Reverse the RLT substrings using the Bidirectional Algorithm (http://unicode.org/reports/tr9/).
10048           * @param array $ta array of characters composing the string.
10049           * @param string $str string to process
10050           * @param bool $forcertl if 'R' forces RTL, if 'L' forces LTR
10051           * @return array of unicode chars
10052           * @author Nicola Asuni
10053           * @access protected
10054           * @since 2.4.000 (2008-03-06)
10055           */
10056  		protected function utf8Bidi($ta, $str='', $forcertl=false) {
10057              global $unicode, $unicode_mirror, $unicode_arlet, $laa_array, $diacritics;
10058              // paragraph embedding level
10059              $pel = 0;
10060              // max level
10061              $maxlevel = 0;
10062              if ($this->empty_string($str)) {
10063                  // create string from array
10064                  $str = $this->UTF8ArrSubString($ta);
10065              }
10066              // check if string contains arabic text
10067              if (preg_match(K_RE_PATTERN_ARABIC, $str)) {
10068                  $arabic = true;
10069              } else {
10070                  $arabic = false;
10071              }
10072              // check if string contains RTL text
10073              if (!($forcertl OR $arabic OR preg_match(K_RE_PATTERN_RTL, $str))) {
10074                  return $ta;
10075              }
10076  
10077              // get number of chars
10078              $numchars = count($ta);
10079  
10080              if ($forcertl == 'R') {
10081                  $pel = 1;
10082              } elseif ($forcertl == 'L') {
10083                  $pel = 0;
10084              } else {
10085                  // P2. In each paragraph, find the first character of type L, AL, or R.
10086                  // 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.
10087                  for ($i=0; $i < $numchars; ++$i) {
10088                      $type = $unicode[$ta[$i]];
10089                      if ($type == 'L') {
10090                          $pel = 0;
10091                          break;
10092                      } elseif (($type == 'AL') OR ($type == 'R')) {
10093                          $pel = 1;
10094                          break;
10095                      }
10096                  }
10097              }
10098  
10099              // Current Embedding Level
10100              $cel = $pel;
10101              // directional override status
10102              $dos = 'N';
10103              $remember = array();
10104              // start-of-level-run
10105              $sor = $pel % 2 ? 'R' : 'L';
10106              $eor = $sor;
10107  
10108              // Array of characters data
10109              $chardata = Array();
10110  
10111              // 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.
10112              //     In the resolution of levels in rules I1 and I2, the maximum embedding level of 62 can be reached.
10113              for ($i=0; $i < $numchars; ++$i) {
10114                  if ($ta[$i] == K_RLE) {
10115                      // X2. With each RLE, compute the least greater odd embedding level.
10116                      //    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.
10117                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
10118                      $next_level = $cel + ($cel % 2) + 1;
10119                      if ($next_level < 62) {
10120                          $remember[] = array('num' => K_RLE, 'cel' => $cel, 'dos' => $dos);
10121                          $cel = $next_level;
10122                          $dos = 'N';
10123                          $sor = $eor;
10124                          $eor = $cel % 2 ? 'R' : 'L';
10125                      }
10126                  } elseif ($ta[$i] == K_LRE) {
10127                      // X3. With each LRE, compute the least greater even embedding level.
10128                      //    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.
10129                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
10130                      $next_level = $cel + 2 - ($cel % 2);
10131                      if ( $next_level < 62 ) {
10132                          $remember[] = array('num' => K_LRE, 'cel' => $cel, 'dos' => $dos);
10133                          $cel = $next_level;
10134                          $dos = 'N';
10135                          $sor = $eor;
10136                          $eor = $cel % 2 ? 'R' : 'L';
10137                      }
10138                  } elseif ($ta[$i] == K_RLO) {
10139                      // X4. With each RLO, compute the least greater odd embedding level.
10140                      //    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.
10141                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
10142                      $next_level = $cel + ($cel % 2) + 1;
10143                      if ($next_level < 62) {
10144                          $remember[] = array('num' => K_RLO, 'cel' => $cel, 'dos' => $dos);
10145                          $cel = $next_level;
10146                          $dos = 'R';
10147                          $sor = $eor;
10148                          $eor = $cel % 2 ? 'R' : 'L';
10149                      }
10150                  } elseif ($ta[$i] == K_LRO) {
10151                      // X5. With each LRO, compute the least greater even embedding level.
10152                      //    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.
10153                      //    b. If the new level would not be valid, then this code is invalid. Do not change the current level or override status.
10154                      $next_level = $cel + 2 - ($cel % 2);
10155                      if ( $next_level < 62 ) {
10156                          $remember[] = array('num' => K_LRO, 'cel' => $cel, 'dos' => $dos);
10157                          $cel = $next_level;
10158                          $dos = 'L';
10159                          $sor = $eor;
10160                          $eor = $cel % 2 ? 'R' : 'L';
10161                      }
10162                  } elseif ($ta[$i] == K_PDF) {
10163                      // 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.
10164                      if (count($remember)) {
10165                          $last = count($remember ) - 1;
10166                          if (($remember[$last]['num'] == K_RLE) OR
10167                              ($remember[$last]['num'] == K_LRE) OR
10168                              ($remember[$last]['num'] == K_RLO) OR
10169                              ($remember[$last]['num'] == K_LRO)) {
10170                              $match = array_pop($remember);
10171                              $cel = $match['cel'];
10172                              $dos = $match['dos'];
10173                              $sor = $eor;
10174                              $eor = ($cel > $match['cel'] ? $cel : $match['cel']) % 2 ? 'R' : 'L';
10175                          }
10176                      }
10177                  } elseif (($ta[$i] != K_RLE) AND
10178                                   ($ta[$i] != K_LRE) AND
10179                                   ($ta[$i] != K_RLO) AND
10180                                   ($ta[$i] != K_LRO) AND
10181                                   ($ta[$i] != K_PDF)) {
10182                      // X6. For all types besides RLE, LRE, RLO, LRO, and PDF:
10183                      //    a. Set the level of the current character to the current embedding level.
10184                      //    b. Whenever the directional override status is not neutral, reset the current character type to the directional override status.
10185                      if ($dos != 'N') {
10186                          $chardir = $dos;
10187                      } else {
10188                          if (isset($unicode[$ta[$i]])) {
10189                              $chardir = $unicode[$ta[$i]];
10190                          } else {
10191                              $chardir = 'L';
10192                          }
10193                      }
10194                      // stores string characters and other information
10195                      $chardata[] = array('char' => $ta[$i], 'level' => $cel, 'type' => $chardir, 'sor' => $sor, 'eor' => $eor);
10196                  }
10197              } // end for each char
10198  
10199              // X8. All explicit directional embeddings and overrides are completely terminated at the end of each paragraph. Paragraph separators are not included in the embedding.
10200              // X9. Remove all RLE, LRE, RLO, LRO, PDF, and BN codes.
10201              // 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.
10202  
10203              // 3.3.3 Resolving Weak Types
10204              // 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.
10205              // Nonspacing marks are now resolved based on the previous characters.
10206              $numchars = count($chardata);
10207  
10208              // 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.
10209              $prevlevel = -1; // track level changes
10210              $levcount = 0; // counts consecutive chars at the same level
10211              for ($i=0; $i < $numchars; ++$i) {
10212                  if ($chardata[$i]['type'] == 'NSM') {
10213                      if ($levcount) {
10214                          $chardata[$i]['type'] = $chardata[$i]['sor'];
10215                      } elseif ($i > 0) {
10216                          $chardata[$i]['type'] = $chardata[($i-1)]['type'];
10217                      }
10218                  }
10219                  if ($chardata[$i]['level'] != $prevlevel) {
10220                      $levcount = 0;
10221                  } else {
10222                      ++$levcount;
10223                  }
10224                  $prevlevel = $chardata[$i]['level'];
10225              }
10226  
10227              // 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.
10228              $prevlevel = -1;
10229              $levcount = 0;
10230              for ($i=0; $i < $numchars; ++$i) {
10231                  if ($chardata[$i]['char'] == 'EN') {
10232                      for ($j=$levcount; $j >= 0; $j--) {
10233                          if ($chardata[$j]['type'] == 'AL') {
10234                              $chardata[$i]['type'] = 'AN';
10235                          } elseif (($chardata[$j]['type'] == 'L') OR ($chardata[$j]['type'] == 'R')) {
10236                              break;
10237                          }
10238                      }
10239                  }
10240                  if ($chardata[$i]['level'] != $prevlevel) {
10241                      $levcount = 0;
10242                  } else {
10243                      ++$levcount;
10244                  }
10245                  $prevlevel = $chardata[$i]['level'];
10246              }
10247  
10248              // W3. Change all ALs to R.
10249              for ($i=0; $i < $numchars; ++$i) {
10250                  if ($chardata[$i]['type'] == 'AL') {
10251                      $chardata[$i]['type'] = 'R';
10252                  }
10253              }
10254  
10255              // 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.
10256              $prevlevel = -1;
10257              $levcount = 0;
10258              for ($i=0; $i < $numchars; ++$i) {
10259                  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
10260                      if (($chardata[$i]['type'] == 'ES') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
10261                          $chardata[$i]['type'] = 'EN';
10262                      } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'EN') AND ($chardata[($i+1)]['type'] == 'EN')) {
10263                          $chardata[$i]['type'] = 'EN';
10264                      } elseif (($chardata[$i]['type'] == 'CS') AND ($chardata[($i-1)]['type'] == 'AN') AND ($chardata[($i+1)]['type'] == 'AN')) {
10265                          $chardata[$i]['type'] = 'AN';
10266                      }
10267                  }
10268                  if ($chardata[$i]['level'] != $prevlevel) {
10269                      $levcount = 0;
10270                  } else {
10271                      ++$levcount;
10272                  }
10273                  $prevlevel = $chardata[$i]['level'];
10274              }
10275  
10276              // W5. A sequence of European terminators adjacent to European numbers changes to all European numbers.
10277              $prevlevel = -1;
10278              $levcount = 0;
10279              for ($i=0; $i < $numchars; ++$i) {
10280                  if ($chardata[$i]['type'] == 'ET') {
10281                      if (($levcount > 0) AND ($chardata[($i-1)]['type'] == 'EN')) {
10282                          $chardata[$i]['type'] = 'EN';
10283                      } else {
10284                          $j = $i+1;
10285                          while (($j < $numchars) AND ($chardata[$j]['level'] == $prevlevel)) {
10286                              if ($chardata[$j]['type'] == 'EN') {
10287                                  $chardata[$i]['type'] = 'EN';
10288                                  break;
10289                              } elseif ($chardata[$j]['type'] != 'ET') {
10290                                  break;
10291                              }
10292                              ++$j;
10293                          }
10294                      }
10295                  }
10296                  if ($chardata[$i]['level'] != $prevlevel) {
10297                      $levcount = 0;
10298                  } else {
10299                      ++$levcount;
10300                  }
10301                  $prevlevel = $chardata[$i]['level'];
10302              }
10303  
10304              // W6. Otherwise, separators and terminators change to Other Neutral.
10305              $prevlevel = -1;
10306              $levcount = 0;
10307              for ($i=0; $i < $numchars; ++$i) {
10308                  if (($chardata[$i]['type'] == 'ET') OR ($chardata[$i]['type'] == 'ES') OR ($chardata[$i]['type'] == 'CS')) {
10309                      $chardata[$i]['type'] = 'ON';
10310                  }
10311                  if ($chardata[$i]['level'] != $prevlevel) {
10312                      $levcount = 0;
10313                  } else {
10314                      ++$levcount;
10315                  }
10316                  $prevlevel = $chardata[$i]['level'];
10317              }
10318  
10319              //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.
10320              $prevlevel = -1;
10321              $levcount = 0;
10322              for ($i=0; $i < $numchars; ++$i) {
10323                  if ($chardata[$i]['char'] == 'EN') {
10324                      for ($j=$levcount; $j >= 0; $j--) {
10325                          if ($chardata[$j]['type'] == 'L') {
10326                              $chardata[$i]['type'] = 'L';
10327                          } elseif ($chardata[$j]['type'] == 'R') {
10328                              break;
10329                          }
10330                      }
10331                  }
10332                  if ($chardata[$i]['level'] != $prevlevel) {
10333                      $levcount = 0;
10334                  } else {
10335                      ++$levcount;
10336                  }
10337                  $prevlevel = $chardata[$i]['level'];
10338              }
10339  
10340              // 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.
10341              $prevlevel = -1;
10342              $levcount = 0;
10343              for ($i=0; $i < $numchars; ++$i) {
10344                  if (($levcount > 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
10345                      if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
10346                          $chardata[$i]['type'] = 'L';
10347                      } elseif (($chardata[$i]['type'] == 'N') AND
10348                       (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
10349                       (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
10350                          $chardata[$i]['type'] = 'R';
10351                      } elseif ($chardata[$i]['type'] == 'N') {
10352                          // N2. Any remaining neutrals take the embedding direction
10353                          $chardata[$i]['type'] = $chardata[$i]['sor'];
10354                      }
10355                  } elseif (($levcount == 0) AND (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] == $prevlevel)) {
10356                      // first char
10357                      if (($chardata[$i]['type'] == 'N') AND ($chardata[$i]['sor'] == 'L') AND ($chardata[($i+1)]['type'] == 'L')) {
10358                          $chardata[$i]['type'] = 'L';
10359                      } elseif (($chardata[$i]['type'] == 'N') AND
10360                       (($chardata[$i]['sor'] == 'R') OR ($chardata[$i]['sor'] == 'EN') OR ($chardata[$i]['sor'] == 'AN')) AND
10361                       (($chardata[($i+1)]['type'] == 'R') OR ($chardata[($i+1)]['type'] == 'EN') OR ($chardata[($i+1)]['type'] == 'AN'))) {
10362                          $chardata[$i]['type'] = 'R';
10363                      } elseif ($chardata[$i]['type'] == 'N') {
10364                          // N2. Any remaining neutrals take the embedding direction
10365                          $chardata[$i]['type'] = $chardata[$i]['sor'];
10366                      }
10367                  } elseif (($levcount > 0) AND ((($i+1) == $numchars) OR (($i+1) < $numchars) AND ($chardata[($i+1)]['level'] != $prevlevel))) {
10368                      //last char
10369                      if (($chardata[$i]['type'] == 'N') AND ($chardata[($i-1)]['type'] == 'L') AND ($chardata[$i]['eor'] == 'L')) {
10370                          $chardata[$i]['type'] = 'L';
10371                      } elseif (($chardata[$i]['type'] == 'N') AND
10372                       (($chardata[($i-1)]['type'] == 'R') OR ($chardata[($i-1)]['type'] == 'EN') OR ($chardata[($i-1)]['type'] == 'AN')) AND
10373                       (($chardata[$i]['eor'] == 'R') OR ($chardata[$i]['eor'] == 'EN') OR ($chardata[$i]['eor'] == 'AN'))) {
10374                          $chardata[$i]['type'] = 'R';
10375                      } elseif ($chardata[$i]['type'] == 'N') {
10376                          // N2. Any remaining neutrals take the embedding direction
10377                          $chardata[$i]['type'] = $chardata[$i]['sor'];
10378                      }
10379                  } elseif ($chardata[$i]['type'] == 'N') {
10380                      // N2. Any remaining neutrals take the embedding direction
10381                      $chardata[$i]['type'] = $chardata[$i]['sor'];
10382                  }
10383                  if ($chardata[$i]['level'] != $prevlevel) {
10384                      $levcount = 0;
10385                  } else {
10386                      ++$levcount;
10387                  }
10388                  $prevlevel = $chardata[$i]['level'];
10389              }
10390  
10391              // 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.
10392              // I2. For all characters with an odd (right-to-left) embedding direction, those of type L, EN or AN go up one level.
10393              for ($i=0; $i < $numchars; ++$i) {
10394                  $odd = $chardata[$i]['level'] % 2;
10395                  if ($odd) {
10396                      if (($chardata[$i]['type'] == 'L') OR ($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
10397                          $chardata[$i]['level'] += 1;
10398                      }
10399                  } else {
10400                      if ($chardata[$i]['type'] == 'R') {
10401                          $chardata[$i]['level'] += 1;
10402                      } elseif (($chardata[$i]['type'] == 'AN') OR ($chardata[$i]['type'] == 'EN')) {
10403                          $chardata[$i]['level'] += 2;
10404                      }
10405                  }
10406                  $maxlevel = max($chardata[$i]['level'],$maxlevel);
10407              }
10408  
10409              // L1. On each line, reset the embedding level of the following characters to the paragraph embedding level:
10410              //    1. Segment separators,
10411              //    2. Paragraph separators,
10412              //    3. Any sequence of whitespace characters preceding a segment separator or paragraph separator, and
10413              //    4. Any sequence of white space characters at the end of the line.
10414              for ($i=0; $i < $numchars; ++$i) {
10415                  if (($chardata[$i]['type'] == 'B') OR ($chardata[$i]['type'] == 'S')) {
10416                      $chardata[$i]['level'] = $pel;
10417                  } elseif ($chardata[$i]['type'] == 'WS') {
10418                      $j = $i+1;
10419                      while ($j < $numchars) {
10420                          if ((($chardata[$j]['type'] == 'B') OR ($chardata[$j]['type'] == 'S')) OR
10421                              (($j == ($numchars-1)) AND ($chardata[$j]['type'] == 'WS'))) {
10422                              $chardata[$i]['level'] = $pel;
10423                              break;
10424                          } elseif ($chardata[$j]['type'] != 'WS') {
10425                              break;
10426                          }
10427                          ++$j;
10428                      }
10429                  }
10430              }
10431  
10432              // Arabic Shaping
10433              // 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.
10434              if ($arabic) {
10435                  $endedletter = array(1569,1570,1571,1572,1573,1575,1577,1583,1584,1585,1586,1608,1688);
10436                  $alfletter = array(1570,1571,1573,1575);
10437                  $chardata2 = $chardata;
10438                  $laaletter = false;
10439                  $charAL = array();
10440                  $x = 0;
10441                  for ($i=0; $i < $numchars; ++$i) {
10442                      if (($unicode[$chardata[$i]['char']] == 'AL') OR ($chardata[$i]['char'] == 32) OR ($chardata[$i]['char'] == 8204)) {
10443                          $charAL[$x] = $chardata[$i];
10444                          $charAL[$x]['i'] = $i;
10445                          $chardata[$i]['x'] = $x;
10446                          ++$x;
10447                      }
10448                  }
10449                  $numAL = $x;
10450                  for ($i=0; $i < $numchars; ++$i) {
10451                      $thischar = $chardata[$i];
10452                      if ($i > 0) {
10453                          $prevchar = $chardata[($i-1)];
10454                      } else {
10455                          $prevchar = false;
10456                      }
10457                      if (($i+1) < $numchars) {
10458                          $nextchar = $chardata[($i+1)];
10459                      } else {
10460                          $nextchar = false;
10461                      }
10462                      if ($unicode[$thischar['char']] == 'AL') {
10463                          $x = $thischar['x'];
10464                          if ($x > 0) {
10465                              $prevchar = $charAL[($x-1)];
10466                          } else {
10467                              $prevchar = false;
10468                          }
10469                          if (($x+1) < $numAL) {
10470                              $nextchar = $charAL[($x+1)];
10471                          } else {
10472                              $nextchar = false;
10473                          }
10474                          // if laa letter
10475                          if (($prevchar !== false) AND ($prevchar['char'] == 1604) AND (in_array($thischar['char'], $alfletter))) {
10476                              $arabicarr = $laa_array;
10477                              $laaletter = true;
10478                              if ($x > 1) {
10479                                  $prevchar = $charAL[($x-2)];
10480                              } else {
10481                                  $prevchar = false;
10482                              }
10483                          } else {
10484                              $arabicarr = $unicode_arlet;
10485                              $laaletter = false;
10486                          }
10487                          if (($prevchar !== false) AND ($nextchar !== false) AND
10488                              (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
10489                              (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
10490                              ($prevchar['type'] == $thischar['type']) AND
10491                              ($nextchar['type'] == $thischar['type']) AND
10492                              ($nextchar['char'] != 1567)) {
10493                              if (in_array($prevchar['char'], $endedletter)) {
10494                                  if (isset($arabicarr[$thischar['char']][2])) {
10495                                      // initial
10496                                      $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
10497                                  }
10498                              } else {
10499                                  if (isset($arabicarr[$thischar['char']][3])) {
10500                                      // medial
10501                                      $chardata2[$i]['char'] = $arabicarr[$thischar['char']][3];
10502                                  }
10503                              }
10504                          } elseif (($nextchar !== false) AND
10505                              (($unicode[$nextchar['char']] == 'AL') OR ($unicode[$nextchar['char']] == 'NSM')) AND
10506                              ($nextchar['type'] == $thischar['type']) AND
10507                              ($nextchar['char'] != 1567)) {
10508                              if (isset($arabicarr[$chardata[$i]['char']][2])) {
10509                                  // initial
10510                                  $chardata2[$i]['char'] = $arabicarr[$thischar['char']][2];
10511                              }
10512                          } elseif ((($prevchar !== false) AND
10513                              (($unicode[$prevchar['char']] == 'AL') OR ($unicode[$prevchar['char']] == 'NSM')) AND
10514                              ($prevchar['type'] == $thischar['type'])) OR
10515                              (($nextchar !== false) AND ($nextchar['char'] == 1567))) {
10516                              // final
10517                              if (($i > 1) AND ($thischar['char'] == 1607) AND
10518                                  ($chardata[$i-1]['char'] == 1604) AND
10519                                  ($chardata[$i-2]['char'] == 1604)) {
10520                                  //Allah Word
10521                                  // mark characters to delete with false
10522                                  $chardata2[$i-2]['char'] = false;
10523                                  $chardata2[$i-1]['char'] = false;
10524                                  $chardata2[$i]['char'] = 65010;
10525                              } else {
10526                                  if (($prevchar !== false) AND in_array($prevchar['char'], $endedletter)) {
10527                                      if (isset($arabicarr[$thischar['char']][0])) {
10528                                          // isolated
10529                                          $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
10530                                      }
10531                                  } else {
10532                                      if (isset($arabicarr[$thischar['char']][1])) {
10533                                          // final
10534                                          $chardata2[$i]['char'] = $arabicarr[$thischar['char']][1];
10535                                      }
10536                                  }
10537                              }
10538                          } elseif (isset($arabicarr[$thischar['char']][0])) {
10539                              // isolated
10540                              $chardata2[$i]['char'] = $arabicarr[$thischar['char']][0];
10541                          }
10542                          // if laa letter
10543                          if ($laaletter) {
10544                              // mark characters to delete with false
10545                              $chardata2[($charAL[($x-1)]['i'])]['char'] = false;
10546                          }
10547                      } // end if AL (Arabic Letter)
10548                  } // end for each char
10549                  /*
10550                   * Combining characters that can occur with Arabic Shadda (0651 HEX, 1617 DEC) are replaced.
10551                   * Putting the combining mark and shadda in the same glyph allows us to avoid the two marks overlapping each other in an illegible manner.
10552                   */
10553                  $cw = &$this->CurrentFont['cw'];
10554                  for ($i = 0; $i < ($numchars-1); ++$i) {
10555                      if (($chardata2[$i]['char'] == 1617) AND (isset($diacritics[($chardata2[$i+1]['char'])]))) {
10556                          // check if the subtitution font is defined on current font
10557                          if (isset($cw[($diacritics[($chardata2[$i+1]['char'])])])) {
10558                              $chardata2[$i]['char'] = false;
10559                              $chardata2[$i+1]['char'] = $diacritics[($chardata2[$i+1]['char'])];
10560                          }
10561                      }
10562                  }
10563                  // remove marked characters
10564                  foreach ($chardata2 as $key => $value) {
10565                      if ($value['char'] === false) {
10566                          unset($chardata2[$key]);
10567                      }
10568                  }
10569                  $chardata = array_values($chardata2);
10570                  $numchars = count($chardata);
10571                  unset($chardata2);
10572                  unset($arabicarr);
10573                  unset($laaletter);
10574                  unset($charAL);
10575              }
10576  
10577              // 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.
10578              for ($j=$maxlevel; $j > 0; $j--) {
10579                  $ordarray = Array();
10580                  $revarr = Array();
10581                  $onlevel = false;
10582                  for ($i=0; $i < $numchars; ++$i) {
10583                      if ($chardata[$i]['level'] >= $j) {
10584                          $onlevel = true;
10585                          if (isset($unicode_mirror[$chardata[$i]['char']])) {
10586                              // 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.
10587                              $chardata[$i]['char'] = $unicode_mirror[$chardata[$i]['char']];
10588                          }
10589                          $revarr[] = $chardata[$i];
10590                      } else {
10591                          if ($onlevel) {
10592                              $revarr = array_reverse($revarr);
10593                              $ordarray = array_merge($ordarray, $revarr);
10594                              $revarr = Array();
10595                              $onlevel = false;
10596                          }
10597                          $ordarray[] = $chardata[$i];
10598                      }
10599                  }
10600                  if ($onlevel) {
10601                      $revarr = array_reverse($revarr);
10602                      $ordarray = array_merge($ordarray, $revarr);
10603                  }
10604                  $chardata = $ordarray;
10605              }
10606  
10607              $ordarray = array();
10608              for ($i=0; $i < $numchars; ++$i) {
10609                  $ordarray[] = $chardata[$i]['char'];
10610              }
10611  
10612              return $ordarray;
10613          }
10614  
10615          // END OF BIDIRECTIONAL TEXT SECTION -------------------
10616  
10617          /**
10618           * Adds a bookmark.
10619           * @param string $txt bookmark description.
10620           * @param int $level bookmark level (minimum value is 0).
10621           * @param float $y Ordinate of the boorkmark position (default = -1 = current position).
10622           * @param int $page target page number (leave empty for current page).
10623           * @access public
10624           * @author Olivier Plathey, Nicola Asuni
10625           * @since 2.1.002 (2008-02-12)
10626           */
10627  		public function Bookmark($txt, $level=0, $y=-1, $page='') {
10628              if ($level < 0) {
10629                  $level = 0;
10630              }
10631              if (isset($this->outlines[0])) {
10632                  $lastoutline = end($this->outlines);
10633                  $maxlevel = $lastoutline['l'] + 1;
10634              } else {
10635                  $maxlevel = 0;
10636              }
10637              if ($level > $maxlevel) {
10638                  $level = $maxlevel;
10639              }
10640              if ($y == -1) {
10641                  $y = $this->GetY();
10642              }
10643              if (empty($page)) {
10644                  $page = $this->PageNo();
10645              }
10646              $this->outlines[] = array('t' => $txt, 'l' => $level, 'y' => $y, 'p' => $page);
10647          }
10648  
10649          /**
10650           * Create a bookmark PDF string.
10651           * @access protected
10652           * @author Olivier Plathey, Nicola Asuni
10653           * @since 2.1.002 (2008-02-12)
10654           */
10655  		protected function _putbookmarks() {
10656              $nb = count($this->outlines);
10657              if ($nb == 0) {
10658                  return;
10659              }
10660              // get sorting columns
10661              $outline_p = array();
10662              $outline_y = array();
10663              foreach ($this->outlines as $key => $row) {
10664                  $outline_p[$key]  = $row['p'];
10665                  $outline_k[$key] = $key;
10666              }
10667              // sort outlines by page and original position
10668              array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
10669              $lru = array();
10670              $level = 0;
10671              foreach ($this->outlines as $i => $o) {
10672                  if ($o['l'] > 0) {
10673                      $parent = $lru[($o['l'] - 1)];
10674                      //Set parent and last pointers
10675                      $this->outlines[$i]['parent'] = $parent;
10676                      $this->outlines[$parent]['last'] = $i;
10677                      if ($o['l'] > $level) {
10678                          //Level increasing: set first pointer
10679                          $this->outlines[$parent]['first'] = $i;
10680                      }
10681                  } else {
10682                      $this->outlines[$i]['parent'] = $nb;
10683                  }
10684                  if (($o['l'] <= $level) AND ($i > 0)) {
10685                      //Set prev and next pointers
10686                      $prev = $lru[$o['l']];
10687                      $this->outlines[$prev]['next'] = $i;
10688                      $this->outlines[$i]['prev'] = $prev;
10689                  }
10690                  $lru[$o['l']] = $i;
10691                  $level = $o['l'];
10692              }
10693              //Outline items
10694              $n = $this->n + 1;
10695              foreach ($this->outlines as $i => $o) {
10696                  $this->_newobj();
10697                  // covert HTML title to string
10698                  $nltags = '/<br[\s]?\/>|<\/(blockquote|dd|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|p|pre|ul|tcpdf|table|tr|td)>/si';
10699                  $title = preg_replace($nltags, "\n", $o['t']);
10700                  $title = preg_replace("/[\r]+/si", '', $title);
10701                  $title = preg_replace("/[\n]+/si", "\n", $title);
10702                  $title = strip_tags(trim($title));
10703                  $out = '<</Title '.$this->_textstring($title);
10704                  $out .= ' /Parent '.($n + $o['parent']).' 0 R';
10705                  if (isset($o['prev'])) {
10706                      $out .= ' /Prev '.($n + $o['prev']).' 0 R';
10707                  }
10708                  if (isset($o['next'])) {
10709                      $out .= ' /Next '.($n + $o['next']).' 0 R';
10710                  }
10711                  if (isset($o['first'])) {
10712                      $out .= ' /First '.($n + $o['first']).' 0 R';
10713                  }
10714                  if (isset($o['last'])) {
10715                      $out .= ' /Last '.($n + $o['last']).' 0 R';
10716                  }
10717                  $out .= ' '.sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]', (1 + (2 * $o['p'])), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
10718                  $out .= ' /Count 0 >> endobj';
10719                  $this->_out($out);
10720              }
10721              //Outline root
10722              $this->_newobj();
10723              $this->OutlineRoot = $this->n;
10724              $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >> endobj');
10725          }
10726  
10727          // --- JAVASCRIPT ------------------------------------------------------
10728  
10729          /**
10730           * Adds a javascript
10731           * @param string $script Javascript code
10732           * @access public
10733           * @author Johannes Güntert, Nicola Asuni
10734           * @since 2.1.002 (2008-02-12)
10735           */
10736  		public function IncludeJS($script) {
10737              $this->javascript .= $script;
10738          }
10739  
10740          /**
10741           * Adds a javascript object and return object ID
10742           * @param string $script Javascript code
10743           * @param boolean $onload if true executes this object when opening the document
10744           * @return int internal object ID
10745           * @access public
10746           * @author Nicola Asuni
10747           * @since 4.8.000 (2009-09-07)
10748           */
10749  		public function addJavascriptObject($script, $onload=false) {
10750              ++$this->js_obj_id;
10751              $this->js_objects[$this->js_obj_id] = array('js' => $script, 'onload' => $onload);
10752              return $this->js_obj_id;
10753          }
10754  
10755          /**
10756           * Create a javascript PDF string.
10757           * @access protected
10758           * @author Johannes Güntert, Nicola Asuni
10759           * @since 2.1.002 (2008-02-12)
10760           */
10761  		protected function _putjavascript() {
10762              if (empty($this->javascript) AND empty($this->js_objects)) {
10763                  return;
10764              }
10765              if (strpos($this->javascript, 'this.addField') > 0) {
10766                  if (!$this->ur) {
10767                      //$this->setUserRights();
10768                  }
10769                  // the following two lines are used to avoid form fields duplication after saving
10770                  // The addField method only works on Acrobat Writer, unless the document is signed with Adobe private key (UR3)
10771                  $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%.2F,%.2F,%.2F,%.2F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
10772                  $jsb = "getField('tcpdfdocsaved').value='saved';";
10773                  $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
10774              }
10775              $this->n_js = $this->_newobj();
10776              $out = ' << /Names [';
10777              if (!empty($this->javascript)) {
10778                  $out .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
10779              }
10780              if (!empty($this->js_objects)) {
10781                  foreach ($this->js_objects as $key => $val) {
10782                      if ($val['onload']) {
10783                          $out .= ' (JS'.$key.') '.$key.' 0 R';
10784                      }
10785                  }
10786              }
10787              $out .= ' ] >> endobj';
10788              $this->_out($out);
10789              // default Javascript object
10790              if (!empty($this->javascript)) {
10791                  $this->_newobj();
10792                  $out = '<< /S /JavaScript';
10793                  $out .= ' /JS '.$this->_textstring($this->javascript);
10794                  $out .= ' >> endobj';
10795                  $this->_out($out);
10796              }
10797              // additional Javascript objects
10798              if (!empty($this->js_objects)) {
10799                  foreach ($this->js_objects as $key => $val) {
10800                      $this->offsets[$key] = $this->bufferlen;
10801                      $out = $key.' 0 obj'."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js']).' >> endobj';
10802                      $this->_out($out);
10803                  }
10804              }
10805          }
10806  
10807          /**
10808           * Convert color to javascript color.
10809           * @param string $color color name or #RRGGBB
10810           * @access protected
10811           * @author Denis Van Nuffelen, Nicola Asuni
10812           * @since 2.1.002 (2008-02-12)
10813           */
10814  		protected function _JScolor($color) {
10815              static $aColors = array('transparent', 'black', 'white', 'red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'dkGray', 'gray', 'ltGray');
10816              if (substr($color,0,1) == '#') {
10817                  return sprintf("['RGB',%.3F,%.3F,%.3F]", hexdec(substr($color,1,2))/255, hexdec(substr($color,3,2))/255, hexdec(substr($color,5,2))/255);
10818              }
10819              if (!in_array($color,$aColors)) {
10820                  $this->Error('Invalid color: '.$color);
10821              }
10822              return 'color.'.$color;
10823          }
10824  
10825          /**
10826           * Adds a javascript form field.
10827           * @param string $type field type
10828           * @param string $name field name
10829           * @param int $x horizontal position
10830           * @param int $y vertical position
10831           * @param int $w width
10832           * @param int $h height
10833           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10834           * @access protected
10835           * @author Denis Van Nuffelen, Nicola Asuni
10836           * @since 2.1.002 (2008-02-12)
10837           */
10838  		protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
10839              if ($this->rtl) {
10840                  $x = $x - $w;
10841              }
10842              // the followind avoid fields duplication after saving the document
10843              $this->javascript .= "if(getField('tcpdfdocsaved').value != 'saved') {";
10844              $k = $this->k;
10845              $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";
10846              $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
10847              while (list($key, $val) = each($prop)) {
10848                  if (strcmp(substr($key, -5), 'Color') == 0) {
10849                      $val = $this->_JScolor($val);
10850                  } else {
10851                      $val = "'".$val."'";
10852                  }
10853                  $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
10854              }
10855              if ($this->rtl) {
10856                  $this->x -= $w;
10857              } else {
10858                  $this->x += $w;
10859              }
10860              $this->javascript .= '}';
10861          }
10862  
10863          // --- FORM FIELDS -----------------------------------------------------
10864  
10865          /**
10866           * Convert JavaScript form fields properties array to Annotation Properties array.
10867           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
10868           * @return array of annotation properties
10869           * @access protected
10870           * @author Nicola Asuni
10871           * @since 4.8.000 (2009-09-06)
10872           */
10873  		protected function getAnnotOptFromJSProp($prop) {
10874              if (isset($prop['aopt']) AND is_array($prop['aopt'])) {
10875                  // the annotation options area lready defined
10876                  return $prop['aopt'];
10877              }
10878              $opt = array(); // value to be returned
10879              // alignment: Controls how the text is laid out within the text field.
10880              if (isset($prop['alignment'])) {
10881                  switch ($prop['alignment']) {
10882                      case 'left': {
10883                          $opt['q'] = 0;
10884                          break;
10885                      }
10886                      case 'center': {
10887                          $opt['q'] = 1;
10888                          break;
10889                      }
10890                      case 'right': {
10891                          $opt['q'] = 2;
10892                          break;
10893                      }
10894                      default: {
10895                          $opt['q'] = ($this->rtl)?2:0;
10896                          break;
10897                      }
10898                  }
10899              }
10900              // lineWidth: Specifies the thickness of the border when stroking the perimeter of a field's rectangle.
10901              if (isset($prop['lineWidth'])) {
10902                  $linewidth = intval($prop['lineWidth']);
10903              } else {
10904                  $linewidth = 1;
10905              }
10906              // borderStyle: The border style for a field.
10907              if (isset($prop['borderStyle'])) {
10908                  switch ($prop['borderStyle']) {
10909                      case 'border.d':
10910                      case 'dashed': {
10911                          $opt['border'] = array(0, 0, $linewidth, array(3, 2));
10912                          $opt['bs'] = array('w'=>$linewidth, 's'=>'D', 'd'=>array(3, 2));
10913                          break;
10914                      }
10915                      case 'border.b':
10916                      case 'beveled': {
10917                          $opt['border'] = array(0, 0, $linewidth);
10918                          $opt['bs'] = array('w'=>$linewidth, 's'=>'B');
10919                          break;
10920                      }
10921                      case 'border.i':
10922                      case 'inset': {
10923                          $opt['border'] = array(0, 0, $linewidth);
10924                          $opt['bs'] = array('w'=>$linewidth, 's'=>'I');
10925                          break;
10926                      }
10927                      case 'border.u':
10928                      case 'underline': {
10929                          $opt['border'] = array(0, 0, $linewidth);
10930                          $opt['bs'] = array('w'=>$linewidth, 's'=>'U');
10931                          break;
10932                      }
10933                      default:
10934                      case 'border.s':
10935                      case 'solid': {
10936                          $opt['border'] = array(0, 0, $linewidth);
10937                          $opt['bs'] = array('w'=>$linewidth, 's'=>'S');
10938                          break;
10939                      }
10940                  }
10941              }
10942              if (isset($prop['border']) AND is_array($prop['border'])) {
10943                  $opt['border'] = $prop['border'];
10944              }
10945              if (!isset($opt['mk'])) {
10946                  $opt['mk'] = array();
10947              }
10948              if (!isset($opt['mk']['if'])) {
10949                  $opt['mk']['if'] = array();
10950              }
10951              $opt['mk']['if']['a'] = array(0.5, 0.5);
10952              // buttonAlignX: Controls how space is distributed from the left of the button face with respect to the icon.
10953              if (isset($prop['buttonAlignX'])) {
10954                  $opt['mk']['if']['a'][0] = $prop['buttonAlignX'];
10955              }
10956              // buttonAlignY: Controls how unused space is distributed from the bottom of the button face with respect to the icon.
10957              if (isset($prop['buttonAlignY'])) {
10958                  $opt['mk']['if']['a'][1] = $prop['buttonAlignY'];
10959              }
10960              // buttonFitBounds: If true, the extent to which the icon may be scaled is set to the bounds of the button field.
10961              if (isset($prop['buttonFitBounds']) AND ($prop['buttonFitBounds'] == 'true')) {
10962                  $opt['mk']['if']['fb'] = true;
10963              }
10964              // buttonScaleHow: Controls how the icon is scaled (if necessary) to fit inside the button face.
10965              if (isset($prop['buttonScaleHow'])) {
10966                  switch ($prop['buttonScaleHow']) {
10967                      case 'scaleHow.proportional': {
10968                          $opt['mk']['if']['s'] = 'P';
10969                          break;
10970                      }
10971                      case 'scaleHow.anamorphic': {
10972                          $opt['mk']['if']['s'] = 'A';
10973                          break;
10974                      }
10975                  }
10976              }
10977              // buttonScaleWhen: Controls when an icon is scaled to fit inside the button face.
10978              if (isset($prop['buttonScaleWhen'])) {
10979                  switch ($prop['buttonScaleWhen']) {
10980                      case 'scaleWhen.always': {
10981                          $opt['mk']['if']['sw'] = 'A';
10982                          break;
10983                      }
10984                      case 'scaleWhen.never': {
10985                          $opt['mk']['if']['sw'] = 'N';
10986                          break;
10987                      }
10988                      case 'scaleWhen.tooBig': {
10989                          $opt['mk']['if']['sw'] = 'B';
10990                          break;
10991                      }
10992                      case 'scaleWhen.tooSmall': {
10993                          $opt['mk']['if']['sw'] = 'S';
10994                          break;
10995                      }
10996                  }
10997              }
10998              // buttonPosition: Controls how the text and the icon of the button are positioned with respect to each other within the button face.
10999              if (isset($prop['buttonPosition'])) {
11000                  switch ($prop['buttonPosition']) {
11001                      case 0:
11002                      case 'position.textOnly': {
11003                          $opt['mk']['tp'] = 0;
11004                          break;
11005                      }
11006                      case 1:
11007                      case 'position.iconOnly': {
11008                          $opt['mk']['tp'] = 1;
11009                          break;
11010                      }
11011                      case 2:
11012                      case 'position.iconTextV': {
11013                          $opt['mk']['tp'] = 2;
11014                          break;
11015                      }
11016                      case 3:
11017                      case 'position.textIconV': {
11018                          $opt['mk']['tp'] = 3;
11019                          break;
11020                      }
11021                      case 4:
11022                      case 'position.iconTextH': {
11023                          $opt['mk']['tp'] = 4;
11024                          break;
11025                      }
11026                      case 5:
11027                      case 'position.textIconH': {
11028                          $opt['mk']['tp'] = 5;
11029                          break;
11030                      }
11031                      case 6:
11032                      case 'position.overlay': {
11033                          $opt['mk']['tp'] = 6;
11034                          break;
11035                      }
11036                  }
11037              }
11038              // fillColor: Specifies the background color for a field.
11039              if (isset($prop['fillColor'])) {
11040                  if (is_array($prop['fillColor'])) {
11041                      $opt['mk']['bg'] = $prop['fillColor'];
11042                  } else {
11043                      $opt['mk']['bg'] = $this->convertHTMLColorToDec($prop['fillColor']);
11044                  }
11045              }
11046              // strokeColor: Specifies the stroke color for a field that is used to stroke the rectangle of the field with a line as large as the line width.
11047              if (isset($prop['strokeColor'])) {
11048                  if (is_array($prop['strokeColor'])) {
11049                      $opt['mk']['bc'] = $prop['strokeColor'];
11050                  } else {
11051                      $opt['mk']['bc'] = $this->convertHTMLColorToDec($prop['strokeColor']);
11052                  }
11053              }
11054              // rotation: The rotation of a widget in counterclockwise increments.
11055              if (isset($prop['rotation'])) {
11056                  $opt['mk']['r'] = $prop['rotation'];
11057              }
11058              // charLimit: Limits the number of characters that a user can type into a text field.
11059              if (isset($prop['charLimit'])) {
11060                  $opt['maxlen'] = intval($prop['charLimit']);
11061              }
11062              if (!isset($ff)) {
11063                  $ff = 0;
11064              }
11065              // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
11066              if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
11067                  $ff += 1 << 0;
11068              }
11069              // required: Specifies whether a field requires a value.
11070              if (isset($prop['required']) AND ($prop['required'] == 'true')) {
11071                  $ff += 1 << 1;
11072              }
11073              // multiline: Controls how text is wrapped within the field.
11074              if (isset($prop['multiline']) AND ($prop['multiline'] == 'true')) {
11075                  $ff += 1 << 12;
11076              }
11077              // password: Specifies whether the field should display asterisks when data is entered in the field.
11078              if (isset($prop['password']) AND ($prop['password'] == 'true')) {
11079                  $ff += 1 << 13;
11080              }
11081              // NoToggleToOff: If set, exactly one radio button shall be selected at all times; selecting the currently selected button has no effect.
11082              if (isset($prop['NoToggleToOff']) AND ($prop['NoToggleToOff'] == 'true')) {
11083                  $ff += 1 << 14;
11084              }
11085              // Radio: If set, the field is a set of radio buttons.
11086              if (isset($prop['Radio']) AND ($prop['Radio'] == 'true')) {
11087                  $ff += 1 << 15;
11088              }
11089              // Pushbutton: If set, the field is a pushbutton that does not retain a permanent value.
11090              if (isset($prop['Pushbutton']) AND ($prop['Pushbutton'] == 'true')) {
11091                  $ff += 1 << 16;
11092              }
11093              // Combo: If set, the field is a combo box; if clear, the field is a list box.
11094              if (isset($prop['Combo']) AND ($prop['Combo'] == 'true')) {
11095                  $ff += 1 << 17;
11096              }
11097              // editable: Controls whether a combo box is editable.
11098              if (isset($prop['editable']) AND ($prop['editable'] == 'true')) {
11099                  $ff += 1 << 18;
11100              }
11101              // Sort: If set, the field's option items shall be sorted alphabetically.
11102              if (isset($prop['Sort']) AND ($prop['Sort'] == 'true')) {
11103                  $ff += 1 << 19;
11104              }
11105              // fileSelect: If true, sets the file-select flag in the Options tab of the text field (Field is Used for File Selection).
11106              if (isset($prop['fileSelect']) AND ($prop['fileSelect'] == 'true')) {
11107                  $ff += 1 << 20;
11108              }
11109              // multipleSelection: If true, indicates that a list box allows a multiple selection of items.
11110              if (isset($prop['multipleSelection']) AND ($prop['multipleSelection'] == 'true')) {
11111                  $ff += 1 << 21;
11112              }
11113              // doNotSpellCheck: If true, spell checking is not performed on this editable text field.
11114              if (isset($prop['doNotSpellCheck']) AND ($prop['doNotSpellCheck'] == 'true')) {
11115                  $ff += 1 << 22;
11116              }
11117              // doNotScroll: If true, the text field does not scroll and the user, therefore, is limited by the rectangular region designed for the field.
11118              if (isset($prop['doNotScroll']) AND ($prop['doNotScroll'] == 'true')) {
11119                  $ff += 1 << 23;
11120              }
11121              // comb: If set to true, the field background is drawn as series of boxes (one for each character in the value of the field) and each character of the content is drawn within those boxes. The number of boxes drawn is determined from the charLimit property. It applies only to text fields. The setter will also raise if any of the following field properties are also set multiline, password, and fileSelect. A side-effect of setting this property is that the doNotScroll property is also set.
11122              if (isset($prop['comb']) AND ($prop['comb'] == 'true')) {
11123                  $ff += 1 << 24;
11124              }
11125              // radiosInUnison: If false, even if a group of radio buttons have the same name and export value, they behave in a mutually exclusive fashion, like HTML radio buttons.
11126              if (isset($prop['radiosInUnison']) AND ($prop['radiosInUnison'] == 'true')) {
11127                  $ff += 1 << 25;
11128              }
11129              // richText: If true, the field allows rich text formatting.
11130              if (isset($prop['richText']) AND ($prop['richText'] == 'true')) {
11131                  $ff += 1 << 25;
11132              }
11133              // commitOnSelChange: Controls whether a field value is committed after a selection change.
11134              if (isset($prop['commitOnSelChange']) AND ($prop['commitOnSelChange'] == 'true')) {
11135                  $ff += 1 << 26;
11136              }
11137              $opt['ff'] = $ff;
11138              // defaultValue: The default value of a field - that is, the value that the field is set to when the form is reset.
11139              if (isset($prop['defaultValue'])) {
11140                  $opt['dv'] = $prop['defaultValue'];
11141              }
11142              $f = 4; // default value for annotation flags
11143              // readonly: The read-only characteristic of a field. If a field is read-only, the user can see the field but cannot change it.
11144              if (isset($prop['readonly']) AND ($prop['readonly'] == 'true')) {
11145                  $f += 1 << 6;
11146              }
11147              // display: Controls whether the field is hidden or visible on screen and in print.
11148              if (isset($prop['display'])) {
11149                  if ($prop['display'] == 'display.visible') {
11150                      //
11151                  } elseif ($prop['display'] == 'display.hidden') {
11152                      $f += 1 << 1;
11153                  } elseif ($prop['display'] == 'display.noPrint') {
11154                      $f -= 1 << 2;
11155                  } elseif ($prop['display'] == 'display.noView') {
11156                      $f += 1 << 5;
11157                  }
11158              }
11159              $opt['f'] = $f;
11160              // currentValueIndices: Reads and writes single or multiple values of a list box or combo box.
11161              if (isset($prop['currentValueIndices']) AND is_array($prop['currentValueIndices'])) {
11162                  $opt['i'] = $prop['currentValueIndices'];
11163              }
11164              // value: The value of the field data that the user has entered.
11165              if (isset($prop['value'])) {
11166                  if (is_array($prop['value'])) {
11167                      $opt['opt'] = array();
11168                      foreach ($prop['value'] AS $key => $optval) {
11169                          // exportValues: An array of strings representing the export values for the field.
11170                          if (isset($prop['exportValues'][$key])) {
11171                              $opt['opt'][$key] = array($prop['exportValues'][$key], $prop['value'][$key]);
11172                          } else {
11173                              $opt['opt'][$key] = $prop['value'][$key];
11174                          }
11175                      }
11176                  } else {
11177                      $opt['v'] = $prop['value'];
11178                  }
11179              }
11180              // richValue: This property specifies the text contents and formatting of a rich text field.
11181              if (isset($prop['richValue'])) {
11182                  $opt['rv'] = $prop['richValue'];
11183              }
11184              // submitName: If nonempty, used during form submission instead of name. Only applicable if submitting in HTML format (that is, URL-encoded).
11185              if (isset($prop['submitName'])) {
11186                  $opt['tm'] = $prop['submitName'];
11187              }
11188              // name: Fully qualified field name.
11189              if (isset($prop['name'])) {
11190                  $opt['t'] = $prop['name'];
11191              }
11192              // userName: The user name (short description string) of the field.
11193              if (isset($prop['userName'])) {
11194                  $opt['tu'] = $prop['userName'];
11195              }
11196              // highlight: Defines how a button reacts when a user clicks it.
11197              if (isset($prop['highlight'])) {
11198                  switch ($prop['highlight']) {
11199                      case 'none':
11200                      case 'highlight.n': {
11201                          $opt['h'] = 'N';
11202                          break;
11203                      }
11204                      case 'invert':
11205                      case 'highlight.i': {
11206                          $opt['h'] = 'i';
11207                          break;
11208                      }
11209                      case 'push':
11210                      case 'highlight.p': {
11211                          $opt['h'] = 'P';
11212                          break;
11213                      }
11214                      case 'outline':
11215                      case 'highlight.o': {
11216                          $opt['h'] = 'O';
11217                          break;
11218                      }
11219                  }
11220              }
11221              // Unsupported options:
11222              // - calcOrderIndex: Changes the calculation order of fields in the document.
11223              // - delay: Delays the redrawing of a field's appearance.
11224              // - defaultStyle: This property defines the default style attributes for the form field.
11225              // - style: Allows the user to set the glyph style of a check box or radio button.
11226              // - textColor, textFont, textSize
11227              return $opt;
11228          }
11229  
11230          /**
11231           * Set default properties for form fields.
11232           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11233           * @access public
11234           * @author Nicola Asuni
11235           * @since 4.8.000 (2009-09-06)
11236           */
11237  		public function setFormDefaultProp($prop=array()) {
11238              $this->default_form_prop = $prop;
11239          }
11240  
11241          /**
11242           * Return the default properties for form fields.
11243           * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11244           * @access public
11245           * @author Nicola Asuni
11246           * @since 4.8.000 (2009-09-06)
11247           */
11248  		public function getFormDefaultProp() {
11249              return $this->default_form_prop;
11250          }
11251  
11252          /**
11253           * Creates a text field
11254           * @param string $name field name
11255           * @param float $w Width of the rectangle
11256           * @param float $h Height of the rectangle
11257           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11258           * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
11259           * @param float $x Abscissa of the upper-left corner of the rectangle
11260           * @param float $y Ordinate of the upper-left corner of the rectangle
11261           * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11262           * @access public
11263           * @author Nicola Asuni
11264           * @since 4.8.000 (2009-09-07)
11265           */
11266  		public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
11267              if ($x === '') {
11268                  $x = $this->x;
11269              }
11270              if ($y === '') {
11271                  $y = $this->y;
11272              }
11273              if ($js) {
11274                  $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
11275                  return;
11276              }
11277              // get default style
11278              $prop = array_merge($this->getFormDefaultProp(), $prop);
11279              // get annotation data
11280              $popt = $this->getAnnotOptFromJSProp($prop);
11281              // set default appearance stream
11282              $font = $this->FontFamily;
11283              $fontkey = array_search($font, $this->fontkeys);
11284              if (!in_array($fontkey, $this->annotation_fonts)) {
11285                  $this->annotation_fonts[$font] = $fontkey;
11286              }
11287              $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
11288              $popt['da'] = $fontstyle;
11289              $popt['ap'] = array();
11290              $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
11291              // merge options
11292              $opt = array_merge($popt, $opt);
11293              // remove some conflicting options
11294              unset($opt['bs']);
11295              // set remaining annotation data
11296              $opt['Subtype'] = 'Widget';
11297              $opt['ft'] = 'Tx';
11298              $opt['t'] = $name;
11299              /*
11300              Additional annotation's parameters (check _putannotsobj() method):
11301              //$opt['f']
11302              //$opt['ap']
11303              //$opt['as']
11304              //$opt['bs']
11305              //$opt['be']
11306              //$opt['c']
11307              //$opt['border']
11308              //$opt['h']
11309              //$opt['mk']
11310              //$opt['mk']['r']
11311              //$opt['mk']['bc']
11312              //$opt['mk']['bg']
11313              //$opt['mk']['ca']
11314              //$opt['mk']['rc']
11315              //$opt['mk']['ac']
11316              //$opt['mk']['i']
11317              //$opt['mk']['ri']
11318              //$opt['mk']['ix']
11319              //$opt['mk']['if']
11320              //$opt['mk']['if']['sw']
11321              //$opt['mk']['if']['s']
11322              //$opt['mk']['if']['a']
11323              //$opt['mk']['if']['fb']
11324              //$opt['mk']['tp']
11325              //$opt['tu']
11326              //$opt['tm']
11327              //$opt['ff']
11328              //$opt['v']
11329              //$opt['dv']
11330              //$opt['a']
11331              //$opt['aa']
11332              //$opt['q']
11333              */
11334              $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
11335              if ($this->rtl) {
11336                  $this->x -= $w;
11337              } else {
11338                  $this->x += $w;
11339              }
11340          }
11341  
11342          /**
11343           * Creates a RadioButton field
11344           * @param string $name field name
11345           * @param int $w width
11346           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11347           * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
11348           * @param string $onvalue value to be returned if selected.
11349           * @param boolean $checked define the initial state.
11350           * @param float $x Abscissa of the upper-left corner of the rectangle
11351           * @param float $y Ordinate of the upper-left corner of the rectangle
11352           * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11353           * @access public
11354           * @author Nicola Asuni
11355           * @since 4.8.000 (2009-09-07)
11356           */
11357  		public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
11358              if ($x === '') {
11359                  $x = $this->x;
11360              }
11361              if ($y === '') {
11362                  $y = $this->y;
11363              }
11364              if ($js) {
11365                  $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
11366                  return;
11367              }
11368              if ($this->empty_string($onvalue)) {
11369                  $onvalue = 'On';
11370              }
11371              if ($checked) {
11372                  $defval = $onvalue;
11373              } else {
11374                  $defval = 'Off';
11375              }
11376              // set data for parent group
11377              if (!isset($this->radiobutton_groups[$this->page])) {
11378                  $this->radiobutton_groups[$this->page] = array();
11379              }
11380              if (!isset($this->radiobutton_groups[$this->page][$name])) {
11381                  $this->radiobutton_groups[$this->page][$name] = array();
11382                  ++$this->annot_obj_id;
11383                  $this->radio_groups[] = $this->annot_obj_id;
11384              }
11385              // save object ID to be added on Kids entry on parent object
11386              $this->radiobutton_groups[$this->page][$name][] = array('kid' => ($this->annot_obj_id + 1), 'def' => $defval);
11387              // get default style
11388              $prop = array_merge($this->getFormDefaultProp(), $prop);
11389              $prop['NoToggleToOff'] = 'true';
11390              $prop['Radio'] = 'true';
11391              $prop['borderStyle'] = 'inset';
11392              // get annotation data
11393              $popt = $this->getAnnotOptFromJSProp($prop);
11394              // set additional default values
11395              $font = 'zapfdingbats';
11396              $this->AddFont($font);
11397              $fontkey = array_search($font, $this->fontkeys);
11398              if (!in_array($fontkey, $this->annotation_fonts)) {
11399                  $this->annotation_fonts[$font] = $fontkey;
11400              }
11401              $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
11402              $popt['da'] = $fontstyle;
11403              $popt['ap'] = array();
11404              $popt['ap']['n'] = array();
11405              $popt['ap']['n'][$onvalue] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
11406              $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
11407              if (!isset($popt['mk'])) {
11408                  $popt['mk'] = array();
11409              }
11410              $popt['mk']['ca'] = '(l)';
11411              // merge options
11412              $opt = array_merge($popt, $opt);
11413              // set remaining annotation data
11414              $opt['Subtype'] = 'Widget';
11415              $opt['ft'] = 'Btn';
11416              if ($checked) {
11417                  $opt['v'] = array('/'.$onvalue);
11418                  $opt['as'] = $onvalue;
11419              } else {
11420                  $opt['as'] = 'Off';
11421              }
11422              $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
11423              if ($this->rtl) {
11424                  $this->x -= $w;
11425              } else {
11426                  $this->x += $w;
11427              }
11428          }
11429  
11430          /**
11431           * Creates a List-box field
11432           * @param string $name field name
11433           * @param int $w width
11434           * @param int $h height
11435           * @param array $values array containing the list of values.
11436           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11437           * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
11438           * @param float $x Abscissa of the upper-left corner of the rectangle
11439           * @param float $y Ordinate of the upper-left corner of the rectangle
11440           * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11441           * @access public
11442           * @author Nicola Asuni
11443           * @since 4.8.000 (2009-09-07)
11444           */
11445  		public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
11446              if ($x === '') {
11447                  $x = $this->x;
11448              }
11449              if ($y === '') {
11450                  $y = $this->y;
11451              }
11452              if ($js) {
11453                  $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
11454                  $s = '';
11455                  foreach ($values as $value) {
11456                      $s .= "'".addslashes($value)."',";
11457                  }
11458                  $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
11459                  return;
11460              }
11461              // get default style
11462              $prop = array_merge($this->getFormDefaultProp(), $prop);
11463              // get annotation data
11464              $popt = $this->getAnnotOptFromJSProp($prop);
11465              // set additional default values
11466              $font = $this->FontFamily;
11467              $fontkey = array_search($font, $this->fontkeys);
11468              if (!in_array($fontkey, $this->annotation_fonts)) {
11469                  $this->annotation_fonts[$font] = $fontkey;
11470              }
11471              $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
11472              $popt['da'] = $fontstyle;
11473              $popt['ap'] = array();
11474              $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
11475              // merge options
11476              $opt = array_merge($popt, $opt);
11477              // set remaining annotation data
11478              $opt['Subtype'] = 'Widget';
11479              $opt['ft'] = 'Ch';
11480              $opt['t'] = $name;
11481              $opt['opt'] = $values;
11482              $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
11483              if ($this->rtl) {
11484                  $this->x -= $w;
11485              } else {
11486                  $this->x += $w;
11487              }
11488          }
11489  
11490          /**
11491           * Creates a Combo-box field
11492           * @param string $name field name
11493           * @param int $w width
11494           * @param int $h height
11495           * @param array $values array containing the list of values.
11496           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11497           * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
11498           * @param float $x Abscissa of the upper-left corner of the rectangle
11499           * @param float $y Ordinate of the upper-left corner of the rectangle
11500           * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11501           * @access public
11502           * @author Nicola Asuni
11503           * @since 4.8.000 (2009-09-07)
11504           */
11505  		public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
11506              if ($x === '') {
11507                  $x = $this->x;
11508              }
11509              if ($y === '') {
11510                  $y = $this->y;
11511              }
11512              if ($js) {
11513                  $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
11514                  $s = '';
11515                  foreach ($values as $value) {
11516                      $s .= "'".addslashes($value)."',";
11517                  }
11518                  $this->javascript .= 'f'.$name.'.setItems(['.substr($s, 0, -1)."]);\n";
11519                  return;
11520              }
11521              // get default style
11522              $prop = array_merge($this->getFormDefaultProp(), $prop);
11523              $prop['Combo'] = true;
11524              // get annotation data
11525              $popt = $this->getAnnotOptFromJSProp($prop);
11526              // set additional default options
11527              $font = $this->FontFamily;
11528              $fontkey = array_search($font, $this->fontkeys);
11529              if (!in_array($fontkey, $this->annotation_fonts)) {
11530                  $this->annotation_fonts[$font] = $fontkey;
11531              }
11532              $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
11533              $popt['da'] = $fontstyle;
11534              $popt['ap'] = array();
11535              $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
11536              // merge options
11537              $opt = array_merge($popt, $opt);
11538              // set remaining annotation data
11539              $opt['Subtype'] = 'Widget';
11540              $opt['ft'] = 'Ch';
11541              $opt['t'] = $name;
11542              $opt['opt'] = $values;
11543              $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
11544              if ($this->rtl) {
11545                  $this->x -= $w;
11546              } else {
11547                  $this->x += $w;
11548              }
11549          }
11550  
11551          /**
11552           * Creates a CheckBox field
11553           * @param string $name field name
11554           * @param int $w width
11555           * @param boolean $checked define the initial state.
11556           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11557           * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
11558           * @param string $onvalue value to be returned if selected.
11559           * @param float $x Abscissa of the upper-left corner of the rectangle
11560           * @param float $y Ordinate of the upper-left corner of the rectangle
11561           * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11562           * @access public
11563           * @author Nicola Asuni
11564           * @since 4.8.000 (2009-09-07)
11565           */
11566  		public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
11567              if ($x === '') {
11568                  $x = $this->x;
11569              }
11570              if ($y === '') {
11571                  $y = $this->y;
11572              }
11573              if ($js) {
11574                  $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
11575                  return;
11576              }
11577              if (!isset($prop['value'])) {
11578                  $prop['value'] = array('Yes');
11579              }
11580              // get default style
11581              $prop = array_merge($this->getFormDefaultProp(), $prop);
11582              $prop['borderStyle'] = 'inset';
11583              // get annotation data
11584              $popt = $this->getAnnotOptFromJSProp($prop);
11585              // set additional default options
11586              $font = 'zapfdingbats';
11587              $this->AddFont($font);
11588              $fontkey = array_search($font, $this->fontkeys);
11589              if (!in_array($fontkey, $this->annotation_fonts)) {
11590                  $this->annotation_fonts[$font] = $fontkey;
11591              }
11592              $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
11593              $popt['da'] = $fontstyle;
11594              $popt['ap'] = array();
11595              $popt['ap']['n'] = array();
11596              $popt['ap']['n']['Yes'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
11597              $popt['ap']['n']['Off'] = 'q BT '.$fontstyle.' 0 0 Td (8) Tj ET Q';
11598              // merge options
11599              $opt = array_merge($popt, $opt);
11600              // set remaining annotation data
11601              $opt['Subtype'] = 'Widget';
11602              $opt['ft'] = 'Btn';
11603              $opt['t'] = $name;
11604              $opt['opt'] = array($onvalue);
11605              if ($checked) {
11606                  $opt['v'] = array('/0');
11607                  $opt['as'] = 'Yes';
11608              } else {
11609                  $opt['v'] = array('/Off');
11610                  $opt['as'] = 'Off';
11611              }
11612              $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
11613              if ($this->rtl) {
11614                  $this->x -= $w;
11615              } else {
11616                  $this->x += $w;
11617              }
11618          }
11619  
11620          /**
11621           * Creates a button field
11622           * @param string $name field name
11623           * @param int $w width
11624           * @param int $h height
11625           * @param string $caption caption.
11626           * @param mixed $action action triggered by pressing the button. Use a string to specify a javascript action. Use an array to specify a form action options as on section 12.7.5 of PDF32000_2008.
11627           * @param array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
11628           * @param array $opt annotation parameters. Possible values are described on official PDF32000_2008 reference.
11629           * @param float $x Abscissa of the upper-left corner of the rectangle
11630           * @param float $y Ordinate of the upper-left corner of the rectangle
11631           * @param boolean $js if true put the field using JavaScript (requires Acrobat Writer to be rendered).
11632           * @access public
11633           * @author Nicola Asuni
11634           * @since 4.8.000 (2009-09-07)
11635           */
11636  		public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
11637              if ($x === '') {
11638                  $x = $this->x;
11639              }
11640              if ($y === '') {
11641                  $y = $this->y;
11642              }
11643              if ($js) {
11644                  $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
11645                  $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
11646                  $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
11647                  $this->javascript .= 'f'.$name.".highlight='push';\n";
11648                  $this->javascript .= 'f'.$name.".print=false;\n";
11649                  return;
11650              }
11651              // get default style
11652              $prop = array_merge($this->getFormDefaultProp(), $prop);
11653              $prop['Pushbutton'] = 'true';
11654              $prop['highlight'] = 'push';
11655              $prop['display'] = 'display.noPrint';
11656              // get annotation data
11657              $popt = $this->getAnnotOptFromJSProp($prop);
11658              // set additional default options
11659              if (!isset($popt['mk'])) {
11660                  $popt['mk'] = array();
11661              }
11662              $popt['mk']['ca'] = $this->_textstring($caption);
11663              $popt['mk']['rc'] = $this->_textstring($caption);
11664              $popt['mk']['ac'] = $this->_textstring($caption);
11665              $font = $this->FontFamily;
11666              $fontkey = array_search($font, $this->fontkeys);
11667              if (!in_array($fontkey, $this->annotation_fonts)) {
11668                  $this->annotation_fonts[$font] = $fontkey;
11669              }
11670              $fontstyle = sprintf('/F%d %.2F Tf %s', ($fontkey + 1), $this->FontSizePt, $this->TextColor);
11671              $popt['da'] = $fontstyle;
11672              $popt['ap'] = array();
11673              $popt['ap']['n'] = 'q BT '.$fontstyle.' ET Q';
11674              // merge options
11675              $opt = array_merge($popt, $opt);
11676              // set remaining annotation data
11677              $opt['Subtype'] = 'Widget';
11678              $opt['ft'] = 'Btn';
11679              $opt['t'] = $caption;
11680              $opt['v'] = $name;
11681              if (!empty($action)) {
11682                  if (is_array($action)) {
11683                      // form action options as on section 12.7.5 of PDF32000_2008.
11684                      $opt['aa'] = '/D <<';
11685                      $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
11686                      foreach ($action AS $key => $val) {
11687                          if (($key == 'S') AND in_array($val, $bmode)) {
11688                              $opt['aa'] .= ' /S /'.$val;
11689                          } elseif (($key == 'F') AND (!empty($val))) {
11690                              $opt['aa'] .= ' /F '.$this->_datastring($val);
11691                          } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
11692                              $opt['aa'] .= ' /Fields [';
11693                              foreach ($val AS $field) {
11694                                  $opt['aa'] .= ' '.$this->_textstring($field);
11695                              }
11696                              $opt['aa'] .= ']';
11697                          } elseif (($key == 'Flags')) {
11698                              $ff = 0;
11699                              if (is_array($val)) {
11700                                  foreach ($val AS $flag) {
11701                                      switch ($flag) {
11702                                          case 'Include/Exclude': {
11703                                              $ff += 1 << 0;
11704                                              break;
11705                                          }
11706                                          case 'IncludeNoValueFields': {
11707                                              $ff += 1 << 1;
11708                                              break;
11709                                          }
11710                                          case 'ExportFormat': {
11711                                              $ff += 1 << 2;
11712                                              break;
11713                                          }
11714                                          case 'GetMethod': {
11715                                              $ff += 1 << 3;
11716                                              break;
11717                                          }
11718                                          case 'SubmitCoordinates': {
11719                                              $ff += 1 << 4;
11720                                              break;
11721                                          }
11722                                          case 'XFDF': {
11723                                              $ff += 1 << 5;
11724                                              break;
11725                                          }
11726                                          case 'IncludeAppendSaves': {
11727                                              $ff += 1 << 6;
11728                                              break;
11729                                          }
11730                                          case 'IncludeAnnotations': {
11731                                              $ff += 1 << 7;
11732                                              break;
11733                                          }
11734                                          case 'SubmitPDF': {
11735                                              $ff += 1 << 8;
11736                                              break;
11737                                          }
11738                                          case 'CanonicalFormat': {
11739                                              $ff += 1 << 9;
11740                                              break;
11741                                          }
11742                                          case 'ExclNonUserAnnots': {
11743                                              $ff += 1 << 10;
11744                                              break;
11745                                          }
11746                                          case 'ExclFKey': {
11747                                              $ff += 1 << 11;
11748                                              break;
11749                                          }
11750                                          case 'EmbedForm': {
11751                                              $ff += 1 << 13;
11752                                              break;
11753                                          }
11754                                      }
11755                                  }
11756                              } else {
11757                                  $ff = intval($val);
11758                              }
11759                              $opt['aa'] .= ' /Flags '.$ff;
11760                          }
11761                      }
11762                      $opt['aa'] .= ' >>';
11763                  } else {
11764                      // Javascript action or raw action command
11765                      $js_obj_id = $this->addJavascriptObject($action);
11766                      $opt['aa'] = '/D '.$js_obj_id.' 0 R';
11767                  }
11768              }
11769              $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
11770              if ($this->rtl) {
11771                  $this->x -= $w;
11772              } else {
11773                  $this->x += $w;
11774              }
11775          }
11776  
11777          // --- END FORMS FIELDS ------------------------------------------------
11778  
11779          /**
11780           * Add certification signature (DocMDP or UR3)
11781           * You can set only one signature type
11782           * @access protected
11783           * @author Nicola Asuni
11784           * @since 4.6.008 (2009-05-07)
11785           */
11786  		protected function _putsignature() {
11787              if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
11788                  return;
11789              }
11790              $this->_newobj();
11791              $out = ' << /Type /Sig';
11792              $out .= ' /Filter /Adobe.PPKLite';
11793              $out .= ' /SubFilter /adbe.pkcs7.detached';
11794              $out .= ' '.$this->byterange_string;
11795              $out .= ' /Contents<>'.str_repeat(' ', $this->signature_max_length);
11796              $out .= ' /Reference';
11797              $out .= ' [';
11798              $out .= ' << /Type /SigRef';
11799              if ($this->signature_data['cert_type'] > 0) {
11800                  $out .= ' /TransformMethod /DocMDP';
11801                  $out .= ' /TransformParams';
11802                  $out .= ' <<';
11803                  $out .= ' /Type /TransformParams';
11804                  $out .= ' /V /1.2';
11805                  $out .= ' /P '.$this->signature_data['cert_type'];
11806              } else {
11807                  $out .= ' /TransformMethod /UR3';
11808                  $out .= ' /TransformParams';
11809                  $out .= ' << /Type /TransformParams';
11810                  $out .= ' /V /2.2';
11811                  if (!$this->empty_string($this->ur_document)) {
11812                      $out .= ' /Document['.$this->ur_document.']';
11813                  }
11814                  if (!$this->empty_string($this->ur_annots)) {
11815                      $out .= ' /Annots['.$this->ur_annots.']';
11816                  }
11817                  if (!$this->empty_string($this->ur_form)) {
11818                      $out .= ' /Form['.$this->ur_form.']';
11819                  }
11820                  if (!$this->empty_string($this->ur_signature)) {
11821                      $out .= ' /Signature['.$this->ur_signature.']';
11822                  }
11823              }
11824              $out .= ' >> >> ]';
11825              if (isset($this->signature_data['info']['Name']) AND !$this->empty_string($this->signature_data['info']['Name'])) {
11826                  $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name']);
11827              }
11828              if (isset($this->signature_data['info']['Location']) AND !$this->empty_string($this->signature_data['info']['Location'])) {
11829                  $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location']);
11830              }
11831              if (isset($this->signature_data['info']['Reason']) AND !$this->empty_string($this->signature_data['info']['Reason'])) {
11832                  $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason']);
11833              }
11834              if (isset($this->signature_data['info']['ContactInfo']) AND !$this->empty_string($this->signature_data['info']['ContactInfo'])) {
11835                  $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo']);
11836              }
11837              $out .= ' /M '.$this->_datestring();
11838              $out .= ' >> endobj';
11839              $this->_out($out);
11840          }
11841  
11842          /**
11843           * Set User's Rights for PDF Reader
11844           * WARNING: This works only using the Adobe private key with the setSignature() method!.
11845           * Check the PDF Reference 8.7.1 Transform Methods,
11846           * Table 8.105 Entries in the UR transform parameters dictionary
11847           * @param boolean $enable if true enable user's rights on PDF reader
11848           * @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.
11849           * @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.
11850           * @param string $form Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
11851           * @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.
11852           * @access public
11853           * @author Nicola Asuni
11854           * @since 2.9.000 (2008-03-26)
11855           */
11856  		public function setUserRights(
11857                  $enable=true,
11858                  $document='/FullSave',
11859                  $annots='/Create/Delete/Modify/Copy/Import/Export',
11860                  $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
11861                  $signature='/Modify') {
11862              $this->ur = $enable;
11863              $this->ur_document = $document;
11864              $this->ur_annots = $annots;
11865              $this->ur_form = $form;
11866              $this->ur_signature = $signature;
11867              if (!$this->sign) {
11868                  // This signature only works using the Adobe Private key that is unavailable!
11869                  $this->setSignature('', '', '', '', 0, array());
11870              }
11871          }
11872  
11873          /**
11874           * Enable document signature (requires the OpenSSL Library).
11875           * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
11876           * @param mixed $signing_cert signing certificate (string or filename prefixed with 'file://')
11877           * @param mixed $private_key private key (string or filename prefixed with 'file://')
11878           * @param string $private_key_password password
11879           * @param string $extracerts specifies the name of a file containing a bunch of extra certificates to include in the signature which can for example be used to help the recipient to verify the certificate that you used.
11880           * @param int $cert_type The access permissions granted for this document. Valid values shall be: 1 = No changes to the document shall be permitted; any change to the document shall invalidate the signature; 2 = Permitted changes shall be filling in forms, instantiating page templates, and signing; other changes shall invalidate the signature; 3 = Permitted changes shall be the same as for 2, as well as annotation creation, deletion, and modification; other changes shall invalidate the signature.
11881           * @param array $info array of option information: Name, Location, Reason, ContactInfo.
11882           * @access public
11883           * @author Nicola Asuni
11884           * @since 4.6.005 (2009-04-24)
11885           */
11886  		public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array()) {
11887              // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
11888              // to convert pfx certificate to pem: openssl
11889              //     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
11890              $this->sign = true;
11891              $this->signature_data = array();
11892              if (strlen($signing_cert) == 0) {
11893                  $signing_cert = 'file://'.dirname(__FILE__).'/tcpdf.crt';
11894                  $private_key_password = 'tcpdfdemo';
11895              }
11896              if (strlen($private_key) == 0) {
11897                  $private_key = $signing_cert;
11898              }
11899              $this->signature_data['signcert'] = $signing_cert;
11900              $this->signature_data['privkey'] = $private_key;
11901              $this->signature_data['password'] = $private_key_password;
11902              $this->signature_data['extracerts'] = $extracerts;
11903              $this->signature_data['cert_type'] = $cert_type;
11904              $this->signature_data['info'] = $info;
11905          }
11906  
11907          /**
11908           * Create a new page group.
11909           * NOTE: call this function before calling AddPage()
11910           * @param int $page starting group page (leave empty for next page).
11911           * @access public
11912           * @since 3.0.000 (2008-03-27)
11913           */
11914  		public function startPageGroup($page='') {
11915              if (empty($page)) {
11916                  $page = $this->page + 1;
11917              }
11918              $this->newpagegroup[$page] = true;
11919          }
11920  
11921          /**
11922           * Defines an alias for the total number of pages.
11923           * It will be substituted as the document is closed.
11924           * @param string $alias The alias.
11925           * @access public
11926           * @since 1.4
11927           * @see getAliasNbPages(), PageNo(), Footer()
11928           */
11929  		public function AliasNbPages($alias='{nb}') {
11930              $this->AliasNbPages = $alias;
11931          }
11932  
11933          /**
11934           * Returns the string alias used for the total number of pages.
11935           * If the current font is unicode type, the returned string is surrounded by additional curly braces.
11936           * @return string
11937           * @access public
11938           * @since 4.0.018 (2008-08-08)
11939           * @see AliasNbPages(), PageNo(), Footer()
11940           */
11941  		public function getAliasNbPages() {
11942              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11943                  return '{'.$this->AliasNbPages.'}';
11944              }
11945              return $this->AliasNbPages;
11946          }
11947  
11948          /**
11949           * Defines an alias for the page number.
11950           * It will be substituted as the document is closed.
11951           * @param string $alias The alias.
11952           * @access public
11953           * @since 4.5.000 (2009-01-02)
11954           * @see getAliasNbPages(), PageNo(), Footer()
11955           */
11956  		public function AliasNumPage($alias='{pnb}') {
11957              //Define an alias for total number of pages
11958              $this->AliasNumPage = $alias;
11959          }
11960  
11961          /**
11962           * Returns the string alias used for the page number.
11963           * If the current font is unicode type, the returned string is surrounded by additional curly braces.
11964           * @return string
11965           * @access public
11966           * @since 4.5.000 (2009-01-02)
11967           * @see AliasNbPages(), PageNo(), Footer()
11968           */
11969  		public function getAliasNumPage() {
11970              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
11971                  return '{'.$this->AliasNumPage.'}';
11972              }
11973              return $this->AliasNumPage;
11974          }
11975  
11976          /**
11977           * Return the current page in the group.
11978           * @return current page in the group
11979           * @access public
11980           * @since 3.0.000 (2008-03-27)
11981           */
11982  		public function getGroupPageNo() {
11983              return $this->pagegroups[$this->currpagegroup];
11984          }
11985  
11986          /**
11987           * Returns the current group page number formatted as a string.
11988           * @access public
11989           * @since 4.3.003 (2008-11-18)
11990           * @see PaneNo(), formatPageNumber()
11991           */
11992  		public function getGroupPageNoFormatted() {
11993              return $this->formatPageNumber($this->getGroupPageNo());
11994          }
11995  
11996          /**
11997           * Return the alias of the current page group
11998           * If the current font is unicode type, the returned string is surrounded by additional curly braces.
11999           * (will be replaced by the total number of pages in this group).
12000           * @return alias of the current page group
12001           * @access public
12002           * @since 3.0.000 (2008-03-27)
12003           */
12004  		public function getPageGroupAlias() {
12005              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
12006                  return '{'.$this->currpagegroup.'}';
12007              }
12008              return $this->currpagegroup;
12009          }
12010  
12011          /**
12012           * Return the alias for the page number on the current page group
12013           * If the current font is unicode type, the returned string is surrounded by additional curly braces.
12014           * (will be replaced by the total number of pages in this group).
12015           * @return alias of the current page group
12016           * @access public
12017           * @since 4.5.000 (2009-01-02)
12018           */
12019  		public function getPageNumGroupAlias() {
12020              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
12021                  return '{'.str_replace('{nb', '{pnb', $this->currpagegroup).'}';
12022              }
12023              return str_replace('{nb', '{pnb', $this->currpagegroup);
12024          }
12025  
12026          /**
12027           * Format the page numbers.
12028           * This method can be overriden for custom formats.
12029           * @param int $num page number
12030           * @access protected
12031           * @since 4.2.005 (2008-11-06)
12032           */
12033  		protected function formatPageNumber($num) {
12034              return number_format((float)$num, 0, '', '.');
12035          }
12036  
12037          /**
12038           * Format the page numbers on the Table Of Content.
12039           * This method can be overriden for custom formats.
12040           * @param int $num page number
12041           * @access protected
12042           * @since 4.5.001 (2009-01-04)
12043           * @see addTOC(), addHTMLTOC()
12044           */
12045  		protected function formatTOCPageNumber($num) {
12046              return number_format((float)$num, 0, '', '.');
12047          }
12048  
12049          /**
12050           * Returns the current page number formatted as a string.
12051           * @access public
12052           * @since 4.2.005 (2008-11-06)
12053           * @see PaneNo(), formatPageNumber()
12054           */
12055  		public function PageNoFormatted() {
12056              return $this->formatPageNumber($this->PageNo());
12057          }
12058  
12059          /**
12060           * Put visibility settings.
12061           * @access protected
12062           * @since 3.0.000 (2008-03-27)
12063           */
12064  		protected function _putocg() {
12065              $this->_newobj();
12066              $this->n_ocg_print = $this->n;
12067              $this->_out('<< /Type /OCG /Name '.$this->_textstring('print').' /Usage << /Print <</PrintState /ON>> /View <</ViewState /OFF>> >> >> endobj');
12068              $this->_newobj();
12069              $this->n_ocg_view = $this->n;
12070              $this->_out('<< /Type /OCG /Name '.$this->_textstring('view').' /Usage << /Print <</PrintState /OFF>> /View <</ViewState /ON>> >> >> endobj');
12071          }
12072  
12073          /**
12074           * Set the visibility of the successive elements.
12075           * This can be useful, for instance, to put a background
12076           * image or color that will show on screen but won't print.
12077           * @param string $v visibility mode. Legal values are: all, print, screen.
12078           * @access public
12079           * @since 3.0.000 (2008-03-27)
12080           */
12081  		public function setVisibility($v) {
12082              if ($this->openMarkedContent) {
12083                  // close existing open marked-content
12084                  $this->_out('EMC');
12085                  $this->openMarkedContent = false;
12086              }
12087              switch($v) {
12088                  case 'print': {
12089                      $this->_out('/OC /OC1 BDC');
12090                      $this->openMarkedContent = true;
12091                      break;
12092                  }
12093                  case 'screen': {
12094                      $this->_out('/OC /OC2 BDC');
12095                      $this->openMarkedContent = true;
12096                      break;
12097                  }
12098                  case 'all': {
12099                      $this->_out('');
12100                      break;
12101                  }
12102                  default: {
12103                      $this->Error('Incorrect visibility: '.$v);
12104                      break;
12105                  }
12106              }
12107              $this->visibility = $v;
12108          }
12109  
12110          /**
12111           * Add transparency parameters to the current extgstate
12112           * @param array $params parameters
12113           * @return the number of extgstates
12114           * @access protected
12115           * @since 3.0.000 (2008-03-27)
12116           */
12117  		protected function addExtGState($parms) {
12118              $n = count($this->extgstates) + 1;
12119              // check if this ExtGState already exist
12120              for ($i = 1; $i < $n; ++$i) {
12121                  if ($this->extgstates[$i]['parms'] == $parms) {
12122                      // return reference to existing ExtGState
12123                      return $i;
12124                  }
12125              }
12126              $this->extgstates[$n]['parms'] = $parms;
12127              return $n;
12128          }
12129  
12130          /**
12131           * Add an extgstate
12132           * @param array $gs extgstate
12133           * @access protected
12134           * @since 3.0.000 (2008-03-27)
12135           */
12136  		protected function setExtGState($gs) {
12137              $this->_out(sprintf('/GS%d gs', $gs));
12138          }
12139  
12140          /**
12141           * Put extgstates for object transparency
12142           * @param array $gs extgstate
12143           * @access protected
12144           * @since 3.0.000 (2008-03-27)
12145           */
12146  		protected function _putextgstates() {
12147              $ne = count($this->extgstates);
12148              for ($i = 1; $i <= $ne; ++$i) {
12149                  $this->_newobj();
12150                  $this->extgstates[$i]['n'] = $this->n;
12151                  $out = '<< /Type /ExtGState';
12152                  foreach ($this->extgstates[$i]['parms'] as $k => $v) {
12153                      if (is_float($v)) {
12154                          $v = sprintf('%.2F', $v);
12155                      }
12156                      $out .= ' /'.$k.' '.$v;
12157                  }
12158                  $out .= ' >> endobj';
12159                  $this->_out($out);
12160              }
12161          }
12162  
12163          /**
12164           * Set alpha for stroking (CA) and non-stroking (ca) operations.
12165           * @param float $alpha real value from 0 (transparent) to 1 (opaque)
12166           * @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
12167           * @access public
12168           * @since 3.0.000 (2008-03-27)
12169           */
12170  		public function setAlpha($alpha, $bm='Normal') {
12171              $gs = $this->addExtGState(array('ca' => $alpha, 'CA' => $alpha, 'BM' => '/'.$bm, 'AIS' => 'false'));
12172              $this->setExtGState($gs);
12173          }
12174  
12175          /**
12176           * Set the default JPEG compression quality (1-100)
12177           * @param int $quality JPEG quality, integer between 1 and 100
12178           * @access public
12179           * @since 3.0.000 (2008-03-27)
12180           */
12181  		public function setJPEGQuality($quality) {
12182              if (($quality < 1) OR ($quality > 100)) {
12183                  $quality = 75;
12184              }
12185              $this->jpeg_quality = intval($quality);
12186          }
12187  
12188          /**
12189           * Set the default number of columns in a row for HTML tables.
12190           * @param int $cols number of columns
12191           * @access public
12192           * @since 3.0.014 (2008-06-04)
12193           */
12194  		public function setDefaultTableColumns($cols=4) {
12195              $this->default_table_columns = intval($cols);
12196          }
12197  
12198          /**
12199           * Set the height of the cell (line height) respect the font height.
12200           * @param int $h cell proportion respect font height (typical value = 1.25).
12201           * @access public
12202           * @since 3.0.014 (2008-06-04)
12203           */
12204  		public function setCellHeightRatio($h) {
12205              $this->cell_height_ratio = $h;
12206          }
12207  
12208          /**
12209           * return the height of cell repect font height.
12210           * @access public
12211           * @since 4.0.012 (2008-07-24)
12212           */
12213  		public function getCellHeightRatio() {
12214              return $this->cell_height_ratio;
12215          }
12216  
12217          /**
12218           * Set the PDF version (check PDF reference for valid values).
12219           * Default value is 1.t
12220           * @access public
12221           * @since 3.1.000 (2008-06-09)
12222           */
12223  		public function setPDFVersion($version='1.7') {
12224              $this->PDFVersion = $version;
12225          }
12226  
12227          /**
12228           * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
12229           * (see Section 8.1 of PDF reference, "Viewer Preferences").
12230           * <ul><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><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><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><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><li>CenterWindow boolean (Optional) A flag specifying whether to position the document's window in the center of the screen. Default value: false.</li><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><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><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><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><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><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><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><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><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><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><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></ul>
12231           * @param array $preferences array of options.
12232           * @author Nicola Asuni
12233           * @access public
12234           * @since 3.1.000 (2008-06-09)
12235           */
12236  		public function setViewerPreferences($preferences) {
12237              $this->viewer_preferences = $preferences;
12238          }
12239  
12240          /**
12241           * Paints color transition registration bars
12242           * @param float $x abscissa of the top left corner of the rectangle.
12243           * @param float $y ordinate of the top left corner of the rectangle.
12244           * @param float $w width of the rectangle.
12245           * @param float $h height of the rectangle.
12246           * @param boolean $transition if true prints tcolor transitions to white.
12247           * @param boolean $vertical if true prints bar vertically.
12248           * @param string $colors colors to print, one letter per color separated by comma (for example 'A,W,R,G,B,C,M,Y,K'): A=black, W=white, R=red, G=green, B=blue, C=cyan, M=magenta, Y=yellow, K=black.
12249           * @author Nicola Asuni
12250           * @since 4.9.000 (2010-03-26)
12251           * @access public
12252           */
12253  		public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
12254              $bars = explode(',', $colors);
12255              $numbars = count($bars); // number of bars to print
12256              // set bar measures
12257              if ($vertical) {
12258                  $coords = array(0, 0, 0, 1);
12259                  $wb = $w / $numbars; // bar width
12260                  $hb = $h; // bar height
12261                  $xd = $wb; // delta x
12262                  $yd = 0; // delta y
12263              } else {
12264                  $coords = array(1, 0, 0, 0);
12265                  $wb = $w; // bar width
12266                  $hb = $h / $numbars; // bar height
12267                  $xd = 0; // delta x
12268                  $yd = $hb; // delta y
12269              }
12270              $xb = $x;
12271              $yb = $y;
12272              foreach ($bars as $col) {
12273                  switch ($col) {
12274                      // set transition colors
12275                      case 'A': { // BLACK
12276                          $col_a = array(255);
12277                          $col_b = array(0);
12278                          break;
12279                      }
12280                      case 'W': { // WHITE
12281                          $col_a = array(0);
12282                          $col_b = array(255);
12283                          break;
12284                      }
12285                      case 'R': { // R
12286                          $col_a = array(255,255,255);
12287                          $col_b = array(255,0,0);
12288                          break;
12289                      }
12290                      case 'G': { // G
12291                          $col_a = array(255,255,255);
12292                          $col_b = array(0,255,0);
12293                          break;
12294                      }
12295                      case 'B': { // B
12296                          $col_a = array(255,255,255);
12297                          $col_b = array(0,0,255);
12298                          break;
12299                      }
12300                      case 'C': { // C
12301                          $col_a = array(0,0,0,0);
12302                          $col_b = array(100,0,0,0);
12303                          break;
12304                      }
12305                      case 'M': { // M
12306                          $col_a = array(0,0,0,0);
12307                          $col_b = array(0,100,0,0);
12308                          break;
12309                      }
12310                      case 'Y': { // Y
12311                          $col_a = array(0,0,0,0);
12312                          $col_b = array(0,0,100,0);
12313                          break;
12314                      }
12315                      case 'K': { // K
12316                          $col_a = array(0,0,0,0);
12317                          $col_b = array(0,0,0,100);
12318                          break;
12319                      }
12320                      default: { // GRAY
12321                          $col_a = array(255);
12322                          $col_b = array(0);
12323                          break;
12324                      }
12325                  }
12326                  if ($transition) {
12327                      // color gradient
12328                      $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
12329                  } else {
12330                      // color rectangle
12331                      $this->SetFillColorArray($col_b);
12332                      $this->Rect($xb, $yb, $wb, $hb, 'F', array());
12333                  }
12334                  $xb += $xd;
12335                  $yb += $yd;
12336              }
12337          }
12338  
12339          /**
12340           * Paints crop mark
12341           * @param float $x abscissa of the crop mark center.
12342           * @param float $y ordinate of the crop mark center.
12343           * @param float $w width of the crop mark.
12344           * @param float $h height of the crop mark.
12345           * @param string $type type of crop mark, one sybol per type separated by comma: A = top left, B = top right, C = bottom left, D = bottom right.
12346           * @param array $color crop mark color (default black).
12347           * @author Nicola Asuni
12348           * @since 4.9.000 (2010-03-26)
12349           * @access public
12350           */
12351  		public function cropMark($x, $y, $w, $h, $type='A,B,C,D', $color=array(0,0,0)) {
12352              $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
12353              $crops = explode(',', $type);
12354              $numcrops = count($crops); // number of crop marks to print
12355              $dw = $w / 4; // horizontal space to leave before the intersection point
12356              $dh = $h / 4; // vertical space to leave before the intersection point
12357              foreach ($crops as $crop) {
12358                  switch ($crop) {
12359                      case 'A': {
12360                          $x1 = $x;
12361                          $y1 = $y - $h;
12362                          $x2 = $x;
12363                          $y2 = $y - $dh;
12364                          $x3 = $x - $w;
12365                          $y3 = $y;
12366                          $x4 = $x - $dw;
12367                          $y4 = $y;
12368                          break;
12369                      }
12370                      case 'B': {
12371                          $x1 = $x;
12372                          $y1 = $y - $h;
12373                          $x2 = $x;
12374                          $y2 = $y - $dh;
12375                          $x3 = $x + $dw;
12376                          $y3 = $y;
12377                          $x4 = $x + $w;
12378                          $y4 = $y;
12379                          break;
12380                      }
12381                      case 'C': {
12382                          $x1 = $x - $w;
12383                          $y1 = $y;
12384                          $x2 = $x - $dw;
12385                          $y2 = $y;
12386                          $x3 = $x;
12387                          $y3 = $y + $dh;
12388                          $x4 = $x;
12389                          $y4 = $y + $h;
12390                          break;
12391                      }
12392                      case 'D': {
12393                          $x1 = $x + $dw;
12394                          $y1 = $y;
12395                          $x2 = $x + $w;
12396                          $y2 = $y;
12397                          $x3 = $x;
12398                          $y3 = $y + $dh;
12399                          $x4 = $x;
12400                          $y4 = $y + $h;
12401                          break;
12402                      }
12403                  }
12404                  $this->Line($x1, $y1, $x2, $y2);
12405                  $this->Line($x3, $y3, $x4, $y4);
12406              }
12407          }
12408  
12409          /**
12410           * Paints a registration mark
12411           * @param float $x abscissa of the registration mark center.
12412           * @param float $y ordinate of the registration mark center.
12413           * @param float $r radius of the crop mark.
12414           * @param boolean $double if true print two concentric crop marks.
12415           * @param array $cola crop mark color (default black).
12416           * @param array $colb second crop mark color.
12417           * @author Nicola Asuni
12418           * @since 4.9.000 (2010-03-26)
12419           * @access public
12420           */
12421  		public function registrationMark($x, $y, $r, $double=false, $cola=array(0,0,0), $colb=array(255,255,255)) {
12422              $line_style = array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
12423              $this->SetFillColorArray($cola);
12424              $this->PieSector($x, $y, $r, 90, 180, 'F');
12425              $this->PieSector($x, $y, $r, 270, 360, 'F');
12426              $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
12427              if ($double) {
12428                  $r2 = $r * 0.5;
12429                  $this->SetFillColorArray($colb);
12430                  $this->PieSector($x, $y, $r2, 90, 180, 'F');
12431                  $this->PieSector($x, $y, $r2, 270, 360, 'F');
12432                  $this->SetFillColorArray($cola);
12433                  $this->PieSector($x, $y, $r2, 0, 90, 'F');
12434                  $this->PieSector($x, $y, $r2, 180, 270, 'F');
12435                  $this->Circle($x, $y, $r2, 0, 360, 'C', $line_style, array(), 8);
12436              }
12437          }
12438  
12439          /**
12440           * Paints a linear colour gradient.
12441           * @param float $x abscissa of the top left corner of the rectangle.
12442           * @param float $y ordinate of the top left corner of the rectangle.
12443           * @param float $w width of the rectangle.
12444           * @param float $h height of the rectangle.
12445           * @param array $col1 first color (Grayscale, RGB or CMYK components).
12446           * @param array $col2 second color (Grayscale, RGB or CMYK components).
12447           * @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).
12448           * @author Andreas Würmser, Nicola Asuni
12449           * @since 3.1.000 (2008-06-09)
12450           * @access public
12451           */
12452  		public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
12453              $this->Clip($x, $y, $w, $h);
12454              $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
12455          }
12456  
12457          /**
12458           * Paints a radial colour gradient.
12459           * @param float $x abscissa of the top left corner of the rectangle.
12460           * @param float $y ordinate of the top left corner of the rectangle.
12461           * @param float $w width of the rectangle.
12462           * @param float $h height of the rectangle.
12463           * @param array $col1 first color (Grayscale, RGB or CMYK components).
12464           * @param array $col2 second color (Grayscale, RGB or CMYK components).
12465           * @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.
12466           * @author Andreas Würmser, Nicola Asuni
12467           * @since 3.1.000 (2008-06-09)
12468           * @access public
12469           */
12470  		public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
12471              $this->Clip($x, $y, $w, $h);
12472              $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
12473          }
12474  
12475          /**
12476           * Paints a coons patch mesh.
12477           * @param float $x abscissa of the top left corner of the rectangle.
12478           * @param float $y ordinate of the top left corner of the rectangle.
12479           * @param float $w width of the rectangle.
12480           * @param float $h height of the rectangle.
12481           * @param array $col1 first color (lower left corner) (RGB components).
12482           * @param array $col2 second color (lower right corner) (RGB components).
12483           * @param array $col3 third color (upper right corner) (RGB components).
12484           * @param array $col4 fourth color (upper left corner) (RGB components).
12485           * @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>
12486           * @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
12487           * @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
12488           * @param boolean $antialias A flag indicating whether to filter the shading function to prevent aliasing artifacts.
12489           * @author Andreas Würmser, Nicola Asuni
12490           * @since 3.1.000 (2008-06-09)
12491           * @access public
12492           */
12493  		public 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, $antialias=false) {
12494              $this->Clip($x, $y, $w, $h);
12495              $n = count($this->gradients) + 1;
12496              $this->gradients[$n] = array();
12497              $this->gradients[$n]['type'] = 6; //coons patch mesh
12498              $this->gradients[$n]['coords'] = array();
12499              $this->gradients[$n]['antialias'] = $antialias;
12500              $this->gradients[$n]['colors'] = array();
12501              $this->gradients[$n]['transparency'] = false;
12502              //check the coords array if it is the simple array or the multi patch array
12503              if (!isset($coords[0]['f'])) {
12504                  //simple array -> convert to multi patch array
12505                  if (!isset($col1[1])) {
12506                      $col1[1] = $col1[2] = $col1[0];
12507                  }
12508                  if (!isset($col2[1])) {
12509                      $col2[1] = $col2[2] = $col2[0];
12510                  }
12511                  if (!isset($col3[1])) {
12512                      $col3[1] = $col3[2] = $col3[0];
12513                  }
12514                  if (!isset($col4[1])) {
12515                      $col4[1] = $col4[2] = $col4[0];
12516                  }
12517                  $patch_array[0]['f'] = 0;
12518                  $patch_array[0]['points'] = $coords;
12519                  $patch_array[0]['colors'][0]['r'] = $col1[0];
12520                  $patch_array[0]['colors'][0]['g'] = $col1[1];
12521                  $patch_array[0]['colors'][0]['b'] = $col1[2];
12522                  $patch_array[0]['colors'][1]['r'] = $col2[0];
12523                  $patch_array[0]['colors'][1]['g'] = $col2[1];
12524                  $patch_array[0]['colors'][1]['b'] = $col2[2];
12525                  $patch_array[0]['colors'][2]['r'] = $col3[0];
12526                  $patch_array[0]['colors'][2]['g'] = $col3[1];
12527                  $patch_array[0]['colors'][2]['b'] = $col3[2];
12528                  $patch_array[0]['colors'][3]['r'] = $col4[0];
12529                  $patch_array[0]['colors'][3]['g'] = $col4[1];
12530                  $patch_array[0]['colors'][3]['b'] = $col4[2];
12531              } else {
12532                  //multi patch array
12533                  $patch_array = $coords;
12534              }
12535              $bpcd = 65535; //16 bits per coordinate
12536              //build the data stream
12537              $this->gradients[$n]['stream'] = '';
12538              $count_patch = count($patch_array);
12539              for ($i=0; $i < $count_patch; ++$i) {
12540                  $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
12541                  $count_points = count($patch_array[$i]['points']);
12542                  for ($j=0; $j < $count_points; ++$j) {
12543                      //each point as 16 bit
12544                      $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
12545                      if ($patch_array[$i]['points'][$j] < 0) {
12546                          $patch_array[$i]['points'][$j] = 0;
12547                      }
12548                      if ($patch_array[$i]['points'][$j] > $bpcd) {
12549                          $patch_array[$i]['points'][$j] = $bpcd;
12550                      }
12551                      $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
12552                      $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
12553                  }
12554                  $count_cols = count($patch_array[$i]['colors']);
12555                  for ($j=0; $j < $count_cols; ++$j) {
12556                      //each color component as 8 bit
12557                      $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
12558                      $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
12559                      $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
12560                  }
12561              }
12562              //paint the gradient
12563              $this->_out('/Sh'.$n.' sh');
12564              //restore previous Graphic State
12565              $this->_out('Q');
12566          }
12567  
12568          /**
12569           * Set a rectangular clipping area.
12570           * @param float $x abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
12571           * @param float $y ordinate of the top left corner of the rectangle.
12572           * @param float $w width of the rectangle.
12573           * @param float $h height of the rectangle.
12574           * @author Andreas Würmser, Nicola Asuni
12575           * @since 3.1.000 (2008-06-09)
12576           * @access protected
12577           */
12578  		protected function Clip($x, $y, $w, $h) {
12579              if ($this->rtl) {
12580                  $x = $this->w - $x - $w;
12581              }
12582              //save current Graphic State
12583              $s = 'q';
12584              //set clipping area
12585              $s .= sprintf(' %.2F %.2F %.2F %.2F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
12586              //set up transformation matrix for gradient
12587              $s .= sprintf(' %.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
12588              $this->_out($s);
12589          }
12590  
12591          /**
12592           * Output gradient.
12593           * @param int $type type of gradient (1 Function-based shading; 2 Axial shading; 3 Radial shading; 4 Free-form Gouraud-shaded triangle mesh; 5 Lattice-form Gouraud-shaded triangle mesh; 6 Coons patch mesh; 7 Tensor-product patch mesh). (Not all types are currently supported)
12594           * @param array $coords array of coordinates.
12595           * @param array $stops array gradient color components: color = array of GRAY, RGB or CMYK color components; offset = (0 to 1) represents a location along the gradient vector; exponent = exponent of the exponential interpolation function (default = 1).
12596           * @param array $background An array of colour components appropriate to the colour space, specifying a single background colour value.
12597           * @param boolean $antialias A flag indicating whether to filter the shading function to prevent aliasing artifacts.
12598           * @author Nicola Asuni
12599           * @since 3.1.000 (2008-06-09)
12600           * @access public
12601           */
12602  		public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
12603              $n = count($this->gradients) + 1;
12604              $this->gradients[$n] = array();
12605              $this->gradients[$n]['type'] = $type;
12606              $this->gradients[$n]['coords'] = $coords;
12607              $this->gradients[$n]['antialias'] = $antialias;
12608              $this->gradients[$n]['colors'] = array();
12609              $this->gradients[$n]['transparency'] = false;
12610              // color space
12611              $numcolspace = count($stops[0]['color']);
12612              $bcolor = array_values($background);
12613              switch($numcolspace) {
12614                  case 4: { // CMYK
12615                      $this->gradients[$n]['colspace'] = 'DeviceCMYK';
12616                      if (!empty($background)) {
12617                          $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F %.3F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
12618                      }
12619                      break;
12620                  }
12621                  case 3: { // RGB
12622                      $this->gradients[$n]['colspace'] = 'DeviceRGB';
12623                      if (!empty($background)) {
12624                          $this->gradients[$n]['background'] = sprintf('%.3F %.3F %.3F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
12625                      }
12626                      break;
12627                  }
12628                  case 1: { // Gray scale
12629                      $this->gradients[$n]['colspace'] = 'DeviceGray';
12630                      if (!empty($background)) {
12631                          $this->gradients[$n]['background'] = sprintf('%.3F', $bcolor[0]/255);
12632                      }
12633                      break;
12634                  }
12635              }
12636              $num_stops = count($stops);
12637              $last_stop_id = $num_stops - 1;
12638              foreach ($stops as $key => $stop) {
12639                  $this->gradients[$n]['colors'][$key] = array();
12640                  // offset represents a location along the gradient vector
12641                  if (isset($stop['offset'])) {
12642                      $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
12643                  } else {
12644                      if ($key == 0) {
12645                          $this->gradients[$n]['colors'][$key]['offset'] = 0;
12646                      } elseif ($key == $last_stop_id) {
12647                          $this->gradients[$n]['colors'][$key]['offset'] = 1;
12648                      } else {
12649                          $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
12650                          $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
12651                      }
12652                  }
12653                  if (isset($stop['opacity'])) {
12654                      $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
12655                      if ($stop['opacity'] < 1) {
12656                          $this->gradients[$n]['transparency'] = true;
12657                      }
12658                  } else {
12659                      $this->gradients[$n]['colors'][$key]['opacity'] = 1;
12660                  }
12661                  // exponent for the exponential interpolation function
12662                  if (isset($stop['exponent'])) {
12663                      $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
12664                  } else {
12665                      $this->gradients[$n]['colors'][$key]['exponent'] = 1;
12666                  }
12667                  // set colors
12668                  $color = array_values($stop['color']);
12669                  switch($numcolspace) {
12670                      case 4: { // CMYK
12671                          $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F %.3F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
12672                          break;
12673                      }
12674                      case 3: { // RGB
12675                          $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F %.3F %.3F', $color[0]/255, $color[1]/255, $color[2]/255);
12676                          break;
12677                      }
12678                      case 1: { // Gray scale
12679                          $this->gradients[$n]['colors'][$key]['color'] = sprintf('%.3F', $color[0]/255);
12680                          break;
12681                      }
12682                  }
12683              }
12684              if ($this->gradients[$n]['transparency']) {
12685                  // paint luminosity gradient
12686                  $this->_out('/TGS'.$n.' gs');
12687              }
12688              //paint the gradient
12689              $this->_out('/Sh'.$n.' sh');
12690              //restore previous Graphic State
12691              $this->_out('Q');
12692          }
12693  
12694          /**
12695           * Output gradient shaders.
12696           * @author Nicola Asuni
12697           * @since 3.1.000 (2008-06-09)
12698           * @access protected
12699           */
12700  		function _putshaders() {
12701              $idt = count($this->gradients); //index for transparency gradients
12702              foreach ($this->gradients as $id => $grad) {
12703                  if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
12704                      $this->_newobj();
12705                      $fc = $this->n;
12706                      $out = '<<';
12707                      $out .= ' /FunctionType 3';
12708                      $out .= ' /Domain [0 1]';
12709                      $functions = '';
12710                      $bounds = '';
12711                      $encode = '';
12712                      $i = 1;
12713                      $num_cols = count($grad['colors']);
12714                      $lastcols = $num_cols - 1;
12715                      for ($i = 1; $i < $num_cols; ++$i) {
12716                          $functions .= ($fc + $i).' 0 R ';
12717                          if ($i < $lastcols) {
12718                              $bounds .= sprintf('%.3F ', $grad['colors'][$i]['offset']);
12719                          }
12720                          $encode .= '0 1 ';
12721                      }
12722                      $out .= ' /Functions ['.trim($functions).']';
12723                      $out .= ' /Bounds ['.trim($bounds).']';
12724                      $out .= ' /Encode ['.trim($encode).']';
12725                      $out .= ' >>';
12726                      $out .= ' endobj';
12727                      $this->_out($out);
12728                      for ($i = 1; $i < $num_cols; ++$i) {
12729                          $this->_newobj();
12730                          $out = '<<';
12731                          $out .= ' /FunctionType 2';
12732                          $out .= ' /Domain [0 1]';
12733                          $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
12734                          $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
12735                          $out .= ' /N '.$grad['colors'][$i]['exponent'];
12736                          $out .= ' >>';
12737                          $out .= ' endobj';
12738                          $this->_out($out);
12739                      }
12740                      // set transparency fuctions
12741                      if ($grad['transparency']) {
12742                          $this->_newobj();
12743                          $ft = $this->n;
12744                          $out = '<<';
12745                          $out .= ' /FunctionType 3';
12746                          $out .= ' /Domain [0 1]';
12747                          $functions = '';
12748                          $i = 1;
12749                          $num_cols = count($grad['colors']);
12750                          for ($i = 1; $i < $num_cols; ++$i) {
12751                              $functions .= ($ft + $i).' 0 R ';
12752                          }
12753                          $out .= ' /Functions ['.trim($functions).']';
12754                          $out .= ' /Bounds ['.trim($bounds).']';
12755                          $out .= ' /Encode ['.trim($encode).']';
12756                          $out .= ' >>';
12757                          $out .= ' endobj';
12758                          $this->_out($out);
12759                          for ($i = 1; $i < $num_cols; ++$i) {
12760                              $this->_newobj();
12761                              $out = '<<';
12762                              $out .= ' /FunctionType 2';
12763                              $out .= ' /Domain [0 1]';
12764                              $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
12765                              $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
12766                              $out .= ' /N '.$grad['colors'][$i]['exponent'];
12767                              $out .= ' >>';
12768                              $out .= ' endobj';
12769                              $this->_out($out);
12770                          }
12771                      }
12772                  }
12773                  // set shading object
12774                  $this->_newobj();
12775                  $out = '<< /ShadingType '.$grad['type'];
12776                  if (isset($grad['colspace'])) {
12777                      $out .= ' /ColorSpace /'.$grad['colspace'];
12778                  } else {
12779                      $out .= ' /ColorSpace /DeviceRGB';
12780                  }
12781                  if (isset($grad['background']) AND !empty($grad['background'])) {
12782                      $out .= ' /Background ['.$grad['background'].']';
12783                  }
12784                  if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
12785                      $out .= ' /AntiAlias true';
12786                  }
12787                  if ($grad['type'] == 2) {
12788                      $out .= ' '.sprintf('/Coords [%.3F %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
12789                      $out .= ' /Domain [0 1]';
12790                      $out .= ' /Function '.$fc.' 0 R';
12791                      $out .= ' /Extend [true true]';
12792                      $out .= ' >>';
12793                  } elseif ($grad['type'] == 3) {
12794                      //x0, y0, r0, x1, y1, r1
12795                      //at this this time radius of inner circle is 0
12796                      $out .= ' '.sprintf('/Coords [%.3F %.3F 0 %.3F %.3F %.3F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
12797                      $out .= ' /Domain [0 1]';
12798                      $out .= ' /Function '.$fc.' 0 R';
12799                      $out .= ' /Extend [true true]';
12800                      $out .= ' >>';
12801                  } elseif ($grad['type'] == 6) {
12802                      $out .= ' /BitsPerCoordinate 16';
12803                      $out .= ' /BitsPerComponent 8';
12804                      $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
12805                      $out .= ' /BitsPerFlag 8';
12806                      $out .= ' /Length '.strlen($grad['stream']);
12807                      $out .= ' >>';
12808                      $out .= ' '.$this->_getstream($grad['stream']);
12809                  }
12810                  $out .= ' endobj';
12811                  $this->_out($out);
12812                  if ($grad['transparency']) {
12813                      $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
12814                      $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
12815                  }
12816                  $this->gradients[$id]['id'] = $this->n;
12817                  // set pattern object
12818                  $this->_newobj();
12819                  $out = '<< /Type /Pattern /PatternType 2';
12820                  $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
12821                  $out .= ' >> endobj';
12822                  $this->_out($out);
12823                  $this->gradients[$id]['pattern'] = $this->n;
12824                  // set shading and pattern for transparency mask
12825                  if ($grad['transparency']) {
12826                      // luminosity pattern
12827                      $idgs = $id + $idt;
12828                      $this->_newobj();
12829                      $this->_out($shading_transparency);
12830                      $this->gradients[$idgs]['id'] = $this->n;
12831                      $this->_newobj();
12832                      $out = '<< /Type /Pattern /PatternType 2';
12833                      $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
12834                      $out .= ' >> endobj';
12835                      $this->_out($out);
12836                      $this->gradients[$idgs]['pattern'] = $this->n;
12837                      // luminosity XObject
12838                      $this->_newobj();
12839                      $filter = ($this->compress)?' /Filter /FlateDecode':'';
12840                      $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
12841                      $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
12842                      $out .= ' /Length '.strlen($stream);
12843                      $out .= ' /BBox [0 0 '.$this->wPt.' '.$this->hPt.']';
12844                      $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
12845                      $out .= ' /Resources <<';
12846                      $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
12847                      $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
12848                      $out .= ' >>';
12849                      $out .= ' >> ';
12850                      $out .= $this->_getstream($stream);
12851                      $out .= ' endobj';
12852                      $this->_out($out);
12853                      // SMask
12854                      $this->_newobj();
12855                      $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >> endobj';
12856                      $this->_out($out);
12857                      // ExtGState
12858                      $this->_newobj();
12859                      $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >> endobj';
12860                      $this->_out($out);
12861                      $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
12862                  }
12863              }
12864          }
12865  
12866          /**
12867           * Draw the sector of a circle.
12868           * It can be used for instance to render pie charts.
12869           * @param float $xc abscissa of the center.
12870           * @param float $yc ordinate of the center.
12871           * @param float $r radius.
12872           * @param float $a start angle (in degrees).
12873           * @param float $b end angle (in degrees).
12874           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
12875           * @param float $cw: indicates whether to go clockwise (default: true).
12876           * @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.
12877           * @author Maxime Delorme, Nicola Asuni
12878           * @since 3.1.000 (2008-06-09)
12879           * @access public
12880           */
12881  		public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
12882              $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
12883          }
12884  
12885          /**
12886           * Draw the sector of an ellipse.
12887           * It can be used for instance to render pie charts.
12888           * @param float $xc abscissa of the center.
12889           * @param float $yc ordinate of the center.
12890           * @param float $rx the x-axis radius.
12891           * @param float $ry the y-axis radius.
12892           * @param float $a start angle (in degrees).
12893           * @param float $b end angle (in degrees).
12894           * @param string $style Style of rendering. See the getPathPaintOperator() function for more information.
12895           * @param float $cw: indicates whether to go clockwise.
12896           * @param float $o: origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
12897           * @param integer $nc Number of curves used to draw a 90 degrees portion of arc.
12898           * @author Maxime Delorme, Nicola Asuni
12899           * @since 3.1.000 (2008-06-09)
12900           * @access public
12901           */
12902  		public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
12903              if ($this->rtl) {
12904                  $xc = $this->w - $xc;
12905              }
12906              $op = $this->getPathPaintOperator($style);
12907              if ($op == 'f') {
12908                  $line_style = array();
12909              }
12910              if ($cw) {
12911                  $d = $b;
12912                  $b = 360 - $a + $o;
12913                  $a = 360 - $d + $o;
12914              } else {
12915                  $b += $o;
12916                  $a += $o;
12917              }
12918              $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
12919              $this->_out($op);
12920          }
12921  
12922          /**
12923           * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
12924           * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of SVG images using ImageMagick library.
12925           * Only vector drawing is supported, not text or bitmap.
12926           * 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).
12927           * @param string $file Name of the file containing the image.
12928           * @param float $x Abscissa of the upper-left corner.
12929           * @param float $y Ordinate of the upper-left corner.
12930           * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
12931           * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
12932           * @param mixed $link URL or identifier returned by AddLink().
12933           * @param boolean useBoundingBox specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
12934           * @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>
12935           * @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>
12936           * @param mixed $border Indicates if borders must be drawn around the image. 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>
12937           * @param boolean $fitonpage if true the image is resized to not exceed page dimensions.
12938           * @author Valentin Schmidt, Nicola Asuni
12939           * @since 3.1.000 (2008-06-09)
12940           * @access public
12941           */
12942  		public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false) {
12943              if ($this->rasterize_vector_images) {
12944                  // convert SVG to raster image using GD or ImageMagick libraries
12945                  return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
12946              }
12947              if ($x === '') {
12948                  $x = $this->x;
12949              }
12950              if ($y === '') {
12951                  $y = $this->y;
12952              }
12953              $k = $this->k;
12954              $data = file_get_contents($file);
12955              if ($data === false) {
12956                  $this->Error('EPS file not found: '.$file);
12957              }
12958              $regs = array();
12959              // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
12960              preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
12961              if (count($regs) > 1) {
12962                  $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
12963                  if (strpos($version_str, 'Adobe Illustrator') !== false) {
12964                      $versexp = explode(' ', $version_str);
12965                      $version = (float)array_pop($versexp);
12966                      if ($version >= 9) {
12967                          $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
12968                      }
12969                  }
12970              }
12971              // strip binary bytes in front of PS-header
12972              $start = strpos($data, '%!PS-Adobe');
12973              if ($start > 0) {
12974                  $data = substr($data, $start);
12975              }
12976              // find BoundingBox params
12977              preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
12978              if (count($regs) > 1) {
12979                  list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
12980              } else {
12981                  $this->Error('No BoundingBox found in EPS file: '.$file);
12982              }
12983              $start = strpos($data, '%%EndSetup');
12984              if ($start === false) {
12985                  $start = strpos($data, '%%EndProlog');
12986              }
12987              if ($start === false) {
12988                  $start = strpos($data, '%%BoundingBox');
12989              }
12990              $data = substr($data, $start);
12991              $end = strpos($data, '%%PageTrailer');
12992              if ($end===false) {
12993                  $end = strpos($data, 'showpage');
12994              }
12995              if ($end) {
12996                  $data = substr($data, 0, $end);
12997              }
12998              // calculate image width and height on document
12999              if (($w <= 0) AND ($h <= 0)) {
13000                  $w = ($x2 - $x1) / $k;
13001                  $h = ($y2 - $y1) / $k;
13002              } elseif ($w <= 0) {
13003                  $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
13004              } elseif ($h <= 0) {
13005                  $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
13006              }
13007              // Check whether we need a new page first as this does not fit
13008              $prev_x = $this->x;
13009              if ($this->checkPageBreak($h, $y)) {
13010                  $y = $this->y;
13011                  if ($this->rtl) {
13012                      $x += ($prev_x - $this->x);
13013                  } else {
13014                      $x += ($this->x - $prev_x);
13015                  }
13016              }
13017              // resize image to be contained on a single page
13018              if ($fitonpage) {
13019                  $ratio_wh = $w / $h;
13020                  if (($y + $h) > $this->PageBreakTrigger) {
13021                      $h = $this->PageBreakTrigger - $y;
13022                      $w = $h * $ratio_wh;
13023                  }
13024                  if (($x + $w) > ($this->w - $this->rMargin)) {
13025                      $w = $this->w - $this->rMargin - $x;
13026                      $h = $w / $ratio_wh;
13027                  }
13028              }
13029              // set scaling factors
13030              $scale_x = $w / (($x2 - $x1) / $k);
13031              $scale_y = $h / (($y2 - $y1) / $k);
13032              // set alignment
13033              $this->img_rb_y = $y + $h;
13034              // set alignment
13035              if ($this->rtl) {
13036                  if ($palign == 'L') {
13037                      $ximg = $this->lMargin;
13038                  } elseif ($palign == 'C') {
13039                      $ximg = ($this->w - $w) / 2;
13040                  } elseif ($palign == 'R') {
13041                      $ximg = $this->w - $this->rMargin - $w;
13042                  } else {
13043                      $ximg = $this->w - $x - $w;
13044                  }
13045                  $this->img_rb_x = $ximg;
13046              } else {
13047                  if ($palign == 'L') {
13048                      $ximg = $this->lMargin;
13049                  } elseif ($palign == 'C') {
13050                      $ximg = ($this->w - $w) / 2;
13051                  } elseif ($palign == 'R') {
13052                      $ximg = $this->w - $this->rMargin - $w;
13053                  } else {
13054                      $ximg = $x;
13055                  }
13056                  $this->img_rb_x = $ximg + $w;
13057              }
13058              if ($useBoundingBox) {
13059                  $dx = $ximg * $k - $x1;
13060                  $dy = $y * $k - $y1;
13061              } else {
13062                  $dx = $ximg * $k;
13063                  $dy = $y * $k;
13064              }
13065              // save the current graphic state
13066              $this->_out('q'.$this->epsmarker);
13067              // translate
13068              $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
13069              // scale
13070              if (isset($scale_x)) {
13071                  $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
13072              }
13073              // handle pc/unix/mac line endings
13074              preg_match('/[\r\n]+/s', $data, $regs);
13075              $lines = explode($regs[0], $data);
13076              $u=0;
13077              $cnt = count($lines);
13078              for ($i=0; $i < $cnt; ++$i) {
13079                  $line = $lines[$i];
13080                  if (($line == '') OR ($line{0} == '%')) {
13081                      continue;
13082                  }
13083                  $len = strlen($line);
13084                  $chunks = explode(' ', $line);
13085                  $cmd = array_pop($chunks);
13086                  // RGB
13087                  if (($cmd == 'Xa') OR ($cmd == 'XA')) {
13088                      $b = array_pop($chunks);
13089                      $g = array_pop($chunks);
13090                      $r = array_pop($chunks);
13091                      $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!
13092                      continue;
13093                  }
13094                  switch ($cmd) {
13095                      case 'm':
13096                      case 'l':
13097                      case 'v':
13098                      case 'y':
13099                      case 'c':
13100                      case 'k':
13101                      case 'K':
13102                      case 'g':
13103                      case 'G':
13104                      case 's':
13105                      case 'S':
13106                      case 'J':
13107                      case 'j':
13108                      case 'w':
13109                      case 'M':
13110                      case 'd':
13111                      case 'n': {
13112                          $this->_out($line);
13113                          break;
13114                      }
13115                      case 'x': {// custom fill color
13116                          list($c,$m,$y,$k) = $chunks;
13117                          $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' k');
13118                          break;
13119                      }
13120                      case 'X': { // custom stroke color
13121                          list($c,$m,$y,$k) = $chunks;
13122                          $this->_out(''.$c.' '.$m.' '.$y.' '.$k.' K');
13123                          break;
13124                      }
13125                      case 'Y':
13126                      case 'N':
13127                      case 'V':
13128                      case 'L':
13129                      case 'C': {
13130                          $line{$len-1} = strtolower($cmd);
13131                          $this->_out($line);
13132                          break;
13133                      }
13134                      case 'b':
13135                      case 'B': {
13136                          $this->_out($cmd . '*');
13137                          break;
13138                      }
13139                      case 'f':
13140                      case 'F': {
13141                          if ($u > 0) {
13142                              $isU = false;
13143                              $max = min($i+5, $cnt);
13144                              for ($j=$i+1; $j < $max; ++$j) {
13145                                  $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
13146                              }
13147                              if ($isU) {
13148                                  $this->_out('f*');
13149                              }
13150                          } else {
13151                              $this->_out('f*');
13152                          }
13153                          break;
13154                      }
13155                      case '*u': {
13156                          ++$u;
13157                          break;
13158                      }
13159                      case '*U': {
13160                          --$u;
13161                          break;
13162                      }
13163                  }
13164              }
13165              // restore previous graphic state
13166              $this->_out($this->epsmarker.'Q');
13167              if (!empty($border)) {
13168                  $bx = $x;
13169                  $by = $y;
13170                  $this->x = $ximg;
13171                  if ($this->rtl) {
13172                      $this->x += $w;
13173                  }
13174                  $this->y = $y;
13175                  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
13176                  $this->x = $bx;
13177                  $this->y = $by;
13178              }
13179              if ($link) {
13180                  $this->Link($ximg, $y, $w, $h, $link, 0);
13181              }
13182              // set pointer to align the successive text/objects
13183              switch($align) {
13184                  case 'T':{
13185                      $this->y = $y;
13186                      $this->x = $this->img_rb_x;
13187                      break;
13188                  }
13189                  case 'M':{
13190                      $this->y = $y + round($h/2);
13191                      $this->x = $this->img_rb_x;
13192                      break;
13193                  }
13194                  case 'B':{
13195                      $this->y = $this->img_rb_y;
13196                      $this->x = $this->img_rb_x;
13197                      break;
13198                  }
13199                  case 'N':{
13200                      $this->SetY($this->img_rb_y);
13201                      break;
13202                  }
13203                  default:{
13204                      break;
13205                  }
13206              }
13207              $this->endlinex = $this->img_rb_x;
13208          }
13209  
13210          /**
13211            * Set document barcode.
13212           * @param string $bc barcode
13213           * @access public
13214           */
13215  		public function setBarcode($bc='') {
13216              $this->barcode = $bc;
13217          }
13218  
13219          /**
13220            * Get current barcode.
13221           * @return string
13222           * @access public
13223           * @since 4.0.012 (2008-07-24)
13224           */
13225  		public function getBarcode() {
13226              return $this->barcode;
13227          }
13228  
13229          /**
13230            * Print a Linear Barcode.
13231            * @param string $code code to print
13232            * @param string $type type of barcode (see barcodes.php for supported formats).
13233           * @param int $x x position in user units
13234           * @param int $y y position in user units
13235           * @param int $w width in user units
13236           * @param int $h height in user units
13237           * @param float $xres width of the smallest bar in user units
13238           * @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 (set to 'auto' for automatic padding)</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>
13239           * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode 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>
13240           * @author Nicola Asuni
13241           * @since 3.1.000 (2008-06-09)
13242           * @access public
13243           */
13244  		public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres=0.4, $style='', $align='') {
13245              if ($this->empty_string($code)) {
13246                  return;
13247              }
13248              require_once(dirname(__FILE__).'/barcodes.php');
13249              // save current graphic settings
13250              $gvars = $this->getGraphicVars();
13251              // create new barcode object
13252              $barcodeobj = new TCPDFBarcode($code, $type);
13253              $arrcode = $barcodeobj->getBarcodeArray();
13254              if ($arrcode === false) {
13255                  $this->Error('Error in 1D barcode string');
13256              }
13257              // set default values
13258              if (!isset($style['position'])) {
13259                  if ($this->rtl) {
13260                      $style['position'] = 'R';
13261                  } else {
13262                      $style['position'] = 'L';
13263                  }
13264              }
13265              if (!isset($style['fgcolor'])) {
13266                  $style['fgcolor'] = array(0,0,0); // default black
13267              }
13268              if (!isset($style['bgcolor'])) {
13269                  $style['bgcolor'] = false; // default transparent
13270              }
13271              if (!isset($style['border'])) {
13272                  $style['border'] = false;
13273              }
13274              $fontsize = 0;
13275              if (!isset($style['text'])) {
13276                  $style['text'] = false;
13277              }
13278              if ($style['text'] AND isset($style['font'])) {
13279                  if (isset($style['fontsize'])) {
13280                      $fontsize = $style['fontsize'];
13281                  }
13282                  $this->SetFont($style['font'], '', $fontsize);
13283              }
13284              if (!isset($style['stretchtext'])) {
13285                  $style['stretchtext'] = 4;
13286              }
13287              // set foreground color
13288              $this->SetDrawColorArray($style['fgcolor']);
13289              $this->SetTextColorArray($style['fgcolor']);
13290              if ($this->empty_string($w) OR ($w <= 0)) {
13291                  if ($this->rtl) {
13292                      $w = $this->x - $this->lMargin;
13293                  } else {
13294                      $w = $this->w - $this->rMargin - $this->x;
13295                  }
13296              }
13297              if ($this->empty_string($x)) {
13298                  $x = $this->GetX();
13299              }
13300              if ($this->rtl) {
13301                  $x = $this->w - $x;
13302              }
13303              if ($this->empty_string($y)) {
13304                  $y = $this->GetY();
13305              }
13306              if ($this->empty_string($xres)) {
13307                  $xres = 0.4;
13308              }
13309              if ($this->empty_string($h) OR ($h <= 0)) {
13310                  $h = $w / 3;
13311              }
13312              // padding
13313              if (!isset($style['padding'])) {
13314                  $style['padding'] = 0;
13315              } elseif ($style['padding'] === 'auto') {
13316                  $style['padding'] = $h / 4;
13317              }
13318              $fbw = ($arrcode['maxw'] * $xres) + (2 * $style['padding']);
13319              $extraspace = ($this->cell_height_ratio * $fontsize / $this->k) + (2 * $style['padding']);
13320              $prev_x = $this->x;
13321              // maximum bar height
13322              $barh = $h;
13323              $h += $extraspace;
13324              if ($this->checkPageBreak($h, $y)) {
13325                  $y = $this->GetY() + $this->cMargin;
13326                  if ($this->rtl) {
13327                      $x += ($prev_x - $this->x);
13328                  } else {
13329                      $x += ($this->x - $prev_x);
13330                  }
13331              }
13332              switch ($style['position']) {
13333                  case 'L': { // left
13334                      if ($this->rtl) {
13335                          $xpos = $x - $w;
13336                      } else {
13337                          $xpos = $x;
13338                      }
13339                      break;
13340                  }
13341                  case 'C': { // center
13342                      $xdiff = (($w - $fbw) / 2);
13343                      if ($this->rtl) {
13344                          $xpos = $x - $w + $xdiff;
13345                      } else {
13346                          $xpos = $x + $xdiff;
13347                      }
13348                      break;
13349                  }
13350                  case 'R': { // right
13351                      if ($this->rtl) {
13352                          $xpos = $x - $fbw;
13353                      } else {
13354                          $xpos = $x + $w - $fbw;
13355                      }
13356                      break;
13357                  }
13358                  case 'S': { // stretch
13359                      $fbw = $w;
13360                      $xres = ($w - (2 * $style['padding'])) / $arrcode['maxw'];
13361                      if ($this->rtl) {
13362                          $xpos = $x - $w;
13363                      } else {
13364                          $xpos = $x;
13365                      }
13366                      break;
13367                  }
13368              }
13369              $xpos_rect = $xpos;
13370              $xpos = $xpos_rect + $style['padding'];
13371              $xpos_text = $xpos;
13372              // barcode is always printed in LTR direction
13373              $tempRTL = $this->rtl;
13374              $this->rtl = false;
13375              // print background color
13376              if ($style['bgcolor']) {
13377                  $this->Rect($xpos_rect, $y, $fbw, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
13378              } elseif ($style['border']) {
13379                  $this->Rect($xpos_rect, $y, $fbw, $h, 'D');
13380              }
13381              // print bars
13382              if ($arrcode !== false) {
13383                  foreach ($arrcode['bcode'] as $k => $v) {
13384                      $bw = ($v['w'] * $xres);
13385                      if ($v['t']) {
13386                          // draw a vertical bar
13387                          $ypos = $y + $style['padding'] + ($v['p'] * $barh / $arrcode['maxh']);
13388                          $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh  / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
13389                      }
13390                      $xpos += $bw;
13391                  }
13392              }
13393              // print text
13394              if ($style['text']) {
13395                  // print text
13396                  $this->x = $xpos_text;
13397                  $this->y = $y + $style['padding'] + $barh;
13398                  $this->Cell(($arrcode['maxw'] * $xres), ($this->cell_height_ratio * $fontsize / $this->k), $code, 0, 0, 'C', 0, '', $style['stretchtext']);
13399              }
13400              // restore original direction
13401              $this->rtl = $tempRTL;
13402              // restore previous settings
13403              $this->setGraphicVars($gvars);
13404              // set bottomcoordinates
13405              $this->img_rb_y = $y + $h;
13406              if ($this->rtl) {
13407                  // set left side coordinate
13408                  $this->img_rb_x = ($this->w - $x - $w);
13409              } else {
13410                  // set right side coordinate
13411                  $this->img_rb_x = $x + $w;
13412              }
13413              // set pointer to align the successive text/objects
13414              switch($align) {
13415                  case 'T':{
13416                      $this->y = $y;
13417                      $this->x = $this->img_rb_x;
13418                      break;
13419                  }
13420                  case 'M':{
13421                      $this->y = $y + round($h/2);
13422                      $this->x = $this->img_rb_x;
13423                      break;
13424                  }
13425                  case 'B':{
13426                      $this->y = $this->img_rb_y;
13427                      $this->x = $this->img_rb_x;
13428                      break;
13429                  }
13430                  case 'N':{
13431                      $this->SetY($this->img_rb_y);
13432                      break;
13433                  }
13434                  default:{
13435                      break;
13436                  }
13437              }
13438          }
13439  
13440          /**
13441            * This function is DEPRECATED, please use the new write1DBarcode() function.
13442           * @param int $x x position in user units
13443           * @param int $y y position in user units
13444           * @param int $w width in user units
13445           * @param int $h height position in user units
13446           * @param string $type type of barcode (I25, C128A, C128B, C128C, C39)
13447           * @param string $style barcode style
13448           * @param string $font font for text
13449           * @param int $xres x resolution
13450           * @param string $code code to print
13451           * @deprecated deprecated since version 3.1.000 (2008-06-10)
13452           * @access public
13453           * @see write1DBarcode()
13454           */
13455  		public function writeBarcode($x, $y, $w, $h, $type, $style, $font, $xres, $code) {
13456              // convert old settings for the new write1DBarcode() function.
13457              $xres = 1 / $xres;
13458              $newstyle = array(
13459                  'position' => 'L',
13460                  'border' => false,
13461                  'padding' => 0,
13462                  'fgcolor' => array(0,0,0),
13463                  'bgcolor' => false,
13464                  'text' => true,
13465                  'font' => $font,
13466                  'fontsize' => 8,
13467                  'stretchtext' => 4
13468              );
13469              if ($style & 1) {
13470                  $newstyle['border'] = true;
13471              }
13472              if ($style & 2) {
13473                  $newstyle['bgcolor'] = false;
13474              }
13475              if ($style & 4) {
13476                  $newstyle['position'] = 'C';
13477              } elseif ($style & 8) {
13478                  $newstyle['position'] = 'L';
13479              } elseif ($style & 16) {
13480                  $newstyle['position'] = 'R';
13481              }
13482              if ($style & 128) {
13483                  $newstyle['text'] = true;
13484              }
13485              if ($style & 256) {
13486                  $newstyle['stretchtext'] = 4;
13487              }
13488              $this->write1DBarcode($code, $type, $x, $y, $w, $h, $xres, $newstyle, '');
13489          }
13490  
13491          /**
13492            * Print 2D Barcode.
13493            * @param string $code code to print
13494            * @param string $type type of barcode (see 2dbarcodes.php for supported formats).
13495           * @param int $x x position in user units
13496           * @param int $y y position in user units
13497           * @param int $w width in user units
13498           * @param int $h height in user units
13499           * @param array $style array of options:<ul><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 (set to 'auto' for automatic padding)</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></ul>
13500           * @param string $align Indicates the alignment of the pointer next to barcode insertion relative to barcode 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>
13501           * @author Nicola Asuni
13502           * @since 4.5.037 (2009-04-07)
13503           * @access public
13504           */
13505  		public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style='', $align='') {
13506              if ($this->empty_string($code)) {
13507                  return;
13508              }
13509              require_once(dirname(__FILE__).'/2dbarcodes.php');
13510              // save current graphic settings
13511              $gvars = $this->getGraphicVars();
13512              // create new barcode object
13513              $barcodeobj = new TCPDF2DBarcode($code, $type);
13514              $arrcode = $barcodeobj->getBarcodeArray();
13515              if ($arrcode === false) {
13516                  $this->Error('Error in 2D barcode string');
13517              }
13518              // set default values
13519              if (!isset($style['fgcolor'])) {
13520                  $style['fgcolor'] = array(0,0,0); // default black
13521              }
13522              if (!isset($style['bgcolor'])) {
13523                  $style['bgcolor'] = false; // default transparent
13524              }
13525              if (!isset($style['border'])) {
13526                  $style['border'] = false;
13527              }
13528              // set foreground color
13529              $this->SetDrawColorArray($style['fgcolor']);
13530              if ($this->empty_string($x)) {
13531                  $x = $this->GetX();
13532              }
13533              if ($this->rtl) {
13534                  $x = $this->w - $x;
13535              }
13536              if ($this->empty_string($y)) {
13537                  $y = $this->GetY();
13538              }
13539              if ($this->empty_string($w) OR ($w <= 0)) {
13540                  if ($this->rtl) {
13541                      $w = $x - $this->lMargin;
13542                  } else {
13543                      $w = $this->w - $this->rMargin - $x;
13544                  }
13545              }
13546              if ($this->empty_string($h) OR ($h <= 0)) {
13547                  // 2d barcodes are square by default
13548                  $h = $w;
13549              }
13550              $prev_x = $this->x;
13551              if ($this->checkPageBreak($h, $y)) {
13552                  $y = $this->GetY() + $this->cMargin;
13553                  if ($this->rtl) {
13554                      $x += ($prev_x - $this->x);
13555                  } else {
13556                      $x += ($this->x - $prev_x);
13557                  }
13558              }
13559              // padding
13560              if (!isset($style['padding'])) {
13561                  $style['padding'] = 0;
13562              } elseif ($style['padding'] === 'auto') {
13563                  $style['padding'] = 4 * $w / (8 + $arrcode['num_cols']);
13564              }
13565              // calculate barcode size (excluding padding)
13566              $bw = $w - (2 * $style['padding']);
13567              $bh = $h - (2 * $style['padding']);
13568              // calculate starting coordinates
13569              if ($this->rtl) {
13570                  $xpos = $x - $w;
13571              } else {
13572                  $xpos = $x;
13573              }
13574              $xpos += $style['padding'];
13575              $ypos = $y + $style['padding'];
13576              // barcode is always printed in LTR direction
13577              $tempRTL = $this->rtl;
13578              $this->rtl = false;
13579              // print background color
13580              if ($style['bgcolor']) {
13581                  $this->Rect($x, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
13582              } elseif ($style['border']) {
13583                  $this->Rect($x, $y, $w, $h, 'D');
13584              }
13585              // print barcode cells
13586              if ($arrcode !== false) {
13587                  $rows = $arrcode['num_rows'];
13588                  $cols = $arrcode['num_cols'];
13589                  // calculate dimension of single barcode cell
13590                  $cw = $bw / $cols;
13591                  $ch = $bh / $rows;
13592                  // for each row
13593                  for ($r = 0; $r < $rows; ++$r) {
13594                      $xr = $xpos;
13595                      // for each column
13596                      for ($c = 0; $c < $cols; ++$c) {
13597                          if ($arrcode['bcode'][$r][$c] == 1) {
13598                              // draw a single barcode cell
13599                              $this->Rect($xr, $ypos, $cw, $ch, 'F', array(), $style['fgcolor']);
13600                          }
13601                          $xr += $cw;
13602                      }
13603                      $ypos += $ch;
13604                  }
13605              }
13606              // restore original direction
13607              $this->rtl = $tempRTL;
13608              // restore previous settings
13609              $this->setGraphicVars($gvars);
13610              // set bottomcoordinates
13611              $this->img_rb_y = $y + $h;
13612              if ($this->rtl) {
13613                  // set left side coordinate
13614                  $this->img_rb_x = ($this->w - $x - $w);
13615              } else {
13616                  // set right side coordinate
13617                  $this->img_rb_x = $x + $w;
13618              }
13619              // set pointer to align the successive text/objects
13620              switch($align) {
13621                  case 'T':{
13622                      $this->y = $y;
13623                      $this->x = $this->img_rb_x;
13624                      break;
13625                  }
13626                  case 'M':{
13627                      $this->y = $y + round($h/2);
13628                      $this->x = $this->img_rb_x;
13629                      break;
13630                  }
13631                  case 'B':{
13632                      $this->y = $this->img_rb_y;
13633                      $this->x = $this->img_rb_x;
13634                      break;
13635                  }
13636                  case 'N':{
13637                      $this->SetY($this->img_rb_y);
13638                      break;
13639                  }
13640                  default:{
13641                      break;
13642                  }
13643              }
13644          }
13645  
13646          /**
13647           * Returns an array containing current margins:
13648           * <ul>
13649                  <li>$ret['left'] = left  margin</li>
13650                  <li>$ret['right'] = right margin</li>
13651                  <li>$ret['top'] = top margin</li>
13652                  <li>$ret['bottom'] = bottom margin</li>
13653                  <li>$ret['header'] = header margin</li>
13654                  <li>$ret['footer'] = footer margin</li>
13655                  <li>$ret['cell'] = cell margin</li>
13656           * </ul>
13657           * @return array containing all margins measures
13658           * @access public
13659           * @since 3.2.000 (2008-06-23)
13660           */
13661  		public function getMargins() {
13662              $ret = array(
13663                  'left' => $this->lMargin,
13664                  'right' => $this->rMargin,
13665                  'top' => $this->tMargin,
13666                  'bottom' => $this->bMargin,
13667                  'header' => $this->header_margin,
13668                  'footer' => $this->footer_margin,
13669                  'cell' => $this->cMargin,
13670              );
13671              return $ret;
13672          }
13673  
13674          /**
13675           * Returns an array containing original margins:
13676           * <ul>
13677                  <li>$ret['left'] = left  margin</li>
13678                  <li>$ret['right'] = right margin</li>
13679           * </ul>
13680           * @return array containing all margins measures
13681           * @access public
13682           * @since 4.0.012 (2008-07-24)
13683           */
13684  		public function getOriginalMargins() {
13685              $ret = array(
13686                  'left' => $this->original_lMargin,
13687                  'right' => $this->original_rMargin
13688              );
13689              return $ret;
13690          }
13691  
13692          /**
13693           * Returns the current font size.
13694           * @return current font size
13695           * @access public
13696           * @since 3.2.000 (2008-06-23)
13697           */
13698  		public function getFontSize() {
13699              return $this->FontSize;
13700          }
13701  
13702          /**
13703           * Returns the current font size in points unit.
13704           * @return current font size in points unit
13705           * @access public
13706           * @since 3.2.000 (2008-06-23)
13707           */
13708  		public function getFontSizePt() {
13709              return $this->FontSizePt;
13710          }
13711  
13712          /**
13713           * Returns the current font family name.
13714           * @return string current font family name
13715           * @access public
13716           * @since 4.3.008 (2008-12-05)
13717           */
13718  		public function getFontFamily() {
13719              return $this->FontFamily;
13720          }
13721  
13722          /**
13723           * Returns the current font style.
13724           * @return string current font style
13725           * @access public
13726           * @since 4.3.008 (2008-12-05)
13727           */
13728  		public function getFontStyle() {
13729              return $this->FontStyle;
13730          }
13731  
13732          /**
13733           * Prints a cell (rectangular area) with optional borders, background color and html text string.
13734           * 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 />
13735           * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
13736           * @param float $w Cell width. If 0, the cell extends up to the right margin.
13737           * @param float $h Cell minimum height. The cell extends automatically if needed.
13738           * @param float $x upper-left corner X coordinate
13739           * @param float $y upper-left corner Y coordinate
13740           * @param string $html html text to print. Default value: empty string.
13741           * @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>
13742           * @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>
13743      Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
13744           * @param int $fill Indicates if the cell background must be painted (1) or transparent (0). Default value: 0.
13745           * @param boolean $reseth if true reset the last cell height (default true).
13746           * @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>
13747           * @param boolean $autopadding if true, uses internal padding and automatically adjust it to account for line width.
13748           * @access public
13749           * @uses MultiCell()
13750           * @see Multicell(), writeHTML()
13751           */
13752  		public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=0, $reseth=true, $align='', $autopadding=true) {
13753              return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0);
13754          }
13755  
13756          /**
13757            * Returns the HTML DOM array.
13758            * <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>
13759           * @param string $html html code
13760           * @return array
13761           * @access protected
13762           * @since 3.2.000 (2008-06-20)
13763           */
13764  		protected function getHtmlDomArray($html) {
13765              // define block tags
13766              $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
13767              // remove all unsupported tags (the line below lists all supported tags)
13768              $html = strip_tags($html, '<marker/><a><b><blockquote><body><br><br/><dd><del><div><dl><dt><em><font><form><h1><h2><h3><h4><h5><h6><hr><i><img><input><label><li><ol><option><p><pre><select><small><span><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
13769              //replace some blank characters
13770              $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
13771              $html = preg_replace('/<(table|tr|td|th|tcpdf|blockquote|dd|div|dl|dt|form|h1|h2|h3|h4|h5|h6|br|hr|li|ol|ul|p)([^\>]*)>[\n\r\t]+/', '<\\1\\2>', $html);
13772              $html = preg_replace('@(\r\n|\r)@', "\n", $html);
13773              $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
13774              $html = strtr($html, $repTable);
13775              $offset = 0;
13776              while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
13777                  $html_a = substr($html, 0, $offset);
13778                  $html_b = substr($html, $offset, ($pos - $offset + 6));
13779                  while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
13780                      // preserve newlines on <pre> tag
13781                      $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
13782                  }
13783                  $html = $html_a.$html_b.substr($html, $pos + 6);
13784                  $offset = strlen($html_a.$html_b);
13785              }
13786              $offset = 0;
13787              while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
13788                  $html_a = substr($html, 0, $offset);
13789                  $html_b = substr($html, $offset, ($pos - $offset + 11));
13790                  while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
13791                      // preserve newlines on <textarea> tag
13792                      $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
13793                      $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
13794                  }
13795                  $html = $html_a.$html_b.substr($html, $pos + 11);
13796                  $offset = strlen($html_a.$html_b);
13797              }
13798              $html = preg_replace("'([\s]*)<option'si", "<option", $html);
13799              $html = preg_replace("'</option>([\s]*)'si", "</option>", $html);
13800              $offset = 0;
13801              while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
13802                  $html_a = substr($html, 0, $offset);
13803                  $html_b = substr($html, $offset, ($pos - $offset + 9));
13804                  while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
13805                      $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2\t\\4\r", $html_b);
13806                      $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2\r", $html_b);
13807                  }
13808                  $html = $html_a.$html_b.substr($html, $pos + 9);
13809                  $offset = strlen($html_a.$html_b);
13810              }
13811              $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
13812              $html = preg_replace("'([\s]+)</select>'si", "\" />", $html);
13813              $html = str_replace("\n", ' ', $html);
13814              // restore textarea newlines
13815              $html = str_replace('<TBR>', "\n", $html);
13816              // remove extra spaces from code
13817              $html = preg_replace('/[\s]+<\/(table|tr|td|th|ul|ol|li|dl|dt|dd)>/', '</\\1>', $html);
13818              $html = preg_replace('/[\s]+<(tr|td|th|ul|ol|li|dl|dt|dd|br)/', '<\\1', $html);
13819              $html = preg_replace('/<\/(table|tr|td|th|blockquote|dd|dt|dl|div|dt|h1|h2|h3|h4|h5|h6|hr|li|ol|ul|p)>[\s]+</', '</\\1><', $html);
13820              $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
13821              $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
13822              $html = preg_replace('/[\s]*<img/', ' <img', $html);
13823              $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
13824              $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
13825              $html = preg_replace('/<textarea([^\>]*)>/xi', '<textarea\\1 value="', $html);
13826              $html = preg_replace('/<\/textarea>/', '" />', $html);
13827              // trim string
13828              $html = preg_replace('/^[\s]+/', '', $html);
13829              $html = preg_replace('/[\s]+$/', '', $html);
13830              // pattern for generic tag
13831              $tagpattern = '/(<[^>]+>)/';
13832              // explodes the string
13833              $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
13834              // count elements
13835              $maxel = count($a);
13836              $elkey = 0;
13837              $key = 0;
13838              // create an array of elements
13839              $dom = array();
13840              $dom[$key] = array();
13841              // set first void element
13842              $dom[$key]['tag'] = false;
13843              $dom[$key]['block'] = false;
13844              $dom[$key]['value'] = '';
13845              $dom[$key]['parent'] = 0;
13846              $dom[$key]['fontname'] = $this->FontFamily;
13847              $dom[$key]['fontstyle'] = $this->FontStyle;
13848              $dom[$key]['fontsize'] = $this->FontSizePt;
13849              $dom[$key]['stroke'] = $this->textstrokewidth;
13850              $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
13851              $dom[$key]['clip'] = ($this->textrendermode > 3);
13852              $dom[$key]['line-height'] = $this->cell_height_ratio;
13853              $dom[$key]['bgcolor'] = false;
13854              $dom[$key]['fgcolor'] = $this->fgcolor;
13855              $dom[$key]['strokecolor'] = $this->strokecolor;
13856              $dom[$key]['align'] = '';
13857              $dom[$key]['listtype'] = '';
13858              $dom[$key]['text-indent'] = 0;
13859              $thead = false; // true when we are inside the THEAD tag
13860              ++$key;
13861              $level = array();
13862              array_push($level, 0); // root
13863              while ($elkey < $maxel) {
13864                  $dom[$key] = array();
13865                  $element = $a[$elkey];
13866                  $dom[$key]['elkey'] = $elkey;
13867                  if (preg_match($tagpattern, $element)) {
13868                      // html tag
13869                      $element = substr($element, 1, -1);
13870                      // get tag name
13871                      preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
13872                      $tagname = strtolower($tag[1]);
13873                      // check if we are inside a table header
13874                      if ($tagname == 'thead') {
13875                          if ($element{0} == '/') {
13876                              $thead = false;
13877                          } else {
13878                              $thead = true;
13879                          }
13880                          ++$elkey;
13881                          continue;
13882                      }
13883                      $dom[$key]['tag'] = true;
13884                      $dom[$key]['value'] = $tagname;
13885                      if (in_array($dom[$key]['value'], $blocktags)) {
13886                          $dom[$key]['block'] = true;
13887                      } else {
13888                          $dom[$key]['block'] = false;
13889                      }
13890                      if ($element{0} == '/') {
13891                          // *** closing html tag
13892                          $dom[$key]['opening'] = false;
13893                          $dom[$key]['parent'] = end($level);
13894                          array_pop($level);
13895                          $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
13896                          $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
13897                          $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
13898                          $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
13899                          $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
13900                          $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
13901                          $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
13902                          $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
13903                          $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
13904                          $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
13905                          $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
13906                          if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
13907                              $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
13908                          }
13909                          // set the number of columns in table tag
13910                          if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
13911                              $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
13912                          }
13913                          if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
13914                              $dom[($dom[$key]['parent'])]['content'] = '';
13915                              for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
13916                                  $dom[($dom[$key]['parent'])]['content'] .= $a[$dom[$i]['elkey']];
13917                              }
13918                              $key = $i;
13919                              // mark nested tables
13920                              $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
13921                              // remove thead sections from nested tables
13922                              $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
13923                              $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
13924                          }
13925                          // store header rows on a new table
13926                          if (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['thead'] === true)) {
13927                              if ($this->empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
13928                                  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
13929                              }
13930                              for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
13931                                  $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
13932                              }
13933                              if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
13934                                  $dom[($dom[$key]['parent'])]['attribute'] = array();
13935                              }
13936                              // header elements must be always contained in a single page
13937                              $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
13938                          }
13939                          if (($dom[$key]['value'] == 'table') AND (!$this->empty_string($dom[($dom[$key]['parent'])]['thead']))) {
13940                              // remove the nobr attributes from the table header
13941                              $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
13942                              $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
13943                          }
13944                      } else {
13945                          // *** opening html tag
13946                          $dom[$key]['opening'] = true;
13947                          $dom[$key]['parent'] = end($level);
13948                          if (substr($element, -1, 1) != '/') {
13949                              // not self-closing tag
13950                              array_push($level, $key);
13951                              $dom[$key]['self'] = false;
13952                          } else {
13953                              $dom[$key]['self'] = true;
13954                          }
13955                          // copy some values from parent
13956                          $parentkey = 0;
13957                          if ($key > 0) {
13958                              $parentkey = $dom[$key]['parent'];
13959                              $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
13960                              $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
13961                              $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
13962                              $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
13963                              $dom[$key]['fill'] = $dom[$parentkey]['fill'];
13964                              $dom[$key]['clip'] = $dom[$parentkey]['clip'];
13965                              $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
13966                              $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
13967                              $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
13968                              $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
13969                              $dom[$key]['align'] = $dom[$parentkey]['align'];
13970                              $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
13971                              $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
13972                          }
13973                          // get attributes
13974                          preg_match_all('/([^=\s]*)=["]?([^"]*)["]?/', $element, $attr_array, PREG_PATTERN_ORDER);
13975                          $dom[$key]['attribute'] = array(); // reset attribute array
13976                          while (list($id, $name) = each($attr_array[1])) {
13977                              $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
13978                          }
13979                          // split style attributes
13980                          if (isset($dom[$key]['attribute']['style'])) {
13981                              // get style attributes
13982                              preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
13983                              $dom[$key]['style'] = array(); // reset style attribute array
13984                              while (list($id, $name) = each($style_array[1])) {
13985                                  $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
13986                              }
13987                              // --- get some style attributes ---
13988                              if (isset($dom[$key]['style']['font-family'])) {
13989                                  // font family
13990                                  if (isset($dom[$key]['style']['font-family'])) {
13991                                      $fontslist = preg_split('/[,]/', strtolower($dom[$key]['style']['font-family']));
13992                                      foreach ($fontslist as $font) {
13993                                          $font = trim(strtolower($font));
13994                                          if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
13995                                              $dom[$key]['fontname'] = $font;
13996                                              break;
13997                                          }
13998                                      }
13999                                  }
14000                              }
14001                              // list-style-type
14002                              if (isset($dom[$key]['style']['list-style-type'])) {
14003                                  $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
14004                                  if ($dom[$key]['listtype'] == 'inherit') {
14005                                      $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
14006                                  }
14007                              }
14008                              // text-indent
14009                              if (isset($dom[$key]['style']['text-indent'])) {
14010                                  $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
14011                                  if ($dom[$key]['text-indent'] == 'inherit') {
14012                                      $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
14013                                  }
14014                              }
14015                              // font size
14016                              if (isset($dom[$key]['style']['font-size'])) {
14017                                  $fsize = trim($dom[$key]['style']['font-size']);
14018                                  switch ($fsize) {
14019                                      // absolute-size
14020                                      case 'xx-small': {
14021                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 4;
14022                                          break;
14023                                      }
14024                                      case 'x-small': {
14025                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 3;
14026                                          break;
14027                                      }
14028                                      case 'small': {
14029                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] - 2;
14030                                          break;
14031                                      }
14032                                      case 'medium': {
14033                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'];
14034                                          break;
14035                                      }
14036                                      case 'large': {
14037                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 2;
14038                                          break;
14039                                      }
14040                                      case 'x-large': {
14041                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 4;
14042                                          break;
14043                                      }
14044                                      case 'xx-large': {
14045                                          $dom[$key]['fontsize'] = $dom[0]['fontsize'] + 6;
14046                                          break;
14047                                      }
14048                                      // relative-size
14049                                      case 'smaller': {
14050                                          $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] - 3;
14051                                          break;
14052                                      }
14053                                      case 'larger': {
14054                                          $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'] + 3;
14055                                          break;
14056                                      }
14057                                      default: {
14058                                          $dom[$key]['fontsize'] = $this->getHTMLUnitToUnits($fsize, $dom[$parentkey]['fontsize'], 'pt', true);
14059                                      }
14060                                  }
14061                              }
14062                              // line-height
14063                              if (isset($dom[$key]['style']['line-height'])) {
14064                                  $lineheight = trim($dom[$key]['style']['line-height']);
14065                                  switch ($lineheight) {
14066                                      // A normal line height. This is default
14067                                      case 'normal': {
14068                                          $dom[$key]['line-height'] = $dom[0]['line-height'];
14069                                          break;
14070                                      }
14071                                      default: {
14072                                          if (is_numeric($lineheight)) {
14073                                              $lineheight = $lineheight * 100;
14074                                          }
14075                                          $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
14076                                      }
14077                                  }
14078                              }
14079                              // font style
14080                              if (isset($dom[$key]['style']['font-weight']) AND (strtolower($dom[$key]['style']['font-weight']{0}) == 'b')) {
14081                                  $dom[$key]['fontstyle'] .= 'B';
14082                              }
14083                              if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style']{0}) == 'i')) {
14084                                  $dom[$key]['fontstyle'] .= 'I';
14085                              }
14086                              // font color
14087                              if (isset($dom[$key]['style']['color']) AND (!$this->empty_string($dom[$key]['style']['color']))) {
14088                                  $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['color']);
14089                              }
14090                              // background color
14091                              if (isset($dom[$key]['style']['background-color']) AND (!$this->empty_string($dom[$key]['style']['background-color']))) {
14092                                  $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['style']['background-color']);
14093                              }
14094                              // text-decoration
14095                              if (isset($dom[$key]['style']['text-decoration'])) {
14096                                  $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
14097                                  foreach ($decors as $dec) {
14098                                      $dec = trim($dec);
14099                                      if (!$this->empty_string($dec)) {
14100                                          if ($dec{0} == 'u') {
14101                                              // underline
14102                                              $dom[$key]['fontstyle'] .= 'U';
14103                                          } elseif ($dec{0} == 'l') {
14104                                              // line-trough
14105                                              $dom[$key]['fontstyle'] .= 'D';
14106                                          } elseif ($dec{0} == 'o') {
14107                                              // overline
14108                                              $dom[$key]['fontstyle'] .= 'O';
14109                                          }
14110                                      }
14111                                  }
14112                              }
14113                              // check for width attribute
14114                              if (isset($dom[$key]['style']['width'])) {
14115                                  $dom[$key]['width'] = $dom[$key]['style']['width'];
14116                              }
14117                              // check for height attribute
14118                              if (isset($dom[$key]['style']['height'])) {
14119                                  $dom[$key]['height'] = $dom[$key]['style']['height'];
14120                              }
14121                              // check for text alignment
14122                              if (isset($dom[$key]['style']['text-align'])) {
14123                                  $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align']{0});
14124                              }
14125                              // check for border attribute
14126                              if (isset($dom[$key]['style']['border'])) {
14127                                  $dom[$key]['attribute']['border'] = $dom[$key]['style']['border'];
14128                              }
14129                              // page-break-inside
14130                              if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
14131                                  $dom[$key]['attribute']['nobr'] = 'true';
14132                              }
14133                              // page-break-before
14134                              if (isset($dom[$key]['style']['page-break-before'])) {
14135                                  if ($dom[$key]['style']['page-break-before'] == 'always') {
14136                                      $dom[$key]['attribute']['pagebreak'] = 'true';
14137                                  } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
14138                                      $dom[$key]['attribute']['pagebreak'] = 'left';
14139                                  } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
14140                                      $dom[$key]['attribute']['pagebreak'] = 'right';
14141                                  }
14142                              }
14143                              // page-break-after
14144                              if (isset($dom[$key]['style']['page-break-after'])) {
14145                                  if ($dom[$key]['style']['page-break-after'] == 'always') {
14146                                      $dom[$key]['attribute']['pagebreakafter'] = 'true';
14147                                  } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
14148                                      $dom[$key]['attribute']['pagebreakafter'] = 'left';
14149                                  } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
14150                                      $dom[$key]['attribute']['pagebreakafter'] = 'right';
14151                                  }
14152                              }
14153                          }
14154                          // check for font tag
14155                          if ($dom[$key]['value'] == 'font') {
14156                              // font family
14157                              if (isset($dom[$key]['attribute']['face'])) {
14158                                  $fontslist = preg_split('/[,]/', strtolower($dom[$key]['attribute']['face']));
14159                                  foreach ($fontslist as $font) {
14160                                      $font = trim(strtolower($font));
14161                                      if (in_array($font, $this->fontlist) OR in_array($font, $this->fontkeys)) {
14162                                          $dom[$key]['fontname'] = $font;
14163                                          break;
14164                                      }
14165                                  }
14166                              }
14167                              // font size
14168                              if (isset($dom[$key]['attribute']['size'])) {
14169                                  if ($key > 0) {
14170                                      if ($dom[$key]['attribute']['size']{0} == '+') {
14171                                          $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
14172                                      } elseif ($dom[$key]['attribute']['size']{0} == '-') {
14173                                          $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
14174                                      } else {
14175                                          $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
14176                                      }
14177                                  } else {
14178                                      $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
14179                                  }
14180                              }
14181                          }
14182                          // force natural alignment for lists
14183                          if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
14184                              AND (!isset($dom[$key]['align']) OR $this->empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
14185                              if ($this->rtl) {
14186                                  $dom[$key]['align'] = 'R';
14187                              } else {
14188                                  $dom[$key]['align'] = 'L';
14189                              }
14190                          }
14191                          if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
14192                              $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
14193                          }
14194                          if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
14195                              $dom[$key]['fontstyle'] .= 'B';
14196                          }
14197                          if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
14198                              $dom[$key]['fontstyle'] .= 'I';
14199                          }
14200                          if ($dom[$key]['value'] == 'u') {
14201                              $dom[$key]['fontstyle'] .= 'U';
14202                          }
14203                          if ($dom[$key]['value'] == 'del') {
14204                              $dom[$key]['fontstyle'] .= 'D';
14205                          }
14206                          if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
14207                              $dom[$key]['fontname'] = $this->default_monospaced_font;
14208                          }
14209                          if (($dom[$key]['value']{0} == 'h') AND (intval($dom[$key]['value']{1}) > 0) AND (intval($dom[$key]['value']{1}) < 7)) {
14210                              $headsize = (4 - intval($dom[$key]['value']{1})) * 2;
14211                              $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
14212                              $dom[$key]['fontstyle'] .= 'B';
14213                          }
14214                          if (($dom[$key]['value'] == 'table')) {
14215                              $dom[$key]['rows'] = 0; // number of rows
14216                              $dom[$key]['trids'] = array(); // IDs of TR elements
14217                              $dom[$key]['thead'] = ''; // table header rows
14218                          }
14219                          if (($dom[$key]['value'] == 'tr')) {
14220                              $dom[$key]['cols'] = 0;
14221                              if ($thead) {
14222                                  $dom[$key]['thead'] = true;
14223                                  // rows on thead block are printed as a separate table
14224                              } else {
14225                                  $dom[$key]['thead'] = false;
14226                                  // store the number of rows on table element
14227                                  ++$dom[($dom[$key]['parent'])]['rows'];
14228                                  // store the TR elements IDs on table element
14229                                  array_push($dom[($dom[$key]['parent'])]['trids'], $key);
14230                              }
14231                          }
14232                          if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
14233                              if (isset($dom[$key]['attribute']['colspan'])) {
14234                                  $colspan = intval($dom[$key]['attribute']['colspan']);
14235                              } else {
14236                                  $colspan = 1;
14237                              }
14238                              $dom[$key]['attribute']['colspan'] = $colspan;
14239                              $dom[($dom[$key]['parent'])]['cols'] += $colspan;
14240                          }
14241                          // set foreground color attribute
14242                          if (isset($dom[$key]['attribute']['color']) AND (!$this->empty_string($dom[$key]['attribute']['color']))) {
14243                              $dom[$key]['fgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['color']);
14244                          }
14245                          // set background color attribute
14246                          if (isset($dom[$key]['attribute']['bgcolor']) AND (!$this->empty_string($dom[$key]['attribute']['bgcolor']))) {
14247                              $dom[$key]['bgcolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['bgcolor']);
14248                          }
14249                          // set stroke color attribute
14250                          if (isset($dom[$key]['attribute']['strokecolor']) AND (!$this->empty_string($dom[$key]['attribute']['strokecolor']))) {
14251                              $dom[$key]['strokecolor'] = $this->convertHTMLColorToDec($dom[$key]['attribute']['strokecolor']);
14252                          }
14253                          // check for width attribute
14254                          if (isset($dom[$key]['attribute']['width'])) {
14255                              $dom[$key]['width'] = $dom[$key]['attribute']['width'];
14256                          }
14257                          // check for height attribute
14258                          if (isset($dom[$key]['attribute']['height'])) {
14259                              $dom[$key]['height'] = $dom[$key]['attribute']['height'];
14260                          }
14261                          // check for text alignment
14262                          if (isset($dom[$key]['attribute']['align']) AND (!$this->empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
14263                              $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align']{0});
14264                          }
14265                          // check for text rendering mode (the following attributes do not exist in HTML)
14266                          if (isset($dom[$key]['attribute']['stroke'])) {
14267                              // font stroke width
14268                              $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
14269                          }
14270                          if (isset($dom[$key]['attribute']['fill'])) {
14271                              // font fill
14272                              if ($dom[$key]['attribute']['fill'] == 'true') {
14273                                  $dom[$key]['fill'] = true;
14274                              } else {
14275                                  $dom[$key]['fill'] = false;
14276                              }
14277                          }
14278                          if (isset($dom[$key]['attribute']['clip'])) {
14279                              // clipping mode
14280                              if ($dom[$key]['attribute']['clip'] == 'true') {
14281                                  $dom[$key]['clip'] = true;
14282                              } else {
14283                                  $dom[$key]['clip'] = false;
14284                              }
14285                          }
14286                      } // end opening tag
14287                  } else {
14288                      // text
14289                      $dom[$key]['tag'] = false;
14290                      $dom[$key]['block'] = false;
14291                      $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
14292                      $dom[$key]['parent'] = end($level);
14293                  }
14294                  ++$elkey;
14295                  ++$key;
14296              }
14297              return $dom;
14298          }
14299  
14300          /**
14301           * Returns the string used to find spaces
14302           * @return string
14303           * @access protected
14304           * @author Nicola Asuni
14305           * @since 4.8.024 (2010-01-15)
14306           */
14307  		protected function getSpaceString() {
14308              $spacestr = chr(32);
14309              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
14310                  $spacestr = chr(0).chr(32);
14311              }
14312              return $spacestr;
14313          }
14314  
14315          /**
14316           * Allows to preserve some HTML formatting (limited support).<br />
14317           * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
14318           * 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, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
14319           * @param string $html text to display
14320           * @param boolean $ln if true add a new line after text (default = true)
14321           * @param int $fill Indicates if the background must be painted (true) or transparent (false).
14322           * @param boolean $reseth if true reset the last cell height (default false).
14323           * @param boolean $cell if true add the default cMargin space to each Write (default false).
14324           * @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>
14325           * @access public
14326           */
14327  		public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
14328              $gvars = $this->getGraphicVars();
14329              // store current values
14330              $prevPage = $this->page;
14331              $prevlMargin = $this->lMargin;
14332              $prevrMargin = $this->rMargin;
14333              $curfontname = $this->FontFamily;
14334              $curfontstyle = $this->FontStyle;
14335              $curfontsize = $this->FontSizePt;
14336              $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
14337              $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
14338              $this->newline = true;
14339              $startlinepage = $this->page;
14340              $minstartliney = $this->y;
14341              $maxbottomliney = 0;
14342              $startlinex = $this->x;
14343              $startliney = $this->y;
14344              $yshift = 0;
14345              $newline = true;
14346              $loop = 0;
14347              $curpos = 0;
14348              $this_method_vars = array();
14349              $undo = false;
14350              $fontaligned = false;
14351              $this->premode = false;
14352              if (isset($this->PageAnnots[$this->page])) {
14353                  $pask = count($this->PageAnnots[$this->page]);
14354              } else {
14355                  $pask = 0;
14356              }
14357              if (!$this->InFooter) {
14358                  if (isset($this->footerlen[$this->page])) {
14359                      $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
14360                  } else {
14361                      $this->footerpos[$this->page] = $this->pagelen[$this->page];
14362                  }
14363                  $startlinepos = $this->footerpos[$this->page];
14364              } else {
14365                  $startlinepos = $this->pagelen[$this->page];
14366              }
14367              $lalign = $align;
14368              $plalign = $align;
14369              if ($this->rtl) {
14370                  $w = $this->x - $this->lMargin;
14371              } else {
14372                  $w = $this->w - $this->rMargin - $this->x;
14373              }
14374              $w -= (2 * $this->cMargin);
14375              if ($cell) {
14376                  if ($this->rtl) {
14377                      $this->x -= $this->cMargin;
14378                  } else {
14379                      $this->x += $this->cMargin;
14380                  }
14381              }
14382              if ($this->customlistindent >= 0) {
14383                  $this->listindent = $this->customlistindent;
14384              } else {
14385                  $this->listindent = $this->GetStringWidth('0000');
14386              }
14387              $this->listindentlevel = 0;
14388              // save previous states
14389              $prev_cell_height_ratio = $this->cell_height_ratio;
14390              $prev_listnum = $this->listnum;
14391              $prev_listordered = $this->listordered;
14392              $prev_listcount = $this->listcount;
14393              $prev_lispacer = $this->lispacer;
14394              $this->listnum = 0;
14395              $this->listordered = array();
14396              $this->listcount = array();
14397              $this->lispacer = '';
14398              if (($this->empty_string($this->lasth)) OR ($reseth)) {
14399                  //set row height
14400                  $this->lasth = $this->FontSize * $this->cell_height_ratio;
14401              }
14402              $dom = $this->getHtmlDomArray($html);
14403              $maxel = count($dom);
14404              $key = 0;
14405              while ($key < $maxel) {
14406                  if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
14407                      // check for pagebreak
14408                      if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
14409                          // add a page (or trig AcceptPageBreak() for multicolumn mode)
14410                          $this->checkPageBreak($this->PageBreakTrigger + 1);
14411                      }
14412                      if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
14413                          OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
14414                          // add a page (or trig AcceptPageBreak() for multicolumn mode)
14415                          $this->checkPageBreak($this->PageBreakTrigger + 1);
14416                      }
14417                  }
14418                  if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
14419                      if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
14420                          $dom[$key]['attribute']['nobr'] = false;
14421                      } else {
14422                          // store current object
14423                          $this->startTransaction();
14424                          // save this method vars
14425                          $this_method_vars['html'] = $html;
14426                          $this_method_vars['ln'] = $ln;
14427                          $this_method_vars['fill'] = $fill;
14428                          $this_method_vars['reseth'] = $reseth;
14429                          $this_method_vars['cell'] = $cell;
14430                          $this_method_vars['align'] = $align;
14431                          $this_method_vars['gvars'] = $gvars;
14432                          $this_method_vars['prevPage'] = $prevPage;
14433                          $this_method_vars['prevlMargin'] = $prevlMargin;
14434                          $this_method_vars['prevrMargin'] = $prevrMargin;
14435                          $this_method_vars['curfontname'] = $curfontname;
14436                          $this_method_vars['curfontstyle'] = $curfontstyle;
14437                          $this_method_vars['curfontsize'] = $curfontsize;
14438                          $this_method_vars['curfontascent'] = $curfontascent;
14439                          $this_method_vars['curfontdescent'] = $curfontdescent;
14440                          $this_method_vars['minstartliney'] = $minstartliney;
14441                          $this_method_vars['maxbottomliney'] = $maxbottomliney;
14442                          $this_method_vars['yshift'] = $yshift;
14443                          $this_method_vars['startlinepage'] = $startlinepage;
14444                          $this_method_vars['startlinepos'] = $startlinepos;
14445                          $this_method_vars['startlinex'] = $startlinex;
14446                          $this_method_vars['startliney'] = $startliney;
14447                          $this_method_vars['newline'] = $newline;
14448                          $this_method_vars['loop'] = $loop;
14449                          $this_method_vars['curpos'] = $curpos;
14450                          $this_method_vars['pask'] = $pask;
14451                          $this_method_vars['lalign'] = $lalign;
14452                          $this_method_vars['plalign'] = $plalign;
14453                          $this_method_vars['w'] = $w;
14454                          $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
14455                          $this_method_vars['prev_listnum'] = $prev_listnum;
14456                          $this_method_vars['prev_listordered'] = $prev_listordered;
14457                          $this_method_vars['prev_listcount'] = $prev_listcount;
14458                          $this_method_vars['prev_lispacer'] = $prev_lispacer;
14459                          $this_method_vars['fontaligned'] = $fontaligned;
14460                          $this_method_vars['key'] = $key;
14461                          $this_method_vars['dom'] = $dom;
14462                      }
14463                  }
14464                  // print THEAD block
14465                  if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
14466                      if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !$this->empty_string($dom[$dom[$key]['parent']]['thead'])) {
14467                          $this->inthead = true;
14468                          // print table header (thead)
14469                          $this->writeHTML($this->thead, false, false, false, false, '');
14470                          if (($this->start_transaction_page == ($this->numpages - 1)) OR ($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
14471                              // restore previous object
14472                              $this->rollbackTransaction(true);
14473                              // restore previous values
14474                              foreach ($this_method_vars as $vkey => $vval) {
14475                                  $$vkey = $vval;
14476                              }
14477                              // add a page (or trig AcceptPageBreak() for multicolumn mode)
14478                              $pre_y = $this->y;
14479                              if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
14480                                  // fix for multicolumn mode
14481                                  $startliney = $this->y;
14482                              }
14483                              $this->start_transaction_page = $this->page;
14484                              $this->start_transaction_y = $this->y;
14485                          }
14486                      }
14487                      // move $key index forward to skip THEAD block
14488                      while ( ($key < $maxel) AND (!(
14489                          ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
14490                          OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
14491                          ++$key;
14492                      }
14493                  }
14494                  if ($dom[$key]['tag'] OR ($key == 0)) {
14495                      if (isset($dom[$key]['line-height'])) {
14496                          // set line height
14497                          $this->cell_height_ratio = $dom[$key]['line-height'];
14498                          $this->lasth = $this->FontSize * $this->cell_height_ratio;
14499                      }
14500                      if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
14501                          $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
14502                      }
14503                      // vertically align image in line
14504                      if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['attribute']['height'])) AND ($dom[$key]['attribute']['height'] > 0)) {
14505                          // get image height
14506                          $imgh = $this->getHTMLUnitToUnits($dom[$key]['attribute']['height'], $this->lasth, 'px');
14507                          // check for automatic line break
14508                          $autolinebreak = false;
14509                          if (isset($dom[$key]['attribute']['width']) AND ($dom[$key]['attribute']['width'] > 0)) {
14510                              $imgw = $this->getHTMLUnitToUnits($dom[$key]['attribute']['width'], 1, 'px', false);
14511                              if (($this->rtl AND (($this->x - $imgw) < ($this->lMargin + $this->cMargin)))
14512                                  OR (!$this->rtl AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cMargin)))) {
14513                                  // add automatic line break
14514                                  $autolinebreak = true;
14515                                  $this->Ln('', $cell);
14516                                  // go back to evaluate this line break
14517                                  --$key;
14518                              }
14519                          }
14520                          if (!$autolinebreak) {
14521                              if (!$this->InFooter) {
14522                                  $pre_y = $this->y;
14523                                  // check for page break
14524                                  if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
14525                                      // fix for multicolumn mode
14526                                      $startliney = $this->y;
14527                                  }
14528                              }
14529                              if ($this->page > $startlinepage) {
14530                                  // fix line splitted over two pages
14531                                  if (isset($this->footerlen[$startlinepage])) {
14532                                      $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
14533                                  }
14534                                  // line to be moved one page forward
14535                                  $pagebuff = $this->getPageBuffer($startlinepage);
14536                                  $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
14537                                  $tstart = substr($pagebuff, 0, $startlinepos);
14538                                  $tend = substr($this->getPageBuffer($startlinepage), $curpos);
14539                                  // remove line from previous page
14540                                  $this->setPageBuffer($startlinepage, $tstart.''.$tend);
14541                                  $pagebuff = $this->getPageBuffer($this->page);
14542                                  $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
14543                                  $tend = substr($pagebuff, $this->cntmrk[$this->page]);
14544                                  // add line start to current page
14545                                  $yshift = $minstartliney - $this->y;
14546                                  if ($fontaligned) {
14547                                      $yshift += ($curfontsize / $this->k);
14548                                  }
14549                                  $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
14550                                  $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
14551                                  // shift the annotations and links
14552                                  if (isset($this->PageAnnots[$this->page])) {
14553                                      $next_pask = count($this->PageAnnots[$this->page]);
14554                                  } else {
14555                                      $next_pask = 0;
14556                                  }
14557                                  if (isset($this->PageAnnots[$startlinepage])) {
14558                                      foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
14559                                          if ($pak >= $pask) {
14560                                              $this->PageAnnots[$this->page][] = $pac;
14561                                              unset($this->PageAnnots[$startlinepage][$pak]);
14562                                              $npak = count($this->PageAnnots[$this->page]) - 1;
14563                                              $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
14564                                          }
14565                                      }
14566                                  }
14567                                  $pask = $next_pask;
14568                                  $startlinepos = $this->cntmrk[$this->page];
14569                                  $startlinepage = $this->page;
14570                                  $startliney = $this->y;
14571                              }
14572                              $this->y += ((($curfontsize * $this->cell_height_ratio / $this->k) + $curfontascent - $curfontdescent) / 2) - $imgh;
14573                              $minstartliney = min($this->y, $minstartliney);
14574                              $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
14575                          }
14576                      } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize'])) {
14577                          // account for different font size
14578                          $pfontname = $curfontname;
14579                          $pfontstyle = $curfontstyle;
14580                          $pfontsize = $curfontsize;
14581                          $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
14582                          $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
14583                          $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
14584                          $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
14585                          $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
14586                          if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)) {
14587                              if (is_numeric($fontsize) AND ($fontsize >= 0)
14588                                  AND is_numeric($curfontsize) AND ($curfontsize >= 0)
14589                                  AND ($fontsize != $curfontsize) AND (!$this->newline)
14590                                  AND ($key < ($maxel - 1))
14591                                  ) {
14592                                  if ((!$this->newline) AND ($this->page > $startlinepage)) {
14593                                      // fix lines splitted over two pages
14594                                      if (isset($this->footerlen[$startlinepage])) {
14595                                          $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
14596                                      }
14597                                      // line to be moved one page forward
14598                                      $pagebuff = $this->getPageBuffer($startlinepage);
14599                                      $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
14600                                      $tstart = substr($pagebuff, 0, $startlinepos);
14601                                      $tend = substr($this->getPageBuffer($startlinepage), $curpos);
14602                                      // remove line start from previous page
14603                                      $this->setPageBuffer($startlinepage, $tstart.''.$tend);
14604                                      $pagebuff = $this->getPageBuffer($this->page);
14605                                      $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
14606                                      $tend = substr($pagebuff, $this->cntmrk[$this->page]);
14607                                      // add line start to current page
14608                                      $yshift = $minstartliney - $this->y;
14609                                      $try = sprintf('1 0 0 1 0 %.3F cm', ($yshift * $this->k));
14610                                      $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
14611                                      // shift the annotations and links
14612                                      if (isset($this->PageAnnots[$this->page])) {
14613                                          $next_pask = count($this->PageAnnots[$this->page]);
14614                                      } else {
14615                                          $next_pask = 0;
14616                                      }
14617                                      if (isset($this->PageAnnots[$startlinepage])) {
14618                                          foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
14619                                              if ($pak >= $pask) {
14620                                                  $this->PageAnnots[$this->page][] = $pac;
14621                                                  unset($this->PageAnnots[$startlinepage][$pak]);
14622                                                  $npak = count($this->PageAnnots[$this->page]) - 1;
14623                                                  $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
14624                                              }
14625                                          }
14626                                      }
14627                                      $pask = $next_pask;
14628                                      $startlinepos = $this->cntmrk[$this->page];
14629                                      $startlinepage = $this->page;
14630                                      $startliney = $this->y;
14631                                  }
14632                                  if (!$dom[$key]['block']) {
14633                                      $this->y += ((($curfontsize - $fontsize) * $this->cell_height_ratio / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
14634                                      $minstartliney = min($this->y, $minstartliney);
14635                                      $maxbottomliney = max(($this->y + (($fontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
14636                                  }
14637                                  $fontaligned = true;
14638                              }
14639                              $this->SetFont($fontname, $fontstyle, $fontsize);
14640                              $this->lasth = $this->FontSize * $this->cell_height_ratio;
14641                              $curfontname = $fontname;
14642                              $curfontstyle = $fontstyle;
14643                              $curfontsize = $fontsize;
14644                              $curfontascent = $fontascent;
14645                              $curfontdescent = $fontdescent;
14646                          }
14647                      }
14648                      // set text rendering mode
14649                      $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
14650                      $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
14651                      $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
14652                      $this->setTextRenderingMode($textstroke, $textfill, $textclip);
14653                      if (($plalign == 'J') AND $dom[$key]['block']) {
14654                          $plalign = '';
14655                      }
14656                      // get current position on page buffer
14657                      $curpos = $this->pagelen[$startlinepage];
14658                      if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
14659                          $this->SetFillColorArray($dom[$key]['bgcolor']);
14660                          $wfill = true;
14661                      } else {
14662                          $wfill = $fill | false;
14663                      }
14664                      if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
14665                          $this->SetTextColorArray($dom[$key]['fgcolor']);
14666                      }
14667                      if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
14668                          $this->SetDrawColorArray($dom[$key]['strokecolor']);
14669                      }
14670                      if (isset($dom[$key]['align'])) {
14671                          $lalign = $dom[$key]['align'];
14672                      }
14673                      if ($this->empty_string($lalign)) {
14674                          $lalign = $align;
14675                      }
14676                  }
14677                  // align lines
14678                  if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
14679                      $newline = true;
14680                      $fontaligned = false;
14681                      // we are at the beginning of a new line
14682                      if (isset($startlinex)) {
14683                          $yshift = $minstartliney - $startliney;
14684                          if (($yshift > 0) OR ($this->page > $startlinepage)) {
14685                              $yshift = 0;
14686                          }
14687                          $t_x = 0;
14688                          // the last line must be shifted to be aligned as requested
14689                          $linew = abs($this->endlinex - $startlinex);
14690                          $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
14691                          if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
14692                              $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
14693                              $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
14694                          } elseif (isset($opentagpos)) {
14695                              $midpos = $opentagpos;
14696                          } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
14697                              $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
14698                              $midpos = $this->footerpos[$startlinepage];
14699                          } else {
14700                              $midpos = 0;
14701                          }
14702                          if ($midpos > 0) {
14703                              $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
14704                              $pend = substr($this->getPageBuffer($startlinepage), $midpos);
14705                          } else {
14706                              $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
14707                              $pend = '';
14708                          }
14709                          if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
14710                              // calculate shifting amount
14711                              $tw = $w;
14712                              if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
14713                                  $tw += $this->cMargin;
14714                              }
14715                              if ($this->lMargin != $prevlMargin) {
14716                                  $tw += ($prevlMargin - $this->lMargin);
14717                              }
14718                              if ($this->rMargin != $prevrMargin) {
14719                                  $tw += ($prevrMargin - $this->rMargin);
14720                              }
14721                              $one_space_width = $this->GetStringWidth(chr(32));
14722                              $mdiff = abs($tw - $linew);
14723                              if ($plalign == 'C') {
14724                                  if ($this->rtl) {
14725                                      $t_x = -($mdiff / 2);
14726                                  } else {
14727                                      $t_x = ($mdiff / 2);
14728                                  }
14729                              } elseif (($plalign == 'R') AND (!$this->rtl)) {
14730                                  // right alignment on LTR document
14731                                  if (intval($this->revstrpos($pmid, ')]')) == (intval($this->revstrpos($pmid, ' )]')) + 1)) {
14732                                      // remove last space (if any)
14733                                      $linew -= $one_space_width;
14734                                      $mdiff = abs($tw - $linew);
14735                                  }
14736                                  $t_x = $mdiff;
14737                              } elseif (($plalign == 'L') AND ($this->rtl)) {
14738                                  // left alignment on RTL document
14739                                  if (($this->revstrpos($pmid, '[(') > 0) AND ((intval($this->revstrpos($pmid, '[( ')) == intval($this->revstrpos($pmid, '[('))) OR (intval($this->revstrpos($pmid, '[('.chr(0).chr(32))) == intval($this->revstrpos($pmid, '[('))))) {
14740                                      // remove first space (if any)
14741                                      $linew -= $one_space_width;
14742                                  }
14743                                  if ((strpos($pmid, '[(') > 0) AND (intval(strpos($pmid, '[(')) == (intval($this->revstrpos($pmid, '[('))))) {
14744                                      // remove last space (if any)
14745                                      $linew -= $one_space_width;
14746                                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
14747                                          $linew -= $one_space_width;
14748                                      }
14749                                  }
14750                                  $mdiff = abs($tw - $linew);
14751                                  $t_x = -$mdiff;
14752                              } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
14753                                  // Justification
14754                                  if ($this->isRTLTextDir()) {
14755                                      $t_x = $this->lMargin - $this->endlinex + $this->cMargin;
14756                                  }
14757                                  $no = 0; // spaces without trim
14758                                  $ns = 0; // spaces with trim
14759                                  $pmidtemp = $pmid;
14760                                  // escape special characters
14761                                  $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
14762                                  $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
14763                                  // search spaces
14764                                  if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
14765                                      $spacestr = $this->getSpaceString();
14766                                      $maxkk = count($lnstring[1]) - 1;
14767                                      for ($kk=0; $kk <= $maxkk; ++$kk) {
14768                                          // restore special characters
14769                                          $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
14770                                          $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
14771                                          if ($kk == $maxkk) {
14772                                              if ($this->isRTLTextDir()) {
14773                                                  $tvalue = ltrim($lnstring[1][$kk]);
14774                                              } else {
14775                                                  $tvalue = rtrim($lnstring[1][$kk]);
14776                                              }
14777                                          } else {
14778                                              $tvalue = $lnstring[1][$kk];
14779                                          }
14780                                          // store number of spaces on the strings
14781                                          $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
14782                                          $lnstring[3][$kk] = substr_count($tvalue, $spacestr);
14783                                          // count total spaces on line
14784                                          $no += $lnstring[2][$kk];
14785                                          $ns += $lnstring[3][$kk];
14786                                          $lnstring[4][$kk] = $no;
14787                                          $lnstring[5][$kk] = $ns;
14788                                      }
14789                                      if ($this->isRTLTextDir()) {
14790                                          $t_x = $this->lMargin - $this->endlinex + $this->cMargin - (($no - $ns) * $one_space_width);
14791                                      }
14792                                      // calculate additional space to add to each space
14793                                      $spacelen = $one_space_width;
14794                                      $spacewidth = ((($tw - $linew) + (($no - $ns) * $spacelen)) / ($ns?$ns:1)) * $this->k;
14795                                      $spacewidthu = -1000 * (($tw - $linew) + ($no * $spacelen)) / ($ns?$ns:1) / $this->FontSize;
14796                                      $nsmax = $ns;
14797                                      $ns = 0;
14798                                      reset($lnstring);
14799                                      $offset = 0;
14800                                      $strcount = 0;
14801                                      $prev_epsposbeg = 0;
14802                                      $textpos = 0;
14803                                      if ($this->isRTLTextDir()) {
14804                                          $textpos = $this->wPt;
14805                                      }
14806                                      global $spacew;
14807                                      while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
14808                                          // check if we are inside a string section '[( ... )]'
14809                                          $stroffset = strpos($pmid, '[(', $offset);
14810                                          if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
14811                                              // set offset to the end of string section
14812                                              $offset = strpos($pmid, ')]', $stroffset);
14813                                              while (($offset !== false) AND ($pmid{($offset - 1)} == '\\')) {
14814                                                  $offset = strpos($pmid, ')]', ($offset + 1));
14815                                              }
14816                                              if ($offset === false) {
14817                                                  $this->Error('HTML Justification: malformed PDF code.');
14818                                              }
14819                                              continue;
14820                                          }
14821                                          if ($this->isRTLTextDir()) {
14822                                              $spacew = ($spacewidth * ($nsmax - $ns));
14823                                          } else {
14824                                              $spacew = ($spacewidth * $ns);
14825                                          }
14826                                          $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
14827                                          $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
14828                                          $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset) + strlen($this->epsmarker.'Q');
14829                                          if ((($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend))
14830                                              OR (($epsposbeg === false) AND ($epsposend > 0) AND ($offset < $epsposend))) {
14831                                              // shift EPS images
14832                                              $trx = sprintf('1 0 0 1 %.3F 0 cm', $spacew);
14833                                              $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
14834                                              $pmid_b = substr($pmid, 0, $epsposbeg);
14835                                              $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
14836                                              $pmid_e = substr($pmid, $epsposend);
14837                                              $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
14838                                              $offset = $epsposend;
14839                                              continue;
14840  
14841                                          }
14842                                          $prev_epsposbeg = $epsposbeg;
14843                                          $currentxpos = 0;
14844                                          // shift blocks of code
14845                                          switch ($strpiece[2][0]) {
14846                                              case 'Td':
14847                                              case 'cm':
14848                                              case 'm':
14849                                              case 'l': {
14850                                                  // get current X position
14851                                                  preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
14852                                                  $currentxpos = $xmatches[1];
14853                                                  $textpos = $currentxpos;
14854                                                  if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
14855                                                      if ($strcount == $maxkk) {
14856                                                          if ($this->isRTLTextDir()) {
14857                                                              $tvalue = $lnstring[1][$strcount];
14858                                                          } else {
14859                                                              $tvalue = rtrim($lnstring[1][$strcount]);
14860                                                          }
14861                                                      } else {
14862                                                          $tvalue = $lnstring[1][$strcount];
14863                                                      }
14864                                                      $ns += substr_count($tvalue, $spacestr);
14865                                                      ++$strcount;
14866                                                  }
14867                                                  if ($this->isRTLTextDir()) {
14868                                                      $spacew = ($spacewidth * ($nsmax - $ns));
14869                                                  }
14870                                                  // justify block
14871                                                  $pmid = preg_replace_callback('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x',
14872                                                      create_function('$matches', 'global $spacew;
14873                                                      $newx = sprintf("%.2F",(floatval($matches[1]) + $spacew));
14874                                                      return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
14875                                                  break;
14876                                              }
14877                                              case 're': {
14878                                                  // justify block
14879                                                  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
14880                                                  $currentxpos = $xmatches[1];
14881                                                  global $x_diff, $w_diff;
14882                                                  $x_diff = 0;
14883                                                  $w_diff = 0;
14884                                                  if ($this->isRTLTextDir()) { // RTL
14885                                                      if ($currentxpos < $textpos) {
14886                                                          $x_diff = ($spacewidth * ($nsmax - $lnstring[5][$strcount]));
14887                                                          $w_diff = ($spacewidth * $lnstring[3][$strcount]);
14888                                                      } else {
14889                                                          if ($strcount > 0) {
14890                                                              $x_diff = ($spacewidth * ($nsmax - $lnstring[5][($strcount - 1)]));
14891                                                              $w_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
14892                                                          }
14893                                                      }
14894                                                  } else { // LTR
14895                                                      if ($currentxpos > $textpos) {
14896                                                          if ($strcount > 0) {
14897                                                              $x_diff = ($spacewidth * $lnstring[4][($strcount - 1)]);
14898                                                          }
14899                                                          $w_diff = ($spacewidth * $lnstring[3][$strcount]);
14900                                                      } else {
14901                                                          if ($strcount > 1) {
14902                                                              $x_diff = ($spacewidth * $lnstring[4][($strcount - 2)]);
14903                                                          }
14904                                                          if ($strcount > 0) {
14905                                                              $w_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
14906                                                          }
14907                                                      }
14908                                                  }
14909                                                  $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x',
14910                                                      create_function('$matches', 'global $x_diff, $w_diff;
14911                                                      $newx = sprintf("%.2F",(floatval($matches[1]) + $x_diff));
14912                                                      $neww = sprintf("%.2F",(floatval($matches[3]) + $w_diff));
14913                                                      return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
14914                                                  break;
14915                                              }
14916                                              case 'c': {
14917                                                  // get current X position
14918                                                  preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $xmatches);
14919                                                  $currentxpos = $xmatches[1];
14920                                                  // justify block
14921                                                  $pmid = preg_replace_callback('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x',
14922                                                      create_function('$matches', 'global $spacew;
14923                                                      $newx1 = sprintf("%.3F",(floatval($matches[1]) + $spacew));
14924                                                      $newx2 = sprintf("%.3F",(floatval($matches[3]) + $spacew));
14925                                                      $newx3 = sprintf("%.3F",(floatval($matches[5]) + $spacew));
14926                                                      return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
14927                                                  break;
14928                                              }
14929                                          }
14930                                          // shift the annotations and links
14931                                          if (isset($this->PageAnnots[$this->page])) {
14932                                              $cxpos = ($currentxpos / $this->k);
14933                                              $lmpos = ($this->lMargin + $this->cMargin + $this->feps);
14934                                              foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
14935                                                  if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
14936                                                      if ($cxpos > $lmpos) {
14937                                                          $this->PageAnnots[$this->page][$pak]['x'] += (($spacew - $one_space_width) / $this->k);
14938                                                          $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
14939                                                      } else {
14940                                                          $this->PageAnnots[$this->page][$pak]['w'] += ((($spacewidth * $pac['numspaces']) - $one_space_width) / $this->k);
14941                                                      }
14942                                                      break;
14943                                                  }
14944                                              }
14945                                          }
14946                                      } // end of while
14947                                      // remove markers
14948                                      $pmid = str_replace('x*#!#*x', '', $pmid);
14949                                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
14950                                          // multibyte characters
14951                                          $spacew = $spacewidthu;
14952                                          $pmidtemp = $pmid;
14953                                          // escape special characters
14954                                          $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
14955                                          $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
14956                                          $pmid = preg_replace_callback("/\[\(([^\)]*)\)\]/x",
14957                                                      create_function('$matches', 'global $spacew;
14958                                                      $matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
14959                                                      $matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
14960                                                      return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%.3F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
14961                                          $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
14962                                          $endlinepos = strlen($pstart."\n".$pmid."\n");
14963                                      } else {
14964                                          // non-unicode (single-byte characters)
14965                                          $rs = sprintf('%.3F Tw', $spacewidth);
14966                                          $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
14967                                          $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
14968                                          $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
14969                                      }
14970                                  }
14971                              } // end of J
14972                          } // end if $startlinex
14973                          if (($t_x != 0) OR ($yshift < 0)) {
14974                              // shift the line
14975                              $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
14976                              $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
14977                              $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
14978                              // shift the annotations and links
14979                              if (isset($this->PageAnnots[$this->page])) {
14980                                  foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
14981                                      if ($pak >= $pask) {
14982                                          $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
14983                                          $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
14984                                      }
14985                                  }
14986                              }
14987                              $this->y -= $yshift;
14988                          }
14989                      }
14990                      $this->newline = false;
14991                      $pbrk = $this->checkPageBreak($this->lasth);
14992                      $startlinex = $this->x;
14993                      $startliney = $this->y;
14994                      $minstartliney = $startliney;
14995                      $maxbottomliney = ($this->y + (($fontsize * $this->cell_height_ratio) / $this->k));
14996                      $startlinepage = $this->page;
14997                      if (isset($endlinepos) AND (!$pbrk)) {
14998                          $startlinepos = $endlinepos;
14999                      } else {
15000                          if (!$this->InFooter) {
15001                              if (isset($this->footerlen[$this->page])) {
15002                                  $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
15003                              } else {
15004                                  $this->footerpos[$this->page] = $this->pagelen[$this->page];
15005                              }
15006                              $startlinepos = $this->footerpos[$this->page];
15007                          } else {
15008                              $startlinepos = $this->pagelen[$this->page];
15009                          }
15010                      }
15011                      unset($endlinepos);
15012                      $plalign = $lalign;
15013                      if (isset($this->PageAnnots[$this->page])) {
15014                          $pask = count($this->PageAnnots[$this->page]);
15015                      } else {
15016                          $pask = 0;
15017                      }
15018                      $this->SetFont($fontname, $fontstyle, $fontsize);
15019                      if ($wfill) {
15020                          $this->SetFillColorArray($this->bgcolor);
15021                      }
15022                  } // end newline
15023                  if (isset($opentagpos)) {
15024                      unset($opentagpos);
15025                  }
15026                  if ($dom[$key]['tag']) {
15027                      if ($dom[$key]['opening']) {
15028                          // get text indentation (if any)
15029                          if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
15030                              $this->textindent = $dom[$key]['text-indent'];
15031                              $this->newline = true;
15032                          }
15033                          if ($dom[$key]['value'] == 'table') {
15034                              // available page width
15035                              if ($this->rtl) {
15036                                  $wtmp = $this->x - $this->lMargin;
15037                              } else {
15038                                  $wtmp = $this->w - $this->rMargin - $this->x;
15039                              }
15040                              if (isset($dom[$key]['attribute']['nested']) AND ($dom[$key]['attribute']['nested'] == 'true')) {
15041                                  // add margin for nested tables
15042                                  $wtmp -= $this->cMargin;
15043                              }
15044                              // table width
15045                              if (isset($dom[$key]['width'])) {
15046                                  $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
15047                              } else {
15048                                  $table_width = $wtmp;
15049                              }
15050                          }
15051                          if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
15052                              $trid = $dom[$key]['parent'];
15053                              $table_el = $dom[$trid]['parent'];
15054                              if (!isset($dom[$table_el]['cols'])) {
15055                                  $dom[$table_el]['cols'] = $dom[$trid]['cols'];
15056                              }
15057                              $oldmargin = $this->cMargin;
15058                              if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
15059                                  $currentcmargin = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
15060                              } else {
15061                                  $currentcmargin = 0;
15062                              }
15063                              $this->cMargin = $currentcmargin;
15064                              if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'])) {
15065                                  $cellspacing = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellspacing'], 1, 'px');
15066                              } else {
15067                                  $cellspacing = 0;
15068                              }
15069                              if ($this->rtl) {
15070                                  $cellspacingx = -$cellspacing;
15071                              } else {
15072                                  $cellspacingx = $cellspacing;
15073                              }
15074                              $colspan = $dom[$key]['attribute']['colspan'];
15075                              $table_columns_width = ($table_width - ($cellspacing * ($dom[$table_el]['cols'] - 1)));
15076                              $wtmp = ($colspan * ($table_columns_width / $dom[$table_el]['cols'])) + (($colspan - 1) * $cellspacing);
15077                              if (isset($dom[$key]['width'])) {
15078                                  $cellw = $this->getHTMLUnitToUnits($dom[$key]['width'], $table_columns_width, 'px');
15079                              } else {
15080                                  $cellw = $wtmp;
15081                              }
15082                              if (isset($dom[$key]['height'])) {
15083                                  // minimum cell height
15084                                  $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
15085                              } else {
15086                                  $cellh = 0;
15087                              }
15088                              if (isset($dom[$key]['content'])) {
15089                                  $cell_content = $dom[$key]['content'];
15090                              } else {
15091                                  $cell_content = '&nbsp;';
15092                              }
15093                              $tagtype = $dom[$key]['value'];
15094                              $parentid = $key;
15095                              while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
15096                                  // move $key index forward
15097                                  ++$key;
15098                              }
15099                              if (!isset($dom[$trid]['startpage'])) {
15100                                  $dom[$trid]['startpage'] = $this->page;
15101                              } else {
15102                                  $this->setPage($dom[$trid]['startpage']);
15103                              }
15104                              if (!isset($dom[$trid]['starty'])) {
15105                                  $dom[$trid]['starty'] = $this->y;
15106                              } else {
15107                                  $this->y = $dom[$trid]['starty'];
15108                              }
15109                              if (!isset($dom[$trid]['startx'])) {
15110                                  $dom[$trid]['startx'] = $this->x;
15111                              } else {
15112                                  $this->x += ($cellspacingx / 2);
15113                              }
15114                              if (isset($dom[$parentid]['attribute']['rowspan'])) {
15115                                  $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
15116                              } else {
15117                                  $rowspan = 1;
15118                              }
15119                              // skip row-spanned cells started on the previous rows
15120                              if (isset($dom[$table_el]['rowspans'])) {
15121                                  $rsk = 0;
15122                                  $rskmax = count($dom[$table_el]['rowspans']);
15123                                  while ($rsk < $rskmax) {
15124                                      $trwsp = $dom[$table_el]['rowspans'][$rsk];
15125                                      $rsstartx = $trwsp['startx'];
15126                                      $rsendx = $trwsp['endx'];
15127                                      // account for margin changes
15128                                      if ($trwsp['startpage'] < $this->page) {
15129                                          if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
15130                                              $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
15131                                              $rsstartx -= $dl;
15132                                              $rsendx -= $dl;
15133                                          } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
15134                                              $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
15135                                              $rsstartx += $dl;
15136                                              $rsendx += $dl;
15137                                          }
15138                                      }
15139                                      if  (($trwsp['rowspan'] > 0)
15140                                          AND ($rsstartx > ($this->x - $cellspacing - $currentcmargin - $this->feps))
15141                                          AND ($rsstartx < ($this->x + $cellspacing + $currentcmargin + $this->feps))
15142                                          AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page))) {
15143                                          // set the starting X position of the current cell
15144                                          $this->x = $rsendx + $cellspacingx;
15145                                          if (($trwsp['rowspan'] == 1)
15146                                              AND (isset($dom[$trid]['endy']))
15147                                              AND (isset($dom[$trid]['endpage']))
15148                                              AND ($trwsp['endpage'] == $dom[$trid]['endpage'])) {
15149                                              // set ending Y position for row
15150                                              $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
15151                                              $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
15152                                          }
15153                                          $rsk = 0;
15154                                      } else {
15155                                          ++$rsk;
15156                                      }
15157                                  }
15158                              }
15159                              // add rowspan information to table element
15160                              if ($rowspan > 1) {
15161                                  $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startx' => $this->x, 'starty' => $this->y));
15162                              }
15163                              $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
15164                              if ($rowspan > 1) {
15165                                  $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
15166                              }
15167                              // push background colors
15168                              if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
15169                                  $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
15170                              }
15171                              $prevLastH = $this->lasth;
15172                              // ****** write the cell content ******
15173                              $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true);
15174                              $this->lasth = $prevLastH;
15175                              $this->cMargin = $oldmargin;
15176                              $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
15177                              // update the end of row position
15178                              if ($rowspan <= 1) {
15179                                  if (isset($dom[$trid]['endy'])) {
15180                                      if ($this->page == $dom[$trid]['endpage']) {
15181                                          $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
15182                                      } elseif ($this->page > $dom[$trid]['endpage']) {
15183                                          $dom[$trid]['endy'] = $this->y;
15184                                      }
15185                                  } else {
15186                                      $dom[$trid]['endy'] = $this->y;
15187                                  }
15188                                  if (isset($dom[$trid]['endpage'])) {
15189                                      $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
15190                                  } else {
15191                                      $dom[$trid]['endpage'] = $this->page;
15192                                  }
15193                              } else {
15194                                  // account for row-spanned cells
15195                                  $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
15196                                  $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
15197                                  $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
15198                              }
15199                              if (isset($dom[$table_el]['rowspans'])) {
15200                                  // update endy and endpage on rowspanned cells
15201                                  foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
15202                                      if ($trwsp['rowspan'] > 0) {
15203                                          if (isset($dom[$trid]['endpage'])) {
15204                                              if ($trwsp['endpage'] == $dom[$trid]['endpage']) {
15205                                                  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
15206                                              } elseif ($trwsp['endpage'] < $dom[$trid]['endpage']) {
15207                                                  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
15208                                                  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
15209                                              } else {
15210                                                  $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
15211                                              }
15212                                          }
15213                                      }
15214                                  }
15215                              }
15216                              $this->x += ($cellspacingx / 2);
15217                          } else {
15218                              // opening tag (or self-closing tag)
15219                              if (!isset($opentagpos)) {
15220                                  if (!$this->InFooter) {
15221                                      if (isset($this->footerlen[$this->page])) {
15222                                          $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
15223                                      } else {
15224                                          $this->footerpos[$this->page] = $this->pagelen[$this->page];
15225                                      }
15226                                      $opentagpos = $this->footerpos[$this->page];
15227                                  }
15228                              }
15229                              $this->openHTMLTagHandler($dom, $key, $cell);
15230                          }
15231                      } else {
15232                          // closing tag
15233                          $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
15234                      }
15235                  } elseif (strlen($dom[$key]['value']) > 0) {
15236                      // print list-item
15237                      if (!$this->empty_string($this->lispacer)) {
15238                          $this->SetFont($pfontname, $pfontstyle, $pfontsize);
15239                          $this->lasth = $this->FontSize * $this->cell_height_ratio;
15240                          $minstartliney = $this->y;
15241                          $maxbottomliney = ($startliney + ($this->FontSize * $this->cell_height_ratio));
15242                          $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
15243                          $this->SetFont($curfontname, $curfontstyle, $curfontsize);
15244                          $this->lasth = $this->FontSize * $this->cell_height_ratio;
15245                          if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
15246                              $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
15247                              $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
15248                              $this->y += ((($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
15249                              $minstartliney = min($this->y, $minstartliney);
15250                              $maxbottomliney = max(($this->y + (($pfontsize * $this->cell_height_ratio) / $this->k)), $maxbottomliney);
15251                          }
15252                      }
15253                      // text
15254                      $this->htmlvspace = 0;
15255                      if ((!$this->premode) AND $this->isRTLTextDir()) {
15256                          // reverse spaces order
15257                          $len1 = strlen($dom[$key]['value']);
15258                          $lsp = $len1 - strlen(ltrim($dom[$key]['value']));
15259                          $rsp = $len1 - strlen(rtrim($dom[$key]['value']));
15260                          $tmpstr = '';
15261                          if ($rsp > 0) {
15262                              $tmpstr .= substr($dom[$key]['value'], -$rsp);
15263                          }
15264                          $tmpstr .= trim($dom[$key]['value']);
15265                          if ($lsp > 0) {
15266                              $tmpstr .= substr($dom[$key]['value'], 0, $lsp);
15267                          }
15268                          $dom[$key]['value'] = $tmpstr;
15269                      }
15270                      if ($newline) {
15271                          if (!$this->premode) {
15272                              $prelen = strlen($dom[$key]['value']);
15273                              if ($this->isRTLTextDir()) {
15274                                  $dom[$key]['value'] = rtrim($dom[$key]['value']).chr(0);
15275                              } else {
15276                                  $dom[$key]['value'] = ltrim($dom[$key]['value']);
15277                              }
15278                              $postlen = strlen($dom[$key]['value']);
15279                              if (($postlen == 0) AND ($prelen > 0)) {
15280                                  $dom[$key]['trimmed_space'] = true;
15281                              }
15282                          }
15283                          $newline = false;
15284                          $firstblock = true;
15285                      } else {
15286                          $firstblock = false;
15287                      }
15288                      $strrest = '';
15289                      if ($this->rtl) {
15290                          $this->x -= $this->textindent;
15291                      } else {
15292                          $this->x += $this->textindent;
15293                      }
15294                      if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
15295                          // HTML <a> Link
15296                          $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $this->HREF['color'], $this->HREF['style'], true);
15297                      } else {
15298                          // ****** write only until the end of the line and get the rest ******
15299                          $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0);
15300                      }
15301                      $this->textindent = 0;
15302  
15303                      if (strlen($strrest) > 0) {
15304                          // store the remaining string on the previous $key position
15305                          $this->newline = true;
15306                          if ($cell) {
15307                              if ($this->rtl) {
15308                                  $this->x -= $this->cMargin;
15309                              } else {
15310                                  $this->x += $this->cMargin;
15311                              }
15312                          }
15313                          if ($strrest == $dom[$key]['value']) {
15314                              // used to avoid infinite loop
15315                              ++$loop;
15316                          } else {
15317                              $loop = 0;
15318                          }
15319                          if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
15320                              $dom[$key]['value'] = trim($strrest);
15321                          } elseif ($this->premode) {
15322                              $dom[$key]['value'] = $strrest;
15323                          } elseif ($this->isRTLTextDir()) {
15324                              $dom[$key]['value'] = rtrim($strrest);
15325                          } else {
15326                              $dom[$key]['value'] = ltrim($strrest);
15327                          }
15328                          if ($loop < 3) {
15329                              --$key;
15330                          }
15331                      } else {
15332                          $loop = 0;
15333                      }
15334                  }
15335                  ++$key;
15336                  if (isset($dom[$key]['tag']) AND $dom[$key]['tag'] AND (!isset($dom[$key]['opening']) OR !$dom[$key]['opening']) AND isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
15337                      if ( (!$undo) AND (($this->start_transaction_page == ($this->numpages - 1)) OR ($this->y < $this->start_transaction_y))) {
15338                          // restore previous object
15339                          $this->rollbackTransaction(true);
15340                          // restore previous values
15341                          foreach ($this_method_vars as $vkey => $vval) {
15342                              $$vkey = $vval;
15343                          }
15344                          // add a page (or trig AcceptPageBreak() for multicolumn mode)
15345                          $pre_y = $this->y;
15346                          if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
15347                              $startliney = $this->y;
15348                          }
15349                          $undo = true; // avoid infinite loop
15350                      } else {
15351                          $undo = false;
15352                      }
15353                  }
15354              } // end for each $key
15355              // align the last line
15356              if (isset($startlinex)) {
15357                  $yshift = $minstartliney - $startliney;
15358                  if (($yshift > 0) OR ($this->page > $startlinepage)) {
15359                      $yshift = 0;
15360                  }
15361                  $t_x = 0;
15362                  // the last line must be shifted to be aligned as requested
15363                  $linew = abs($this->endlinex - $startlinex);
15364                  $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
15365                  if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
15366                      $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
15367                      $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
15368                  } elseif (isset($opentagpos)) {
15369                      $midpos = $opentagpos;
15370                  } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
15371                      $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
15372                      $midpos = $this->footerpos[$startlinepage];
15373                  } else {
15374                      $midpos = 0;
15375                  }
15376                  if ($midpos > 0) {
15377                      $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
15378                      $pend = substr($this->getPageBuffer($startlinepage), $midpos);
15379                  } else {
15380                      $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
15381                      $pend = '';
15382                  }
15383                  if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl))))) OR ($yshift < 0)) {
15384                      // calculate shifting amount
15385                      $tw = $w;
15386                      if ($this->lMargin != $prevlMargin) {
15387                          $tw += ($prevlMargin - $this->lMargin);
15388                      }
15389                      if ($this->rMargin != $prevrMargin) {
15390                          $tw += ($prevrMargin - $this->rMargin);
15391                      }
15392                      $one_space_width = $this->GetStringWidth(chr(32));
15393                      $mdiff = abs($tw - $linew);
15394                      if ($plalign == 'C') {
15395                          if ($this->rtl) {
15396                              $t_x = -($mdiff / 2);
15397                          } else {
15398                              $t_x = ($mdiff / 2);
15399                          }
15400                      } elseif (($plalign == 'R') AND (!$this->rtl)) {
15401                          // right alignment on LTR document
15402                          if (intval($this->revstrpos($pmid, ')]')) == (intval($this->revstrpos($pmid, ' )]')) + 1)) {
15403                              // remove last space (if any)
15404                              $linew -= $one_space_width;
15405                              $mdiff = abs($tw - $linew);
15406                          }
15407                          $t_x = $mdiff;
15408                      } elseif (($plalign == 'L') AND ($this->rtl)) {
15409                          // left alignment on RTL document
15410                          if (($this->revstrpos($pmid, '[(') > 0) AND ((intval($this->revstrpos($pmid, '[( ')) == intval($this->revstrpos($pmid, '[('))) OR (intval($this->revstrpos($pmid, '[('.chr(0).chr(32))) == intval($this->revstrpos($pmid, '[('))))) {
15411                              // remove first space (if any)
15412                              $linew -= $one_space_width;
15413                          }
15414                          if ((strpos($pmid, '[(') > 0) AND (intval(strpos($pmid, '[(')) == (intval($this->revstrpos($pmid, '[('))))) {
15415                              // remove last space (if any)
15416                              $linew -= $one_space_width;
15417                              if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
15418                                  $linew -= $one_space_width;
15419                              }
15420                          }
15421                          $mdiff = abs($tw - $linew);
15422                          $t_x = -$mdiff;
15423                      }
15424                  } // end if startlinex
15425                  if (($t_x != 0) OR ($yshift < 0)) {
15426                      // shift the line
15427                      $trx = sprintf('1 0 0 1 %.3F %.3F cm', ($t_x * $this->k), ($yshift * $this->k));
15428                      $this->setPageBuffer($startlinepage, $pstart."\nq\n".$trx."\n".$pmid."\nQ\n".$pend);
15429                      $endlinepos = strlen($pstart."\nq\n".$trx."\n".$pmid."\nQ\n");
15430                      // shift the annotations and links
15431                      if (isset($this->PageAnnots[$this->page])) {
15432                          foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
15433                              if ($pak >= $pask) {
15434                                  $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
15435                                  $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
15436                              }
15437                          }
15438                      }
15439                      $this->y -= $yshift;
15440                  }
15441              }
15442              if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
15443                  $this->Ln($this->lasth);
15444                  if ($this->y < $maxbottomliney) {
15445                      $this->y = $maxbottomliney;
15446                  }
15447              }
15448              // restore previous values
15449              $this->setGraphicVars($gvars);
15450              if ($this->page > $prevPage) {
15451                  $this->lMargin = $this->pagedim[$this->page]['olm'];
15452                  $this->rMargin = $this->pagedim[$this->page]['orm'];
15453              }
15454              // restore previous list state
15455              $this->cell_height_ratio = $prev_cell_height_ratio;
15456              $this->listnum = $prev_listnum;
15457              $this->listordered = $prev_listordered;
15458              $this->listcount = $prev_listcount;
15459              $this->lispacer = $prev_lispacer;
15460              unset($dom);
15461          }
15462  
15463          /**
15464           * Process opening tags.
15465           * @param array $dom html dom array
15466           * @param int $key current element id
15467           * @param boolean $cell if true add the default cMargin space to each new line (default false).
15468           * @access protected
15469           */
15470  		protected function openHTMLTagHandler(&$dom, $key, $cell) {
15471              $tag = $dom[$key];
15472              $parent = $dom[($dom[$key]['parent'])];
15473              $firstorlast = ($key == 1);
15474              // check for text direction attribute
15475              if (isset($tag['attribute']['dir'])) {
15476                  $this->setTempRTL($tag['attribute']['dir']);
15477              } else {
15478                  $this->tmprtl = false;
15479              }
15480              if ($tag['block']) {
15481                  $hbz = 0; // distance from y to line bottom
15482                  $hb = 0; // vertical space between block tags
15483                  // calculate vertical space for block tags
15484                  if (isset($this->tagvspaces[$tag['value']][0]['h']) AND ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
15485                      $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
15486                  } elseif (isset($tag['fontsize'])) {
15487                      $cur_h = ($tag['fontsize'] / $this->k) * $this->cell_height_ratio;
15488                  } else {
15489                      $cur_h = $this->FontSize * $this->cell_height_ratio;
15490                  }
15491                  if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
15492                      $n = $this->tagvspaces[$tag['value']][0]['n'];
15493                  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
15494                      $n = 0.6;
15495                  } else {
15496                      $n = 1;
15497                  }
15498                  $hb = ($n * $cur_h);
15499                  if (($this->htmlvspace <= 0) AND ($n > 0)) {
15500                      if (isset($parent['fontsize'])) {
15501                          $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
15502                      } else {
15503                          $hbz = $this->FontSize * $this->cell_height_ratio;
15504                      }
15505                  }
15506              }
15507              //Opening tag
15508              switch($tag['value']) {
15509                  case 'table': {
15510                      $cp = 0;
15511                      $cs = 0;
15512                      $dom[$key]['rowspans'] = array();
15513                      if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
15514                          // set table header
15515                          if (!$this->empty_string($dom[$key]['thead'])) {
15516                              // set table header
15517                              $this->thead = $dom[$key]['thead'];
15518                              if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
15519                                  $this->theadMargins = array();
15520                                  $this->theadMargins['cmargin'] = $this->cMargin;
15521                              }
15522                          }
15523                      }
15524                      if (isset($tag['attribute']['cellpadding'])) {
15525                          $cp = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
15526                          $this->oldcMargin = $this->cMargin;
15527                          $this->cMargin = $cp;
15528                      }
15529                      if (isset($tag['attribute']['cellspacing'])) {
15530                          $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
15531                      }
15532                      if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false)) {
15533                          $this->inthead = true;
15534                          // add a page (or trig AcceptPageBreak() for multicolumn mode)
15535                          $this->checkPageBreak($this->PageBreakTrigger + 1);
15536                      }
15537                      break;
15538                  }
15539                  case 'tr': {
15540                      // array of columns positions
15541                      $dom[$key]['cellpos'] = array();
15542                      break;
15543                  }
15544                  case 'hr': {
15545                      $wtmp = $this->w - $this->lMargin - $this->rMargin;
15546                      if ((isset($tag['attribute']['width'])) AND ($tag['attribute']['width'] != '')) {
15547                          $hrWidth = $this->getHTMLUnitToUnits($tag['attribute']['width'], $wtmp, 'px');
15548                      } else {
15549                          $hrWidth = $wtmp;
15550                      }
15551                      if ((isset($tag['height'])) AND ($tag['height'] != '')) {
15552                          $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
15553                      } else {
15554                          $hrHeight = $this->GetLineWidth();
15555                      }
15556                      $this->addHTMLVertSpace($hbz, ($hrHeight / 2), $cell, $firstorlast);
15557                      $x = $this->GetX();
15558                      $y = $this->GetY();
15559                      $prevlinewidth = $this->GetLineWidth();
15560                      $this->SetLineWidth($hrHeight);
15561                      $this->Line($x, $y, $x + $hrWidth, $y);
15562                      $this->SetLineWidth($prevlinewidth);
15563                      $this->addHTMLVertSpace(($hrHeight / 2), 0, $cell, !isset($dom[($key + 1)]));
15564                      break;
15565                  }
15566                  case 'a': {
15567                      if (array_key_exists('href', $tag['attribute'])) {
15568                          $this->HREF['url'] = $tag['attribute']['href'];
15569                      }
15570                      $this->HREF['color'] = $this->htmlLinkColorArray;
15571                      $this->HREF['style'] = $this->htmlLinkFontStyle;
15572                      if (array_key_exists('style', $tag['attribute'])) {
15573                          // get style attributes
15574                          preg_match_all('/([^;:\s]*):([^;]*)/', $tag['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
15575                          $astyle = array();
15576                          while (list($id, $name) = each($style_array[1])) {
15577                              $name = strtolower($name);
15578                              $astyle[$name] = trim($style_array[2][$id]);
15579                          }
15580                          if (isset($astyle['color'])) {
15581                              $this->HREF['color'] = $this->convertHTMLColorToDec($astyle['color']);
15582                          }
15583                          if (isset($astyle['text-decoration'])) {
15584                              $this->HREF['style'] = '';
15585                              $decors = explode(' ', strtolower($astyle['text-decoration']));
15586                              foreach ($decors as $dec) {
15587                                  $dec = trim($dec);
15588                                  if (!$this->empty_string($dec)) {
15589                                      if ($dec{0} == 'u') {
15590                                          // underline
15591                                          $this->HREF['style'] .= 'U';
15592                                      } elseif ($dec{0} == 'l') {
15593                                          // line-trough
15594                                          $this->HREF['style'] .= 'D';
15595                                      } elseif ($dec{0} == 'o') {
15596                                          // overline
15597                                          $this->HREF['style'] .= 'O';
15598                                      }
15599                                  }
15600                              }
15601                          }
15602                      }
15603                      break;
15604                  }
15605                  case 'img': {
15606                      if (isset($tag['attribute']['src'])) {
15607                          // replace relative path with real server path
15608                          if (($tag['attribute']['src'][0] == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
15609                              $findroot = strpos($tag['attribute']['src'], $_SERVER['DOCUMENT_ROOT']);
15610                              if (($findroot === false) OR ($findroot > 1)) {
15611                                  $tag['attribute']['src'] = $_SERVER['DOCUMENT_ROOT'].$tag['attribute']['src'];
15612                              }
15613                          }
15614                          $tag['attribute']['src'] = urldecode($tag['attribute']['src']);
15615                          $type = $this->getImageFileType($tag['attribute']['src']);
15616                          $testscrtype = @parse_url($tag['attribute']['src']);
15617                          if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
15618                              // convert URL to server path
15619                              $tag['attribute']['src'] = str_replace(K_PATH_URL, K_PATH_MAIN, $tag['attribute']['src']);
15620                          }
15621                          if (!isset($tag['attribute']['width'])) {
15622                              $tag['attribute']['width'] = 0;
15623                          }
15624                          if (!isset($tag['attribute']['height'])) {
15625                              $tag['attribute']['height'] = 0;
15626                          }
15627                          //if (!isset($tag['attribute']['align'])) {
15628                              // the only alignment supported is "bottom"
15629                              // further development is required for other modes.
15630                              $tag['attribute']['align'] = 'bottom';
15631                          //}
15632                          switch($tag['attribute']['align']) {
15633                              case 'top': {
15634                                  $align = 'T';
15635                                  break;
15636                              }
15637                              case 'middle': {
15638                                  $align = 'M';
15639                                  break;
15640                              }
15641                              case 'bottom': {
15642                                  $align = 'B';
15643                                  break;
15644                              }
15645                              default: {
15646                                  $align = 'B';
15647                                  break;
15648                              }
15649                          }
15650                          $prevy = $this->y;
15651                          $xpos = $this->GetX();
15652                          // eliminate marker spaces
15653                          if (isset($dom[($key - 1)])) {
15654                              if (($dom[($key - 1)]['value'] == ' ') OR (isset($dom[($key - 1)]['trimmed_space']))) {
15655                                  $xpos -= $this->GetStringWidth(chr(32));
15656                              } elseif ($this->rtl AND $dom[($key - 1)]['value'] == '  ') {
15657                                  $xpos -= (2 * $this->GetStringWidth(chr(32)));
15658                              }
15659                          }
15660                          $imglink = '';
15661                          if (isset($this->HREF['url']) AND !$this->empty_string($this->HREF['url'])) {
15662                              $imglink = $this->HREF['url'];
15663                              if ($imglink{0} == '#') {
15664                                  // convert url to internal link
15665                                  $page = intval(substr($imglink, 1));
15666                                  $imglink = $this->AddLink();
15667                                  $this->SetLink($imglink, 0, $page);
15668                              }
15669                          }
15670                          $border = 0;
15671                          if (isset($tag['attribute']['border']) AND !empty($tag['attribute']['border'])) {
15672                              // currently only support 1 (frame) or a combination of 'LTRB'
15673                              $border = $tag['attribute']['border'];
15674                          }
15675                          $iw = '';
15676                          if (isset($tag['attribute']['width'])) {
15677                              $iw = $this->getHTMLUnitToUnits($tag['attribute']['width'], 1, 'px', false);
15678                          }
15679                          $ih = '';
15680                          if (isset($tag['attribute']['height'])) {
15681                              $ih = $this->getHTMLUnitToUnits($tag['attribute']['height'], 1, 'px', false);
15682                          }
15683                          if (($type == 'eps') OR ($type == 'ai')) {
15684                              $this->ImageEps($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
15685                          } elseif ($type == 'svg') {
15686                              $this->ImageSVG($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
15687                          } else  {
15688                              $this->Image($tag['attribute']['src'], $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
15689                          }
15690                          switch($align) {
15691                              case 'T': {
15692                                  $this->y = $prevy;
15693                                  break;
15694                              }
15695                              case 'M': {
15696                                  $this->y = (($this->img_rb_y + $prevy - ($tag['fontsize'] / $this->k)) / 2) ;
15697                                  break;
15698                              }
15699                              case 'B': {
15700                                  $this->y = $this->img_rb_y - ($tag['fontsize'] / $this->k);
15701                                  break;
15702                              }
15703                          }
15704                      }
15705                      break;
15706                  }
15707                  case 'dl': {
15708                      ++$this->listnum;
15709                      if ($this->listnum == 1) {
15710                          $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
15711                      } else {
15712                          $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
15713                      }
15714                      break;
15715                  }
15716                  case 'dt': {
15717                      $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast);
15718                      break;
15719                  }
15720                  case 'dd': {
15721                      if ($this->rtl) {
15722                          $this->rMargin += $this->listindent;
15723                      } else {
15724                          $this->lMargin += $this->listindent;
15725                      }
15726                      ++$this->listindentlevel;
15727                      $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast);
15728                      break;
15729                  }
15730                  case 'ul':
15731                  case 'ol': {
15732                      ++$this->listnum;
15733                      if ($tag['value'] == 'ol') {
15734                          $this->listordered[$this->listnum] = true;
15735                      } else {
15736                          $this->listordered[$this->listnum] = false;
15737                      }
15738                      if (isset($tag['attribute']['start'])) {
15739                          $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
15740                      } else {
15741                          $this->listcount[$this->listnum] = 0;
15742                      }
15743                      if ($this->rtl) {
15744                          $this->rMargin += $this->listindent;
15745                      } else {
15746                          $this->lMargin += $this->listindent;
15747                      }
15748                      ++$this->listindentlevel;
15749                      if ($this->listnum == 1) {
15750                          $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
15751                      } else {
15752                          $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
15753                      }
15754                      break;
15755                  }
15756                  case 'li': {
15757                      $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast);
15758                      if ($this->listordered[$this->listnum]) {
15759                          // ordered item
15760                          if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
15761                              $this->lispacer = $parent['attribute']['type'];
15762                          } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
15763                              $this->lispacer = $parent['listtype'];
15764                          } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
15765                              $this->lispacer = $this->lisymbol;
15766                          } else {
15767                              $this->lispacer = '#';
15768                          }
15769                          ++$this->listcount[$this->listnum];
15770                          if (isset($tag['attribute']['value'])) {
15771                              $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
15772                          }
15773                      } else {
15774                          // unordered item
15775                          if (isset($parent['attribute']['type']) AND !$this->empty_string($parent['attribute']['type'])) {
15776                              $this->lispacer = $parent['attribute']['type'];
15777                          } elseif (isset($parent['listtype']) AND !$this->empty_string($parent['listtype'])) {
15778                              $this->lispacer = $parent['listtype'];
15779                          } elseif (isset($this->lisymbol) AND !$this->empty_string($this->lisymbol)) {
15780                              $this->lispacer = $this->lisymbol;
15781                          } else {
15782                              $this->lispacer = '!';
15783                          }
15784                      }
15785                      break;
15786                  }
15787                  case 'blockquote': {
15788                      if ($this->rtl) {
15789                          $this->rMargin += $this->listindent;
15790                      } else {
15791                          $this->lMargin += $this->listindent;
15792                      }
15793                      ++$this->listindentlevel;
15794                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
15795                      break;
15796                  }
15797                  case 'br': {
15798                      $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast);
15799                      break;
15800                  }
15801                  case 'div': {
15802                      $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast);
15803                      break;
15804                  }
15805                  case 'p': {
15806                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
15807                      break;
15808                  }
15809                  case 'pre': {
15810                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
15811                      $this->premode = true;
15812                      break;
15813                  }
15814                  case 'sup': {
15815                      $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
15816                      break;
15817                  }
15818                  case 'sub': {
15819                      $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
15820                      break;
15821                  }
15822                  case 'h1':
15823                  case 'h2':
15824                  case 'h3':
15825                  case 'h4':
15826                  case 'h5':
15827                  case 'h6': {
15828                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
15829                      break;
15830                  }
15831                  // Form fields (since 4.8.000 - 2009-09-07)
15832                  case 'form': {
15833                      if (isset($tag['attribute']['action'])) {
15834                          $this->form_action = $tag['attribute']['action'];
15835                      } else {
15836                          $this->form_action = K_PATH_URL.$_SERVER['SCRIPT_NAME'];
15837                      }
15838                      if (isset($tag['attribute']['enctype'])) {
15839                          $this->form_enctype = $tag['attribute']['enctype'];
15840                      } else {
15841                          $this->form_enctype = 'application/x-www-form-urlencoded';
15842                      }
15843                      if (isset($tag['attribute']['method'])) {
15844                          $this->form_mode = $tag['attribute']['method'];
15845                      } else {
15846                          $this->form_mode = 'post';
15847                      }
15848                      break;
15849                  }
15850                  case 'input': {
15851                      if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
15852                          $name = $tag['attribute']['name'];
15853                      } else {
15854                          break;
15855                      }
15856                      $prop = array();
15857                      $opt = array();
15858                      if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
15859                          $value = $tag['attribute']['value'];
15860                      }
15861                      if (isset($tag['attribute']['maxlength']) AND !$this->empty_string($tag['attribute']['maxlength'])) {
15862                          $opt['maxlen'] = intval($tag['attribute']['value']);
15863                      }
15864                      $h = $this->FontSize * $this->cell_height_ratio;
15865                      if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
15866                          $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
15867                      } else {
15868                          $w = $h;
15869                      }
15870                      if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
15871                          $checked = true;
15872                      } else {
15873                          $checked = false;
15874                      }
15875                      switch ($tag['attribute']['type']) {
15876                          case 'text': {
15877                              if (isset($value)) {
15878                                  $opt['v'] = $value;
15879                              }
15880                              $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
15881                              break;
15882                          }
15883                          case 'password': {
15884                              if (isset($value)) {
15885                                  $opt['v'] = $value;
15886                              }
15887                              $prop['password'] = 'true';
15888                              $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
15889                              break;
15890                          }
15891                          case 'checkbox': {
15892                              $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
15893                              break;
15894                          }
15895                          case 'radio': {
15896                              $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
15897                              break;
15898                          }
15899                          case 'submit': {
15900                              $w = $this->GetStringWidth($value) * 1.5;
15901                              $h *= 1.6;
15902                              $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
15903                              $action = array();
15904                              $action['S'] = 'SubmitForm';
15905                              $action['F'] = $this->form_action;
15906                              if ($this->form_enctype != 'FDF') {
15907                                  $action['Flags'] = array('ExportFormat');
15908                              }
15909                              if ($this->form_mode == 'get') {
15910                                  $action['Flags'] = array('GetMethod');
15911                              }
15912                              $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
15913                              break;
15914                          }
15915                          case 'reset': {
15916                              $w = $this->GetStringWidth($value) * 1.5;
15917                              $h *= 1.6;
15918                              $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
15919                              $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
15920                              break;
15921                          }
15922                          case 'file': {
15923                              $prop['fileSelect'] = 'true';
15924                              $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
15925                              if (!isset($value)) {
15926                                  $value = '*';
15927                              }
15928                              $w = $this->GetStringWidth($value) * 2;
15929                              $h *= 1.2;
15930                              $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
15931                              $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
15932                              $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
15933                              break;
15934                          }
15935                          case 'hidden': {
15936                              if (isset($value)) {
15937                                  $opt['v'] = $value;
15938                              }
15939                              $opt['f'] = array('invisible', 'hidden');
15940                              $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
15941                              break;
15942                          }
15943                          case 'image': {
15944                              // THIS TYPE MUST BE FIXED
15945                              if (isset($tag['attribute']['src']) AND !$this->empty_string($tag['attribute']['src'])) {
15946                                  $img = $tag['attribute']['src'];
15947                              } else {
15948                                  break;
15949                              }
15950                              $value = 'img';
15951                              //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
15952                              if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
15953                                  $jsaction = $tag['attribute']['onclick'];
15954                              } else {
15955                                  $jsaction = '';
15956                              }
15957                              $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
15958                              break;
15959                          }
15960                          case 'button': {
15961                              $w = $this->GetStringWidth($value) * 1.5;
15962                              $h *= 1.6;
15963                              $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
15964                              if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
15965                                  $jsaction = $tag['attribute']['onclick'];
15966                              } else {
15967                                  $jsaction = '';
15968                              }
15969                              $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
15970                              break;
15971                          }
15972                      }
15973                      break;
15974                  }
15975                  case 'textarea': {
15976                      $prop = array();
15977                      $opt = array();
15978                      if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
15979                          $name = $tag['attribute']['name'];
15980                      } else {
15981                          break;
15982                      }
15983                      if (isset($tag['attribute']['value']) AND !$this->empty_string($tag['attribute']['value'])) {
15984                          $opt['v'] = $tag['attribute']['value'];
15985                      }
15986                      if (isset($tag['attribute']['cols']) AND !$this->empty_string($tag['attribute']['cols'])) {
15987                          $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
15988                      } else {
15989                          $w = 40;
15990                      }
15991                      if (isset($tag['attribute']['rows']) AND !$this->empty_string($tag['attribute']['rows'])) {
15992                          $h = intval($tag['attribute']['rows']) * $this->FontSize * $this->cell_height_ratio;
15993                      } else {
15994                          $h = 10;
15995                      }
15996                      $prop['multiline'] = 'true';
15997                      $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
15998                      break;
15999                  }
16000                  case 'select': {
16001                      $h = $this->FontSize * $this->cell_height_ratio;
16002                      if (isset($tag['attribute']['size']) AND !$this->empty_string($tag['attribute']['size'])) {
16003                          $h *= ($tag['attribute']['size'] + 1);
16004                      }
16005                      $prop = array();
16006                      $opt = array();
16007                      if (isset($tag['attribute']['name']) AND !$this->empty_string($tag['attribute']['name'])) {
16008                          $name = $tag['attribute']['name'];
16009                      } else {
16010                          break;
16011                      }
16012                      $w = 0;
16013                      if (isset($tag['attribute']['opt']) AND !$this->empty_string($tag['attribute']['opt'])) {
16014                          $options = explode ("\r", $tag['attribute']['opt']);
16015                          $values = array();
16016                          foreach ($options as $val) {
16017                              if (strpos($val, "\t") !== false) {
16018                                  $opts = explode("\t", $val);
16019                                  $values[] = $opts;
16020                                  $w = max($w, $this->GetStringWidth($opts[1]));
16021                              } else {
16022                                  $values[] = $val;
16023                                  $w = max($w, $this->GetStringWidth($val));
16024                              }
16025                          }
16026                      } else {
16027                          break;
16028                      }
16029                      $w *= 2;
16030                      if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
16031                          $prop['multipleSelection'] = 'true';
16032                          $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
16033                      } else {
16034                          $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
16035                      }
16036                      break;
16037                  }
16038                  case 'tcpdf': {
16039                      if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
16040                          // Special tag used to call TCPDF methods
16041                          if (isset($tag['attribute']['method'])) {
16042                              $tcpdf_method = $tag['attribute']['method'];
16043                              if (method_exists($this, $tcpdf_method)) {
16044                                  if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
16045                                      $params = unserialize(urldecode($tag['attribute']['params']));
16046                                      call_user_func_array(array($this, $tcpdf_method), $params);
16047                                  } else {
16048                                      $this->$tcpdf_method();
16049                                  }
16050                                  $this->newline = true;
16051                              }
16052                          }
16053                      }
16054                      break;
16055                  }
16056                  default: {
16057                      break;
16058                  }
16059              }
16060              if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
16061                  $pba = $dom[$key]['attribute']['pagebreakafter'];
16062                  // check for pagebreak
16063                  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
16064                      // add a page (or trig AcceptPageBreak() for multicolumn mode)
16065                      $this->checkPageBreak($this->PageBreakTrigger + 1);
16066                  }
16067                  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
16068                      OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
16069                      // add a page (or trig AcceptPageBreak() for multicolumn mode)
16070                      $this->checkPageBreak($this->PageBreakTrigger + 1);
16071                  }
16072              }
16073          }
16074  
16075          /**
16076           * Process closing tags.
16077           * @param array $dom html dom array
16078           * @param int $key current element id
16079           * @param boolean $cell if true add the default cMargin space to each new line (default false).
16080           * @param int $maxbottomliney maximum y value of current line
16081           * @access protected
16082           */
16083  		protected function closeHTMLTagHandler(&$dom, $key, $cell, $maxbottomliney=0) {
16084              $tag = $dom[$key];
16085              $parent = $dom[($dom[$key]['parent'])];
16086              $firstorlast = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
16087              $in_table_head = false;
16088              if ($tag['block']) {
16089                  $hbz = 0; // distance from y to line bottom
16090                  $hb = 0; // vertical space between block tags
16091                  // calculate vertical space for block tags
16092                  if (isset($this->tagvspaces[$tag['value']][1]['h']) AND ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
16093                      $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
16094                  } elseif (isset($parent['fontsize'])) {
16095                      $pre_h = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
16096                  } else {
16097                      $pre_h = $this->FontSize * $this->cell_height_ratio;
16098                  }
16099                  if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
16100                      $n = $this->tagvspaces[$tag['value']][1]['n'];
16101                  } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
16102                      $n = 0.6;
16103                  } else {
16104                      $n = 1;
16105                  }
16106                  $hb = ($n * $pre_h);
16107                  if ($this->y < $maxbottomliney) {
16108                      $hbz = ($maxbottomliney - $this->y);
16109                  }
16110              }
16111              //Closing tag
16112              switch($tag['value']) {
16113                  case 'tr': {
16114                      $table_el = $dom[($dom[$key]['parent'])]['parent'];
16115                      if (!isset($parent['endy'])) {
16116                          $dom[($dom[$key]['parent'])]['endy'] = $this->y;
16117                          $parent['endy'] = $this->y;
16118                      }
16119                      if (!isset($parent['endpage'])) {
16120                          $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
16121                          $parent['endpage'] = $this->page;
16122                      }
16123                      // update row-spanned cells
16124                      if (isset($dom[$table_el]['rowspans'])) {
16125                          foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
16126                              $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
16127                              if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
16128                                  if ($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) {
16129                                      $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
16130                                  } elseif ($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) {
16131                                      $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
16132                                      $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
16133                                  }
16134                              }
16135                          }
16136                          // report new endy and endpage to the rowspanned cells
16137                          foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
16138                              if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
16139                                  $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
16140                                  $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
16141                                  $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
16142                                  $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
16143                              }
16144                          }
16145                          // update remaining rowspanned cells
16146                          foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
16147                              if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
16148                                  $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
16149                                  $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
16150                              }
16151                          }
16152                      }
16153                      if (($this->num_columns > 1) AND ($dom[($dom[$key]['parent'])]['endy'] >= ($this->PageBreakTrigger - $this->lasth)) AND ($this->y < $dom[($dom[$key]['parent'])]['endy'])) {
16154                          $this->Ln(0, $cell);
16155                      } else {
16156                          $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
16157                          $this->y = $dom[($dom[$key]['parent'])]['endy'];
16158                          if (isset($dom[$table_el]['attribute']['cellspacing'])) {
16159                              $cellspacing = $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
16160                              $this->y += $cellspacing;
16161                          }
16162                          $this->Ln(0, $cell);
16163                          $this->x = $parent['startx'];
16164                          // account for booklet mode
16165                          if ($this->page > $parent['startpage']) {
16166                              if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
16167                                  $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
16168                              } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
16169                                  $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
16170                              }
16171                          }
16172                      }
16173                      break;
16174                  }
16175                  case 'tablehead':
16176                      // closing tag used for the thead part
16177                      $in_table_head = true;
16178                      $this->inthead = false;
16179                  case 'table': {
16180                      $table_el = $parent;
16181                      // draw borders
16182                      if ((isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0))
16183                          OR (isset($table_el['style']['border']) AND ($table_el['style']['border'] > 0))) {
16184                              $border = 1;
16185                      } else {
16186                          $border = 0;
16187                      }
16188                      // fix bottom line alignment of last line before page break
16189                      foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
16190                          // update row-spanned cells
16191                          if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
16192                              foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
16193                                  if ($trwsp['trid'] == $trkey) {
16194                                      $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
16195                                  }
16196                                  if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0)) {
16197                                      $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
16198                                  }
16199                              }
16200                          }
16201                          if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
16202                              $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
16203                              $dom[$prevtrkey]['endy'] = $pgendy;
16204                              // update row-spanned cells
16205                              if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
16206                                  foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
16207                                      if (($trwsp['trid'] == $trkey) AND ($trwsp['mrowspan'] > 1) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
16208                                          $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
16209                                          $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
16210                                      }
16211                                  }
16212                              }
16213                          }
16214                          $prevtrkey = $trkey;
16215                          $table_el = $dom[($dom[$key]['parent'])];
16216                      }
16217                      // for each row
16218                      foreach ($table_el['trids'] as $j => $trkey) {
16219                          $parent = $dom[$trkey];
16220                          // for each cell on the row
16221                          foreach ($parent['cellpos'] as $k => $cellpos) {
16222                              if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
16223                                  $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
16224                                  $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
16225                                  $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
16226                                  $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
16227                                  $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
16228                              } else {
16229                                  $endy = $parent['endy'];
16230                                  $startpage = $parent['startpage'];
16231                                  $endpage = $parent['endpage'];
16232                              }
16233                              if ($endpage > $startpage) {
16234                                  // design borders around HTML cells.
16235                                  for ($page=$startpage; $page <= $endpage; ++$page) {
16236                                      $this->setPage($page);
16237                                      if ($page == $startpage) {
16238                                          $this->y = $parent['starty']; // put cursor at the beginning of row on the first page
16239                                          $ch = $this->getPageHeight() - $parent['starty'] - $this->getBreakMargin();
16240                                          $cborder = $this->getBorderMode($border, $position='start');
16241                                      } elseif ($page == $endpage) {
16242                                          $this->y = $this->tMargin; // put cursor at the beginning of last page
16243                                          $ch = $endy - $this->tMargin;
16244                                          $cborder = $this->getBorderMode($border, $position='end');
16245                                      } else {
16246                                          $this->y = $this->tMargin; // put cursor at the beginning of the current page
16247                                          $ch = $this->getPageHeight() - $this->tMargin - $this->getBreakMargin();
16248                                          $cborder = $this->getBorderMode($border, $position='middle');
16249                                      }
16250                                      if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
16251                                          $this->SetFillColorArray($cellpos['bgcolor']);
16252                                          $fill = true;
16253                                      } else {
16254                                          $fill = false;
16255                                      }
16256                                      $cw = abs($cellpos['endx'] - $cellpos['startx']);
16257                                      $this->x = $cellpos['startx'];
16258                                      // account for margin changes
16259                                      if ($page > $startpage) {
16260                                          if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
16261                                              $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
16262                                          } elseif ((!$this->rtl) AND ($this->pagedim[$page]['lm'] != $this->pagedim[$startpage]['olm'])) {
16263                                              $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
16264                                          }
16265                                      }
16266                                      // design a cell around the text
16267                                      $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $cborder, 1, '', $fill, '', 0, true);
16268                                      if ($cborder OR $fill) {
16269                                          $pagebuff = $this->getPageBuffer($this->page);
16270                                          $pstart = substr($pagebuff, 0, $this->intmrk[$this->page]);
16271                                          $pend = substr($pagebuff, $this->intmrk[$this->page]);
16272                                          $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
16273                                          $this->intmrk[$this->page] += strlen($ccode."\n");
16274                                      }
16275                                  }
16276                              } else {
16277                                  $this->setPage($startpage);
16278                                  if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
16279                                      $this->SetFillColorArray($cellpos['bgcolor']);
16280                                      $fill = true;
16281                                  } else {
16282                                      $fill = false;
16283                                  }
16284                                  $this->x = $cellpos['startx'];
16285                                  $this->y = $parent['starty'];
16286                                  $cw = abs($cellpos['endx'] - $cellpos['startx']);
16287                                  $ch = $endy - $parent['starty'];
16288                                  // design a cell around the text
16289                                  $ccode = $this->FillColor."\n".$this->getCellCode($cw, $ch, '', $border, 1, '', $fill, '', 0, true);
16290                                  if ($border OR $fill) {
16291                                      if (end($this->transfmrk[$this->page]) !== false) {
16292                                          $pagemarkkey = key($this->transfmrk[$this->page]);
16293                                          $pagemark = &$this->transfmrk[$this->page][$pagemarkkey];
16294                                      } elseif ($this->InFooter) {
16295                                          $pagemark = &$this->footerpos[$this->page];
16296                                      } else {
16297                                          $pagemark = &$this->intmrk[$this->page];
16298                                      }
16299                                      $pagebuff = $this->getPageBuffer($this->page);
16300                                      $pstart = substr($pagebuff, 0, $pagemark);
16301                                      $pend = substr($pagebuff, $pagemark);
16302                                      $this->setPageBuffer($this->page, $pstart.$ccode."\n".$pend);
16303                                      $pagemark += strlen($ccode."\n");
16304                                  }
16305                              }
16306                          }
16307                          if (isset($table_el['attribute']['cellspacing'])) {
16308                              $cellspacing = $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
16309                              $this->y += $cellspacing;
16310                          }
16311                          $this->Ln(0, $cell);
16312                          $this->x = $parent['startx'];
16313                          if ($endpage > $startpage) {
16314                              if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
16315                                  $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
16316                              } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
16317                                  $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
16318                              }
16319                          }
16320                      }
16321                      if (!$in_table_head) {
16322                          // we are not inside a thead section
16323                          if (isset($parent['cellpadding'])) {
16324                              $this->cMargin = $this->oldcMargin;
16325                          }
16326                          $this->lasth = $this->FontSize * $this->cell_height_ratio;
16327                          if (isset($this->theadMargins['top'])) {
16328                              if (($this->theadMargins['top'] == $this->tMargin) AND ($this->page == ($this->numpages - 1))) {
16329                                  // remove last page containing only THEAD
16330                                  $this->deletePage($this->numpages);
16331                              }
16332                              // restore top margin
16333                              $this->tMargin = $this->theadMargins['top'];
16334                              $this->pagedim[$this->page]['tm'] = $this->tMargin;
16335                          }
16336                          if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
16337                              // reset main table header
16338                              $this->thead = '';
16339                              $this->theadMargins = array();
16340                          }
16341                      }
16342                      break;
16343                  }
16344                  case 'a': {
16345                      $this->HREF = '';
16346                      break;
16347                  }
16348                  case 'sup': {
16349                      $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
16350                      break;
16351                  }
16352                  case 'sub': {
16353                      $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize'])/$this->k));
16354                      break;
16355                  }
16356                  case 'div': {
16357                      $this->addHTMLVertSpace($hbz, 0, $cell, $firstorlast);
16358                      break;
16359                  }
16360                  case 'blockquote': {
16361                      if ($this->rtl) {
16362                          $this->rMargin -= $this->listindent;
16363                      } else {
16364                          $this->lMargin -= $this->listindent;
16365                      }
16366                      --$this->listindentlevel;
16367                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
16368                      break;
16369                  }
16370                  case 'p': {
16371                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
16372                      break;
16373                  }
16374                  case 'pre': {
16375                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
16376                      $this->premode = false;
16377                      break;
16378                  }
16379                  case 'dl': {
16380                      --$this->listnum;
16381                      if ($this->listnum <= 0) {
16382                          $this->listnum = 0;
16383                          $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
16384                      } else {
16385                          $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
16386                      }
16387                      $this->lasth = $this->FontSize * $this->cell_height_ratio;
16388                      break;
16389                  }
16390                  case 'dt': {
16391                      $this->lispacer = '';
16392                      $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
16393                      break;
16394                  }
16395                  case 'dd': {
16396                      $this->lispacer = '';
16397                      if ($this->rtl) {
16398                          $this->rMargin -= $this->listindent;
16399                      } else {
16400                          $this->lMargin -= $this->listindent;
16401                      }
16402                      --$this->listindentlevel;
16403                      $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
16404                      break;
16405                  }
16406                  case 'ul':
16407                  case 'ol': {
16408                      --$this->listnum;
16409                      $this->lispacer = '';
16410                      if ($this->rtl) {
16411                          $this->rMargin -= $this->listindent;
16412                      } else {
16413                          $this->lMargin -= $this->listindent;
16414                      }
16415                      --$this->listindentlevel;
16416                      if ($this->listnum <= 0) {
16417                          $this->listnum = 0;
16418                          $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
16419                      } else {
16420                          $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
16421                      }
16422                      $this->lasth = $this->FontSize * $this->cell_height_ratio;
16423                      break;
16424                  }
16425                  case 'li': {
16426                      $this->lispacer = '';
16427                      $this->addHTMLVertSpace(0, 0, $cell, $firstorlast);
16428                      break;
16429                  }
16430                  case 'h1':
16431                  case 'h2':
16432                  case 'h3':
16433                  case 'h4':
16434                  case 'h5':
16435                  case 'h6': {
16436                      $this->addHTMLVertSpace($hbz, $hb, $cell, $firstorlast);
16437                      break;
16438                  }
16439                  // Form fields (since 4.8.000 - 2009-09-07)
16440                  case 'form': {
16441                      $this->form_action = '';
16442                      $this->form_enctype = 'application/x-www-form-urlencoded';
16443                      break;
16444                  }
16445                  default : {
16446                      break;
16447                  }
16448              }
16449              if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
16450                  $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
16451                  // check for pagebreak
16452                  if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
16453                      // add a page (or trig AcceptPageBreak() for multicolumn mode)
16454                      $this->checkPageBreak($this->PageBreakTrigger + 1);
16455                  }
16456                  if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
16457                      OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
16458                      // add a page (or trig AcceptPageBreak() for multicolumn mode)
16459                      $this->checkPageBreak($this->PageBreakTrigger + 1);
16460                  }
16461              }
16462              $this->tmprtl = false;
16463          }
16464  
16465          /**
16466           * Add vertical spaces if needed.
16467           * @param string $hbz Distance between current y and line bottom.
16468           * @param string $hb The height of the break.
16469           * @param boolean $cell if true add the default cMargin space to each new line (default false).
16470           * @param boolean $firstorlast if true do not print additional empty lines.
16471           * @access protected
16472           */
16473  		protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firstorlast=false) {
16474              if ($firstorlast) {
16475                  $this->Ln(0, $cell);
16476                  $this->htmlvspace = 0;
16477                  return;
16478              }
16479              if ($hb < $this->htmlvspace) {
16480                  $hd = 0;
16481              } else {
16482                  $hd = $hb - $this->htmlvspace;
16483                  $this->htmlvspace = $hb;
16484              }
16485              $this->Ln(($hbz + $hd), $cell);
16486          }
16487  
16488          /**
16489           * Set the default bullet to be used as LI bullet symbol
16490           * @param string $symbol character or string to be used (legal values are: '' = automatic, '!' = auto bullet, '#' = auto numbering, 'disc', 'disc', 'circle', 'square', '1', 'decimal', 'decimal-leading-zero', 'i', 'lower-roman', 'I', 'upper-roman', 'a', 'lower-alpha', 'lower-latin', 'A', 'upper-alpha', 'upper-latin', 'lower-greek')
16491           * @access public
16492           * @since 4.0.028 (2008-09-26)
16493           */
16494  		public function setLIsymbol($symbol='!') {
16495              $symbol = strtolower($symbol);
16496              switch ($symbol) {
16497                  case '!' :
16498                  case '#' :
16499                  case 'disc' :
16500                  case 'circle' :
16501                  case 'square' :
16502                  case '1':
16503                  case 'decimal':
16504                  case 'decimal-leading-zero':
16505                  case 'i':
16506                  case 'lower-roman':
16507                  case 'I':
16508                  case 'upper-roman':
16509                  case 'a':
16510                  case 'lower-alpha':
16511                  case 'lower-latin':
16512                  case 'A':
16513                  case 'upper-alpha':
16514                  case 'upper-latin':
16515                  case 'lower-greek': {
16516                      $this->lisymbol = $symbol;
16517                      break;
16518                  }
16519                  default : {
16520                      $this->lisymbol = '';
16521                  }
16522              }
16523          }
16524  
16525          /**
16526           * Set the booklet mode for double-sided pages.
16527           * @param boolean $booklet true set the booklet mode on, fals eotherwise.
16528           * @param float $inner Inner page margin.
16529           * @param float $outer Outer page margin.
16530           * @access public
16531           * @since 4.2.000 (2008-10-29)
16532           */
16533  		public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
16534              $this->booklet = $booklet;
16535              if ($inner >= 0) {
16536                  $this->lMargin = $inner;
16537              }
16538              if ($outer >= 0) {
16539                  $this->rMargin = $outer;
16540              }
16541          }
16542  
16543          /**
16544           * Swap the left and right margins.
16545           * @param boolean $reverse if true swap left and right margins.
16546           * @access protected
16547           * @since 4.2.000 (2008-10-29)
16548           */
16549  		protected function swapMargins($reverse=true) {
16550              if ($reverse) {
16551                  // swap left and right margins
16552                  $mtemp = $this->original_lMargin;
16553                  $this->original_lMargin = $this->original_rMargin;
16554                  $this->original_rMargin = $mtemp;
16555                  $deltam = $this->original_lMargin - $this->original_rMargin;
16556                  $this->lMargin += $deltam;
16557                  $this->rMargin -= $deltam;
16558              }
16559          }
16560  
16561          /**
16562           * Set the vertical spaces for HTML tags.
16563           * The array must have the following structure (example):
16564           * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
16565           * The first array level contains the tag names,
16566           * the second level contains 0 for opening tags or 1 for closing tags,
16567           * the third level contains the vertical space unit (h) and the number spaces to add (n).
16568           * If the h parameter is not specified, default values are used.
16569           * @param array $tagvs array of tags and relative vertical spaces.
16570           * @access public
16571           * @since 4.2.001 (2008-10-30)
16572           */
16573  		public function setHtmlVSpace($tagvs) {
16574              $this->tagvspaces = $tagvs;
16575          }
16576  
16577          /**
16578           * Set custom width for list indentation.
16579           * @param float $width width of the indentation. Use negative value to disable it.
16580           * @access public
16581           * @since 4.2.007 (2008-11-12)
16582           */
16583  		public function setListIndentWidth($width) {
16584              return $this->customlistindent = floatval($width);
16585          }
16586  
16587          /**
16588           * Set the top/bottom cell sides to be open or closed when the cell cross the page.
16589           * @param boolean $isopen if true keeps the top/bottom border open for the cell sides that cross the page.
16590           * @access public
16591           * @since 4.2.010 (2008-11-14)
16592           */
16593  		public function setOpenCell($isopen) {
16594              $this->opencell = $isopen;
16595          }
16596  
16597          /**
16598           * Set the color and font style for HTML links.
16599           * @param array $color RGB array of colors
16600           * @param string $fontstyle additional font styles to add
16601           * @access public
16602           * @since 4.4.003 (2008-12-09)
16603           */
16604  		public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
16605              $this->htmlLinkColorArray = $color;
16606              $this->htmlLinkFontStyle = $fontstyle;
16607          }
16608  
16609          /**
16610           * Convert HTML string containing value and unit of measure to user's units or points.
16611           * @param string $htmlval string containing values and unit
16612           * @param string $refsize reference value in points
16613           * @param string $defaultunit default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16614           * @param boolean $point if true returns points, otherwise returns value in user's units
16615           * @return float value in user's unit or point if $points=true
16616           * @access public
16617           * @since 4.4.004 (2008-12-10)
16618           */
16619          public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
16620              $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
16621              $retval = 0;
16622              $value = 0;
16623              $unit = 'px';
16624              $k = $this->k;
16625              if ($points) {
16626                  $k = 1;
16627              }
16628              if (in_array($defaultunit, $supportedunits)) {
16629                  $unit = $defaultunit;
16630              }
16631              if (is_numeric($htmlval)) {
16632                  $value = floatval($htmlval);
16633              } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
16634                  $value = floatval($mnum[1]);
16635                  if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
16636                      if (in_array($munit[1], $supportedunits)) {
16637                          $unit = $munit[1];
16638                      }
16639                  }
16640              }
16641              switch ($unit) {
16642                  // percentage
16643                  case '%': {
16644                      $retval = (($value * $refsize) / 100);
16645                      break;
16646                  }
16647                  // relative-size
16648                  case 'em': {
16649                      $retval = ($value * $refsize);
16650                      break;
16651                  }
16652                  case 'ex': {
16653                      $retval = $value * ($refsize / 2);
16654                      break;
16655                  }
16656                  // absolute-size
16657                  case 'in': {
16658                      $retval = ($value * $this->dpi) / $k;
16659                      break;
16660                  }
16661                  case 'cm': {
16662                      $retval = ($value / 2.54 * $this->dpi) / $k;
16663                      break;
16664                  }
16665                  case 'mm': {
16666                      $retval = ($value / 25.4 * $this->dpi) / $k;
16667                      break;
16668                  }
16669                  case 'pc': {
16670                      // one pica is 12 points
16671                      $retval = ($value * 12) / $k;
16672                      break;
16673                  }
16674                  case 'pt': {
16675                      $retval = $value / $k;
16676                      break;
16677                  }
16678                  case 'px': {
16679                      $retval = $this->pixelsToUnits($value);
16680                      break;
16681                  }
16682              }
16683              return $retval;
16684          }
16685  
16686          /**
16687           * Returns the Roman representation of an integer number
16688           * @param int number to convert
16689           * @return string roman representation of the specified number
16690           * @access public
16691           * @since 4.4.004 (2008-12-10)
16692           */
16693  		public function intToRoman($number) {
16694              $roman = '';
16695              while ($number >= 1000) {
16696                  $roman .= 'M';
16697                  $number -= 1000;
16698              }
16699              while ($number >= 900) {
16700                  $roman .= 'CM';
16701                  $number -= 900;
16702              }
16703              while ($number >= 500) {
16704                  $roman .= 'D';
16705                  $number -= 500;
16706              }
16707              while ($number >= 400) {
16708                  $roman .= 'CD';
16709                  $number -= 400;
16710              }
16711              while ($number >= 100) {
16712                  $roman .= 'C';
16713                  $number -= 100;
16714              }
16715              while ($number >= 90) {
16716                  $roman .= 'XC';
16717                  $number -= 90;
16718              }
16719              while ($number >= 50) {
16720                  $roman .= 'L';
16721                  $number -= 50;
16722              }
16723              while ($number >= 40) {
16724                  $roman .= 'XL';
16725                  $number -= 40;
16726              }
16727              while ($number >= 10) {
16728                  $roman .= 'X';
16729                  $number -= 10;
16730              }
16731              while ($number >= 9) {
16732                  $roman .= 'IX';
16733                  $number -= 9;
16734              }
16735              while ($number >= 5) {
16736                  $roman .= 'V';
16737                  $number -= 5;
16738              }
16739              while ($number >= 4) {
16740                  $roman .= 'IV';
16741                  $number -= 4;
16742              }
16743              while ($number >= 1) {
16744                  $roman .= 'I';
16745                  --$number;
16746              }
16747              return $roman;
16748          }
16749  
16750          /**
16751           * Output an HTML list bullet or ordered item symbol
16752           * @param int $listdepth list nesting level
16753           * @param string $listtype type of list
16754           * @param float $size current font size
16755           * @access protected
16756           * @since 4.4.004 (2008-12-10)
16757           */
16758  		protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
16759              $size /= $this->k;
16760              $fill = '';
16761              $color = $this->fgcolor;
16762              $width = 0;
16763              $textitem = '';
16764              $tmpx = $this->x;
16765              $lspace = $this->GetStringWidth('  ');
16766              if ($listtype == '!') {
16767                  // set default list type for unordered list
16768                  $deftypes = array('disc', 'circle', 'square');
16769                  $listtype = $deftypes[($listdepth - 1) % 3];
16770              } elseif ($listtype == '#') {
16771                  // set default list type for ordered list
16772                  $listtype = 'decimal';
16773              }
16774              switch ($listtype) {
16775                  // unordered types
16776                  case 'none': {
16777                      break;
16778                  }
16779                  case 'disc': {
16780                      $fill = 'F';
16781                  }
16782                  case 'circle': {
16783                      $fill .= 'D';
16784                      $r = $size / 6;
16785                      $lspace += (2 * $r);
16786                      if ($this->rtl) {
16787                          $this->x += $lspace;
16788                      } else {
16789                          $this->x -= $lspace;
16790                      }
16791                      $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, $fill, array('color'=>$color), $color, 8);
16792                      break;
16793                  }
16794                  case 'square': {
16795                      $l = $size / 3;
16796                      $lspace += $l;
16797                      if ($this->rtl) {;
16798                          $this->x += $lspace;
16799                      } else {
16800                          $this->x -= $lspace;
16801                      }
16802                      $this->Rect($this->x, ($this->y + (($this->lasth - $l)/ 2)), $l, $l, 'F', array(), $color);
16803                      break;
16804                  }
16805                  // ordered types
16806                  // $this->listcount[$this->listnum];
16807                  // $textitem
16808                  case '1':
16809                  case 'decimal': {
16810                      $textitem = $this->listcount[$this->listnum];
16811                      break;
16812                  }
16813                  case 'decimal-leading-zero': {
16814                      $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
16815                      break;
16816                  }
16817                  case 'i':
16818                  case 'lower-roman': {
16819                      $textitem = strtolower($this->intToRoman($this->listcount[$this->listnum]));
16820                      break;
16821                  }
16822                  case 'I':
16823                  case 'upper-roman': {
16824                      $textitem = $this->intToRoman($this->listcount[$this->listnum]);
16825                      break;
16826                  }
16827                  case 'a':
16828                  case 'lower-alpha':
16829                  case 'lower-latin': {
16830                      $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
16831                      break;
16832                  }
16833                  case 'A':
16834                  case 'upper-alpha':
16835                  case 'upper-latin': {
16836                      $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
16837                      break;
16838                  }
16839                  case 'lower-greek': {
16840                      $textitem = $this->unichr(945 + $this->listcount[$this->listnum] - 1);
16841                      break;
16842                  }
16843                  /*
16844                  // Types to be implemented (special handling)
16845                  case 'hebrew': {
16846                      break;
16847                  }
16848                  case 'armenian': {
16849                      break;
16850                  }
16851                  case 'georgian': {
16852                      break;
16853                  }
16854                  case 'cjk-ideographic': {
16855                      break;
16856                  }
16857                  case 'hiragana': {
16858                      break;
16859                  }
16860                  case 'katakana': {
16861                      break;
16862                  }
16863                  case 'hiragana-iroha': {
16864                      break;
16865                  }
16866                  case 'katakana-iroha': {
16867                      break;
16868                  }
16869                  */
16870                  default: {
16871                      $textitem = $this->listcount[$this->listnum];
16872                  }
16873              }
16874              if (!$this->empty_string($textitem)) {
16875                  // print ordered item
16876                  if ($this->rtl) {
16877                      $textitem = '.'.$textitem;
16878                  } else {
16879                      $textitem = $textitem.'.';
16880                  }
16881                  $lspace += $this->GetStringWidth($textitem);
16882                  if ($this->rtl) {
16883                      $this->x += $lspace;
16884                  } else {
16885                      $this->x -= $lspace;
16886                  }
16887                  $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
16888              }
16889              $this->x = $tmpx;
16890              $this->lispacer = '';
16891          }
16892  
16893          /**
16894           * Returns current graphic variables as array.
16895           * @return array graphic variables
16896           * @access protected
16897           * @since 4.2.010 (2008-11-14)
16898           */
16899  		protected function getGraphicVars() {
16900              $grapvars = array(
16901                  'FontFamily' => $this->FontFamily,
16902                  'FontStyle' => $this->FontStyle,
16903                  'FontSizePt' => $this->FontSizePt,
16904                  'rMargin' => $this->rMargin,
16905                  'lMargin' => $this->lMargin,
16906                  'cMargin' => $this->cMargin,
16907                  'LineWidth' => $this->LineWidth,
16908                  'linestyleWidth' => $this->linestyleWidth,
16909                  'linestyleCap' => $this->linestyleCap,
16910                  'linestyleJoin' => $this->linestyleJoin,
16911                  'linestyleDash' => $this->linestyleDash,
16912                  'textrendermode' => $this->textrendermode,
16913                  'textstrokewidth' => $this->textstrokewidth,
16914                  'DrawColor' => $this->DrawColor,
16915                  'FillColor' => $this->FillColor,
16916                  'TextColor' => $this->TextColor,
16917                  'ColorFlag' => $this->ColorFlag,
16918                  'bgcolor' => $this->bgcolor,
16919                  'fgcolor' => $this->fgcolor,
16920                  'htmlvspace' => $this->htmlvspace,
16921                  'lasth' => $this->lasth
16922                  );
16923              return $grapvars;
16924          }
16925  
16926          /**
16927           * Set graphic variables.
16928           * @param $gvars array graphic variables
16929           * @access protected
16930           * @since 4.2.010 (2008-11-14)
16931           */
16932  		protected function setGraphicVars($gvars) {
16933              $this->FontFamily = $gvars['FontFamily'];
16934              $this->FontStyle = $gvars['FontStyle'];
16935              $this->FontSizePt = $gvars['FontSizePt'];
16936              $this->rMargin = $gvars['rMargin'];
16937              $this->lMargin = $gvars['lMargin'];
16938              $this->cMargin = $gvars['cMargin'];
16939              $this->LineWidth = $gvars['LineWidth'];
16940              $this->linestyleWidth = $gvars['linestyleWidth'];
16941              $this->linestyleCap = $gvars['linestyleCap'];
16942              $this->linestyleJoin = $gvars['linestyleJoin'];
16943              $this->linestyleDash = $gvars['linestyleDash'];
16944              $this->textrendermode = $gvars['textrendermode'];
16945              $this->textstrokewidth = $gvars['textstrokewidth'];
16946              $this->DrawColor = $gvars['DrawColor'];
16947              $this->FillColor = $gvars['FillColor'];
16948              $this->TextColor = $gvars['TextColor'];
16949              $this->ColorFlag = $gvars['ColorFlag'];
16950              $this->bgcolor = $gvars['bgcolor'];
16951              $this->fgcolor = $gvars['fgcolor'];
16952              $this->htmlvspace = $gvars['htmlvspace'];
16953              //$this->lasth = $gvars['lasth'];
16954              $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
16955              if (!$this->empty_string($this->FontFamily)) {
16956                  $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
16957              }
16958          }
16959  
16960          /**
16961           * Returns a temporary filename for caching object on filesystem.
16962           * @param string $prefix prefix to add to filename
16963           * return string filename.
16964           * @access protected
16965           * @since 4.5.000 (2008-12-31)
16966           */
16967  		protected function getObjFilename($name) {
16968              return tempnam(K_PATH_CACHE, $name.'_');
16969          }
16970  
16971          /**
16972           * Writes data to a temporary file on filesystem.
16973           * @param string $file file name
16974           * @param mixed $data data to write on file
16975           * @param boolean $append if true append data, false replace.
16976           * @access protected
16977           * @since 4.5.000 (2008-12-31)
16978           */
16979  		protected function writeDiskCache($filename, $data, $append=false) {
16980              if ($append) {
16981                  $fmode = 'ab+';
16982              } else {
16983                  $fmode = 'wb+';
16984              }
16985              $f = @fopen($filename, $fmode);
16986              if (!$f) {
16987                  $this->Error('Unable to write cache file: '.$filename);
16988              } else {
16989                  fwrite($f, $data);
16990                  fclose($f);
16991              }
16992              // update file length (needed for transactions)
16993              if (!isset($this->cache_file_length['_'.$filename])) {
16994                  $this->cache_file_length['_'.$filename] = strlen($data);
16995              } else {
16996                  $this->cache_file_length['_'.$filename] += strlen($data);
16997              }
16998          }
16999  
17000          /**
17001           * Read data from a temporary file on filesystem.
17002           * @param string $file file name
17003           * @return mixed retrieved data
17004           * @access protected
17005           * @since 4.5.000 (2008-12-31)
17006           */
17007  		protected function readDiskCache($filename) {
17008              return file_get_contents($filename);
17009          }
17010  
17011          /**
17012           * Set buffer content (always append data).
17013           * @param string $data data
17014           * @access protected
17015           * @since 4.5.000 (2009-01-02)
17016           */
17017  		protected function setBuffer($data) {
17018              $this->bufferlen += strlen($data);
17019              if ($this->diskcache) {
17020                  if (!isset($this->buffer) OR $this->empty_string($this->buffer)) {
17021                      $this->buffer = $this->getObjFilename('buffer');
17022                  }
17023                  $this->writeDiskCache($this->buffer, $data, true);
17024              } else {
17025                  $this->buffer .= $data;
17026              }
17027          }
17028  
17029          /**
17030           * Get buffer content.
17031           * @return string buffer content
17032           * @access protected
17033           * @since 4.5.000 (2009-01-02)
17034           */
17035  		protected function getBuffer() {
17036              if ($this->diskcache) {
17037                  return $this->readDiskCache($this->buffer);
17038              } else {
17039                  return $this->buffer;
17040              }
17041          }
17042  
17043          /**
17044           * Set page buffer content.
17045           * @param int $page page number
17046           * @param string $data page data
17047           * @param boolean $append if true append data, false replace.
17048           * @access protected
17049           * @since 4.5.000 (2008-12-31)
17050           */
17051  		protected function setPageBuffer($page, $data, $append=false) {
17052              if ($this->diskcache) {
17053                  if (!isset($this->pages[$page])) {
17054                      $this->pages[$page] = $this->getObjFilename('page'.$page);
17055                  }
17056                  $this->writeDiskCache($this->pages[$page], $data, $append);
17057              } else {
17058                  if ($append) {
17059                      $this->pages[$page] .= $data;
17060                  } else {
17061                      $this->pages[$page] = $data;
17062                  }
17063              }
17064              if ($append AND isset($this->pagelen[$page])) {
17065                  $this->pagelen[$page] += strlen($data);
17066              } else {
17067                  $this->pagelen[$page] = strlen($data);
17068              }
17069          }
17070  
17071          /**
17072           * Get page buffer content.
17073           * @param int $page page number
17074           * @return string page buffer content or false in case of error
17075           * @access protected
17076           * @since 4.5.000 (2008-12-31)
17077           */
17078  		protected function getPageBuffer($page) {
17079              if ($this->diskcache) {
17080                  return $this->readDiskCache($this->pages[$page]);
17081              } elseif (isset($this->pages[$page])) {
17082                  return $this->pages[$page];
17083              }
17084              return false;
17085          }
17086  
17087          /**
17088           * Set image buffer content.
17089           * @param string $image image key
17090           * @param array $data image data
17091           * @access protected
17092           * @since 4.5.000 (2008-12-31)
17093           */
17094  		protected function setImageBuffer($image, $data) {
17095              if ($this->diskcache) {
17096                  if (!isset($this->images[$image])) {
17097                      $this->images[$image] = $this->getObjFilename('image'.$image);
17098                  }
17099                  $this->writeDiskCache($this->images[$image], serialize($data));
17100              } else {
17101                  $this->images[$image] = $data;
17102              }
17103              if (!in_array($image, $this->imagekeys)) {
17104                  $this->imagekeys[] = $image;
17105                  ++$this->numimages;
17106              }
17107          }
17108  
17109          /**
17110           * Set image buffer content for a specified sub-key.
17111           * @param string $image image key
17112           * @param string $key image sub-key
17113           * @param array $data image data
17114           * @access protected
17115           * @since 4.5.000 (2008-12-31)
17116           */
17117  		protected function setImageSubBuffer($image, $key, $data) {
17118              if (!isset($this->images[$image])) {
17119                  $this->setImageBuffer($image, array());
17120              }
17121              if ($this->diskcache) {
17122                  $tmpimg = $this->getImageBuffer($image);
17123                  $tmpimg[$key] = $data;
17124                  $this->writeDiskCache($this->images[$image], serialize($tmpimg));
17125              } else {
17126                  $this->images[$image][$key] = $data;
17127              }
17128          }
17129  
17130          /**
17131           * Get image buffer content.
17132           * @param string $image image key
17133           * @return string image buffer content or false in case of error
17134           * @access protected
17135           * @since 4.5.000 (2008-12-31)
17136           */
17137  		protected function getImageBuffer($image) {
17138              if ($this->diskcache AND isset($this->images[$image])) {
17139                  return unserialize($this->readDiskCache($this->images[$image]));
17140              } elseif (isset($this->images[$image])) {
17141                  return $this->images[$image];
17142              }
17143              return false;
17144          }
17145  
17146          /**
17147           * Set font buffer content.
17148           * @param string $font font key
17149           * @param array $data font data
17150           * @access protected
17151           * @since 4.5.000 (2009-01-02)
17152           */
17153  		protected function setFontBuffer($font, $data) {
17154              if ($this->diskcache) {
17155                  if (!isset($this->fonts[$font])) {
17156                      $this->fonts[$font] = $this->getObjFilename('font');
17157                  }
17158                  $this->writeDiskCache($this->fonts[$font], serialize($data));
17159              } else {
17160                  $this->fonts[$font] = $data;
17161              }
17162              if (!in_array($font, $this->fontkeys)) {
17163                  $this->fontkeys[] = $font;
17164              }
17165          }
17166  
17167          /**
17168           * Set font buffer content.
17169           * @param string $font font key
17170           * @param string $key font sub-key
17171           * @param array $data font data
17172           * @access protected
17173           * @since 4.5.000 (2009-01-02)
17174           */
17175  		protected function setFontSubBuffer($font, $key, $data) {
17176              if (!isset($this->fonts[$font])) {
17177                  $this->setFontBuffer($font, array());
17178              }
17179              if ($this->diskcache) {
17180                  $tmpfont = $this->getFontBuffer($font);
17181                  $tmpfont[$key] = $data;
17182                  $this->writeDiskCache($this->fonts[$font], serialize($tmpfont));
17183              } else {
17184                  $this->fonts[$font][$key] = $data;
17185              }
17186          }
17187  
17188          /**
17189           * Get font buffer content.
17190           * @param string $font font key
17191           * @return string font buffer content or false in case of error
17192           * @access protected
17193           * @since 4.5.000 (2009-01-02)
17194           */
17195  		protected function getFontBuffer($font) {
17196              if ($this->diskcache AND isset($this->fonts[$font])) {
17197                  return unserialize($this->readDiskCache($this->fonts[$font]));
17198              } elseif (isset($this->fonts[$font])) {
17199                  return $this->fonts[$font];
17200              }
17201              return false;
17202          }
17203  
17204          /**
17205           * Move a page to a previous position.
17206           * @param int $frompage number of the source page
17207           * @param int $topage number of the destination page (must be less than $frompage)
17208           * @return true in case of success, false in case of error.
17209           * @access public
17210           * @since 4.5.000 (2009-01-02)
17211           */
17212  		public function movePage($frompage, $topage) {
17213              if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
17214                  return false;
17215              }
17216              if ($frompage == $this->page) {
17217                  // close the page before moving it
17218                  $this->endPage();
17219              }
17220              // move all page-related states
17221              $tmppage = $this->pages[$frompage];
17222              $tmppagedim = $this->pagedim[$frompage];
17223              $tmppagelen = $this->pagelen[$frompage];
17224              $tmpintmrk = $this->intmrk[$frompage];
17225              if (isset($this->footerpos[$frompage])) {
17226                  $tmpfooterpos = $this->footerpos[$frompage];
17227              }
17228              if (isset($this->footerlen[$frompage])) {
17229                  $tmpfooterlen = $this->footerlen[$frompage];
17230              }
17231              if (isset($this->transfmrk[$frompage])) {
17232                  $tmptransfmrk = $this->transfmrk[$frompage];
17233              }
17234              if (isset($this->PageAnnots[$frompage])) {
17235                  $tmpannots = $this->PageAnnots[$frompage];
17236              }
17237              if (isset($this->newpagegroup[$frompage])) {
17238                  $tmpnewpagegroup = $this->newpagegroup[$frompage];
17239              }
17240              for ($i = $frompage; $i > $topage; --$i) {
17241                  $j = $i - 1;
17242                  // shift pages down
17243                  $this->pages[$i] = $this->pages[$j];
17244                  $this->pagedim[$i] = $this->pagedim[$j];
17245                  $this->pagelen[$i] = $this->pagelen[$j];
17246                  $this->intmrk[$i] = $this->intmrk[$j];
17247                  if (isset($this->footerpos[$j])) {
17248                      $this->footerpos[$i] = $this->footerpos[$j];
17249                  } elseif (isset($this->footerpos[$i])) {
17250                      unset($this->footerpos[$i]);
17251                  }
17252                  if (isset($this->footerlen[$j])) {
17253                      $this->footerlen[$i] = $this->footerlen[$j];
17254                  } elseif (isset($this->footerlen[$i])) {
17255                      unset($this->footerlen[$i]);
17256                  }
17257                  if (isset($this->transfmrk[$j])) {
17258                      $this->transfmrk[$i] = $this->transfmrk[$j];
17259                  } elseif (isset($this->transfmrk[$i])) {
17260                      unset($this->transfmrk[$i]);
17261                  }
17262                  if (isset($this->PageAnnots[$j])) {
17263                      $this->PageAnnots[$i] = $this->PageAnnots[$j];
17264                  } elseif (isset($this->PageAnnots[$i])) {
17265                      unset($this->PageAnnots[$i]);
17266                  }
17267                  if (isset($this->newpagegroup[$j])) {
17268                      $this->newpagegroup[$i] = $this->newpagegroup[$j];
17269                  } elseif (isset($this->newpagegroup[$i])) {
17270                      unset($this->newpagegroup[$i]);
17271                  }
17272              }
17273              $this->pages[$topage] = $tmppage;
17274              $this->pagedim[$topage] = $tmppagedim;
17275              $this->pagelen[$topage] = $tmppagelen;
17276              $this->intmrk[$topage] = $tmpintmrk;
17277              if (isset($tmpfooterpos)) {
17278                  $this->footerpos[$topage] = $tmpfooterpos;
17279              } elseif (isset($this->footerpos[$topage])) {
17280                  unset($this->footerpos[$topage]);
17281              }
17282              if (isset($tmpfooterlen)) {
17283                  $this->footerlen[$topage] = $tmpfooterlen;
17284              } elseif (isset($this->footerlen[$topage])) {
17285                  unset($this->footerlen[$topage]);
17286              }
17287              if (isset($tmptransfmrk)) {
17288                  $this->transfmrk[$topage] = $tmptransfmrk;
17289              } elseif (isset($this->transfmrk[$topage])) {
17290                  unset($this->transfmrk[$topage]);
17291              }
17292              if (isset($tmpannots)) {
17293                  $this->PageAnnots[$topage] = $tmpannots;
17294              } elseif (isset($this->PageAnnots[$topage])) {
17295                  unset($this->PageAnnots[$topage]);
17296              }
17297              if (isset($tmpnewpagegroup)) {
17298                  $this->newpagegroup[$topage] = $tmpnewpagegroup;
17299              } elseif (isset($this->newpagegroup[$topage])) {
17300                  unset($this->newpagegroup[$topage]);
17301              }
17302              // adjust outlines
17303              $tmpoutlines = $this->outlines;
17304              foreach ($tmpoutlines as $key => $outline) {
17305                  if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
17306                      $this->outlines[$key]['p'] = $outline['p'] + 1;
17307                  } elseif ($outline['p'] == $frompage) {
17308                      $this->outlines[$key]['p'] = $topage;
17309                  }
17310              }
17311              // adjust links
17312              $tmplinks = $this->links;
17313              foreach ($tmplinks as $key => $link) {
17314                  if (($link[0] >= $topage) AND ($link[0] < $frompage)) {
17315                      $this->links[$key][0] = $link[0] + 1;
17316                  } elseif ($link[0] == $frompage) {
17317                      $this->links[$key][0] = $topage;
17318                  }
17319              }
17320              // adjust javascript
17321              $tmpjavascript = $this->javascript;
17322              global $jfrompage, $jtopage;
17323              $jfrompage = $frompage;
17324              $jtopage = $topage;
17325              $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
17326                  create_function('$matches', 'global $jfrompage, $jtopage;
17327                  $pagenum = intval($matches[3]) + 1;
17328                  if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
17329                      $newpage = ($pagenum + 1);
17330                  } elseif ($pagenum == $jfrompage) {
17331                      $newpage = $jtopage;
17332                  } else {
17333                      $newpage = $pagenum;
17334                  }
17335                  --$newpage;
17336                  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
17337              // return to last page
17338              $this->lastPage(true);
17339              return true;
17340          }
17341  
17342          /**
17343           * Remove the specified page.
17344           * @param int $page page to remove
17345           * @return true in case of success, false in case of error.
17346           * @access public
17347           * @since 4.6.004 (2009-04-23)
17348           */
17349  		public function deletePage($page) {
17350              if ($page > $this->numpages) {
17351                  return false;
17352              }
17353              // delete current page
17354              unset($this->pages[$page]);
17355              unset($this->pagedim[$page]);
17356              unset($this->pagelen[$page]);
17357              unset($this->intmrk[$page]);
17358              if (isset($this->footerpos[$page])) {
17359                  unset($this->footerpos[$page]);
17360              }
17361              if (isset($this->footerlen[$page])) {
17362                  unset($this->footerlen[$page]);
17363              }
17364              if (isset($this->transfmrk[$page])) {
17365                  unset($this->transfmrk[$page]);
17366              }
17367              if (isset($this->PageAnnots[$page])) {
17368                  unset($this->PageAnnots[$page]);
17369              }
17370              if (isset($this->newpagegroup[$page])) {
17371                  unset($this->newpagegroup[$page]);
17372              }
17373              if (isset($this->pageopen[$page])) {
17374                  unset($this->pageopen[$page]);
17375              }
17376              // update remaining pages
17377              for ($i = $page; $i < $this->numpages; ++$i) {
17378                  $j = $i + 1;
17379                  // shift pages
17380                  $this->pages[$i] = $this->pages[$j];
17381                  $this->pagedim[$i] = $this->pagedim[$j];
17382                  $this->pagelen[$i] = $this->pagelen[$j];
17383                  $this->intmrk[$i] = $this->intmrk[$j];
17384                  if (isset($this->footerpos[$j])) {
17385                      $this->footerpos[$i] = $this->footerpos[$j];
17386                  } elseif (isset($this->footerpos[$i])) {
17387                      unset($this->footerpos[$i]);
17388                  }
17389                  if (isset($this->footerlen[$j])) {
17390                      $this->footerlen[$i] = $this->footerlen[$j];
17391                  } elseif (isset($this->footerlen[$i])) {
17392                      unset($this->footerlen[$i]);
17393                  }
17394                  if (isset($this->transfmrk[$j])) {
17395                      $this->transfmrk[$i] = $this->transfmrk[$j];
17396                  } elseif (isset($this->transfmrk[$i])) {
17397                      unset($this->transfmrk[$i]);
17398                  }
17399                  if (isset($this->PageAnnots[$j])) {
17400                      $this->PageAnnots[$i] = $this->PageAnnots[$j];
17401                  } elseif (isset($this->PageAnnots[$i])) {
17402                      unset($this->PageAnnots[$i]);
17403                  }
17404                  if (isset($this->newpagegroup[$j])) {
17405                      $this->newpagegroup[$i] = $this->newpagegroup[$j];
17406                  } elseif (isset($this->newpagegroup[$i])) {
17407                      unset($this->newpagegroup[$i]);
17408                  }
17409                  if (isset($this->pageopen[$j])) {
17410                      $this->pageopen[$i] = $this->pageopen[$j];
17411                  } elseif (isset($this->pageopen[$i])) {
17412                      unset($this->pageopen[$i]);
17413                  }
17414              }
17415              // remove last page
17416              unset($this->pages[$this->numpages]);
17417              unset($this->pagedim[$this->numpages]);
17418              unset($this->pagelen[$this->numpages]);
17419              unset($this->intmrk[$this->numpages]);
17420              if (isset($this->footerpos[$this->numpages])) {
17421                  unset($this->footerpos[$this->numpages]);
17422              }
17423              if (isset($this->footerlen[$this->numpages])) {
17424                  unset($this->footerlen[$this->numpages]);
17425              }
17426              if (isset($this->transfmrk[$this->numpages])) {
17427                  unset($this->transfmrk[$this->numpages]);
17428              }
17429              if (isset($this->PageAnnots[$this->numpages])) {
17430                  unset($this->PageAnnots[$this->numpages]);
17431              }
17432              if (isset($this->newpagegroup[$this->numpages])) {
17433                  unset($this->newpagegroup[$this->numpages]);
17434              }
17435              if (isset($this->pageopen[$this->numpages])) {
17436                  unset($this->pageopen[$this->numpages]);
17437              }
17438              --$this->numpages;
17439              $this->page = $this->numpages;
17440              // adjust outlines
17441              $tmpoutlines = $this->outlines;
17442              foreach ($tmpoutlines as $key => $outline) {
17443                  if ($outline['p'] > $page) {
17444                      $this->outlines[$key]['p'] = $outline['p'] - 1;
17445                  } elseif ($outline['p'] == $page) {
17446                      unset($this->outlines[$key]);
17447                  }
17448              }
17449              // adjust links
17450              $tmplinks = $this->links;
17451              foreach ($tmplinks as $key => $link) {
17452                  if ($link[0] > $page) {
17453                      $this->links[$key][0] = $link[0] - 1;
17454                  } elseif ($link[0] == $page) {
17455                      unset($this->links[$key]);
17456                  }
17457              }
17458              // adjust javascript
17459              $tmpjavascript = $this->javascript;
17460              global $jpage;
17461              $jpage = $page;
17462              $this->javascript = preg_replace_callback('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/',
17463                  create_function('$matches', 'global $jpage;
17464                  $pagenum = intval($matches[3]) + 1;
17465                  if ($pagenum >= $jpage) {
17466                      $newpage = ($pagenum - 1);
17467                  } elseif ($pagenum == $jpage) {
17468                      $newpage = 1;
17469                  } else {
17470                      $newpage = $pagenum;
17471                  }
17472                  --$newpage;
17473                  return "this.addField(\'".$matches[1]."\',\'".$matches[2]."\',".$newpage."";'), $tmpjavascript);
17474              // return to last page
17475              $this->lastPage(true);
17476              return true;
17477          }
17478  
17479          /**
17480           * Clone the specified page to a new page.
17481           * @param int $page number of page to copy (0 = current page)
17482           * @return true in case of success, false in case of error.
17483           * @access public
17484           * @since 4.9.015 (2010-04-20)
17485           */
17486  		public function copyPage($page=0) {
17487              if ($page == 0) {
17488                  // default value
17489                  $page = $this->page;
17490              }
17491              if ($page > $this->numpages) {
17492                  return false;
17493              }
17494              if ($page == $this->page) {
17495                  // close the page before cloning it
17496                  $this->endPage();
17497              }
17498              // copy all page-related states
17499              ++$this->numpages;
17500              $this->page = $this->numpages;
17501              $this->pages[$this->page] = $this->pages[$page];
17502              $this->pagedim[$this->page] = $this->pagedim[$page];
17503              $this->pagelen[$this->page] = $this->pagelen[$page];
17504              $this->intmrk[$this->page] = $this->intmrk[$page];
17505              $this->pageopen[$this->page] = false;
17506              if (isset($this->footerpos[$page])) {
17507                  $this->footerpos[$this->page] = $this->footerpos[$page];
17508              }
17509              if (isset($this->footerlen[$page])) {
17510                  $this->footerlen[$this->page] = $this->footerlen[$page];
17511              }
17512              if (isset($this->transfmrk[$page])) {
17513                  $this->transfmrk[$this->page] = $this->transfmrk[$page];
17514              }
17515              if (isset($this->PageAnnots[$page])) {
17516                  $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
17517              }
17518              if (isset($this->newpagegroup[$page])) {
17519                  $this->newpagegroup[$this->page] = $this->newpagegroup[$page];
17520              }
17521              // copy outlines
17522              $tmpoutlines = $this->outlines;
17523              foreach ($tmpoutlines as $key => $outline) {
17524                  if ($outline['p'] == $page) {
17525                      $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'y' => $outline['y'], 'p' => $this->page);
17526                  }
17527              }
17528              // copy links
17529              $tmplinks = $this->links;
17530              foreach ($tmplinks as $key => $link) {
17531                  if ($link[0] == $page) {
17532                      $this->links[] = array($this->page, $link[1]);
17533                  }
17534              }
17535              // return to last page
17536              $this->lastPage(true);
17537              return true;
17538          }
17539  
17540          /**
17541           * Output a Table of Content Index (TOC).
17542           * Before calling this method you have to open the page using the addTOCPage() method.
17543           * After calling this method you have to call endTOCPage() to close the TOC page.
17544           * You can override this method to achieve different styles.
17545           * @param int $page page number where this TOC should be inserted (leave empty for current page).
17546           * @param string $numbersfont set the font for page numbers (please use monospaced font for better alignment).
17547           * @param string $filler string used to fill the space between text and page number.
17548           * @param string $toc_name name to use for TOC bookmark.
17549           * @access public
17550           * @author Nicola Asuni
17551           * @since 4.5.000 (2009-01-02)
17552           * @see addTOCPage(), endTOCPage(), addHTMLTOC()
17553           */
17554  		public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC') {
17555              $fontsize = $this->FontSizePt;
17556              $fontfamily = $this->FontFamily;
17557              $fontstyle = $this->FontStyle;
17558              $w = $this->w - $this->lMargin - $this->rMargin;
17559              $spacer = $this->GetStringWidth(chr(32)) * 4;
17560              $page_first = $this->getPage();
17561              $lmargin = $this->lMargin;
17562              $rmargin = $this->rMargin;
17563              $x_start = $this->GetX();
17564              if ($this->empty_string($numbersfont)) {
17565                  $numbersfont = $this->default_monospaced_font;
17566              }
17567              if ($this->empty_string($filler)) {
17568                  $filler = ' ';
17569              }
17570              if ($this->empty_string($page)) {
17571                  $gap = ' ';
17572              } else {
17573                  $gap = '';
17574              }
17575              foreach ($this->outlines as $key => $outline) {
17576                  if ($this->rtl) {
17577                      $aligntext = 'R';
17578                      $alignnum = 'L';
17579                  } else {
17580                      $aligntext = 'L';
17581                      $alignnum = 'R';
17582                  }
17583                  if ($outline['l'] == 0) {
17584                      $this->SetFont($fontfamily, $fontstyle.'B', $fontsize);
17585                  } else {
17586                      $this->SetFont($fontfamily, $fontstyle, $fontsize - $outline['l']);
17587                  }
17588                  $indent = ($spacer * $outline['l']);
17589                  if ($this->rtl) {
17590                      $this->rMargin += $indent;
17591                      $this->x -= $indent;
17592                  } else {
17593                      $this->lMargin += $indent;
17594                      $this->x += $indent;
17595                  }
17596                  $link = $this->AddLink();
17597                  $this->SetLink($link, 0, $outline['p']);
17598                  // write the text
17599                  $this->Write(0, $outline['t'], $link, 0, $aligntext, false, 0, false, false, 0);
17600                  $this->SetFont($numbersfont, $fontstyle, $fontsize);
17601                  if ($this->empty_string($page)) {
17602                      $pagenum = $outline['p'];
17603                  } else {
17604                      // placemark to be replaced with the correct number
17605                      $pagenum = '{#'.($outline['p']).'}';
17606                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
17607                          $pagenum = '{'.$pagenum.'}';
17608                      }
17609                  }
17610                  $numwidth = $this->GetStringWidth($pagenum);
17611                  if ($this->rtl) {
17612                      $tw = $this->x - $this->lMargin;
17613                  } else {
17614                      $tw = $this->w - $this->rMargin - $this->x;
17615                  }
17616                  $fw = $tw - $numwidth - $this->GetStringWidth(chr(32));
17617                  $numfills = floor($fw / $this->GetStringWidth($filler));
17618                  if ($numfills > 0) {
17619                      $rowfill = str_repeat($filler, $numfills);
17620                  } else {
17621                      $rowfill = '';
17622                  }
17623                  if ($this->rtl) {
17624                      $pagenum = $pagenum.$gap.$rowfill.' ';
17625                  } else {
17626                      $pagenum = ' '.$rowfill.$gap.$pagenum;
17627                  }
17628                  // write the number
17629                  $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
17630                  $this->SetX($x_start);
17631                  $this->lMargin = $lmargin;
17632                  $this->rMargin = $rmargin;
17633              }
17634              $page_last = $this->getPage();
17635              $numpages = $page_last - $page_first + 1;
17636              if (!$this->empty_string($page)) {
17637                  for ($p = $page_first; $p <= $page_last; ++$p) {
17638                      // get page data
17639                      $temppage = $this->getPageBuffer($p);
17640                      for ($n = 1; $n <= $this->numpages; ++$n) {
17641                          // update page numbers
17642                          $k = '{#'.$n.'}';
17643                          $ku = '{'.$k.'}';
17644                          $alias_a = $this->_escape($k);
17645                          $alias_au = $this->_escape('{'.$k.'}');
17646                          if ($this->isunicode) {
17647                              $alias_b = $this->_escape($this->UTF8ToLatin1($k));
17648                              $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
17649                              $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
17650                              $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
17651                          }
17652                          if ($n >= $page) {
17653                              $np = $n + $numpages;
17654                          } else {
17655                              $np = $n;
17656                          }
17657                          $ns = $this->formatTOCPageNumber($np);
17658                          $nu = $ns;
17659                          $sdiff = strlen($k) - strlen($ns) - 1;
17660                          $sdiffu = strlen($ku) - strlen($ns) - 1;
17661                          $sfill = str_repeat($filler, $sdiff);
17662                          $sfillu = str_repeat($filler, $sdiffu);
17663                          if ($this->rtl) {
17664                              $ns = $ns.' '.$sfill;
17665                              $nu = $nu.' '.$sfillu;
17666                          } else {
17667                              $ns = $sfill.' '.$ns;
17668                              $nu = $sfillu.' '.$nu;
17669                          }
17670                          $nu = $this->UTF8ToUTF16BE($nu, false);
17671                          $temppage = str_replace($alias_au, $nu, $temppage);
17672                          if ($this->isunicode) {
17673                              $temppage = str_replace($alias_bu, $nu, $temppage);
17674                              $temppage = str_replace($alias_cu, $nu, $temppage);
17675                              $temppage = str_replace($alias_b, $ns, $temppage);
17676                              $temppage = str_replace($alias_c, $ns, $temppage);
17677                          }
17678                          $temppage = str_replace($alias_a, $ns, $temppage);
17679                      }
17680                      // save changes
17681                      $this->setPageBuffer($p, $temppage);
17682                  }
17683                  // move pages
17684                  $this->Bookmark($toc_name, 0, 0, $page_first);
17685                  for ($i = 0; $i < $numpages; ++$i) {
17686                      $this->movePage($page_last, $page);
17687                  }
17688              }
17689          }
17690  
17691          /**
17692           * Output a Table Of Content Index (TOC) using HTML templates.
17693           * Before calling this method you have to open the page using the addTOCPage() method.
17694           * After calling this method you have to call endTOCPage() to close the TOC page.
17695           * @param int $page page number where this TOC should be inserted (leave empty for current page).
17696           * @param string $toc_name name to use for TOC bookmark.
17697           * @param array $templates array of html templates. Use: #TOC_DESCRIPTION# for bookmark title, #TOC_PAGE_NUMBER# for page number.
17698           * @parma boolean $correct_align if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL)
17699           * @access public
17700           * @author Nicola Asuni
17701           * @since 5.0.001 (2010-05-06)
17702           * @see addTOCPage(), endTOCPage(), addTOC()
17703           */
17704  		public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true) {
17705              $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
17706              $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
17707              // set new style for link
17708              $this->htmlLinkColorArray = array();
17709              $this->htmlLinkFontStyle = '';
17710              $page_first = $this->getPage();
17711              foreach ($this->outlines as $key => $outline) {
17712                  if ($this->empty_string($page)) {
17713                      $pagenum = $outline['p'];
17714                  } else {
17715                      // placemark to be replaced with the correct number
17716                      $pagenum = '{#'.($outline['p']).'}';
17717                      if (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0')) {
17718                          $pagenum = '{'.$pagenum.'}';
17719                      }
17720                  }
17721                  // get HTML template
17722                  $row = $templates[$outline['l']];
17723                  // replace templates with current values
17724                  $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
17725                  $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
17726                  // add link to page
17727                  $row = '<a href="#'.$outline['p'].'">'.$row.'</a>';
17728                  // write bookmark entry
17729                  $this->writeHTML($row, false, false, true, false, '');
17730              }
17731              // restore link styles
17732              $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
17733              $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
17734              // move TOC page and replace numbers
17735              $page_last = $this->getPage();
17736              $numpages = $page_last - $page_first + 1;
17737              if (!$this->empty_string($page)) {
17738                  for ($p = $page_first; $p <= $page_last; ++$p) {
17739                      // get page data
17740                      $temppage = $this->getPageBuffer($p);
17741                      for ($n = 1; $n <= $this->numpages; ++$n) {
17742                          // update page numbers
17743                          $k = '{#'.$n.'}';
17744                          $ku = '{'.$k.'}';
17745                          $alias_a = $this->_escape($k);
17746                          $alias_au = $this->_escape('{'.$k.'}');
17747                          if ($this->isunicode) {
17748                              $alias_b = $this->_escape($this->UTF8ToLatin1($k));
17749                              $alias_bu = $this->_escape($this->UTF8ToLatin1($ku));
17750                              $alias_c = $this->_escape($this->utf8StrRev($k, false, $this->tmprtl));
17751                              $alias_cu = $this->_escape($this->utf8StrRev($ku, false, $this->tmprtl));
17752                          }
17753                          if ($n >= $page) {
17754                              $np = $n + $numpages;
17755                          } else {
17756                              $np = $n;
17757                          }
17758                          $ns = $this->formatTOCPageNumber($np);
17759                          $nu = $ns;
17760                          if ($correct_align) {
17761                              $sdiff = strlen($k) - strlen($ns);
17762                              $sdiffu = strlen($ku) - strlen($ns);
17763                              $sfill = str_repeat(' ', $sdiff);
17764                              $sfillu = str_repeat(' ', $sdiffu);
17765                              if ($this->rtl) {
17766                                  $ns = $ns.$sfill;
17767                                  $nu = $nu.$sfillu;
17768                              } else {
17769                                  $ns = $sfill.$ns;
17770                                  $nu = $sfillu.$nu;
17771                              }
17772                          }
17773                          $nu = $this->UTF8ToUTF16BE($nu, false);
17774                          $temppage = str_replace($alias_au, $nu, $temppage);
17775                          if ($this->isunicode) {
17776                              $temppage = str_replace($alias_bu, $nu, $temppage);
17777                              $temppage = str_replace($alias_cu, $nu, $temppage);
17778                              $temppage = str_replace($alias_b, $ns, $temppage);
17779                              $temppage = str_replace($alias_c, $ns, $temppage);
17780                          }
17781                          $temppage = str_replace($alias_a, $ns, $temppage);
17782                      }
17783                      // save changes
17784                      $this->setPageBuffer($p, $temppage);
17785                  }
17786                  // move pages
17787                  $this->Bookmark($toc_name, 0, 0, $page_first);
17788                  for ($i = 0; $i < $numpages; ++$i) {
17789                      $this->movePage($page_last, $page);
17790                  }
17791              }
17792          }
17793  
17794          /**
17795           * Stores a copy of the current TCPDF object used for undo operation.
17796           * @access public
17797           * @since 4.5.029 (2009-03-19)
17798           */
17799  		public function startTransaction() {
17800              if (isset($this->objcopy)) {
17801                  // remove previous copy
17802                  $this->commitTransaction();
17803              }
17804              // record current page number and Y position
17805              $this->start_transaction_page = $this->page;
17806              $this->start_transaction_y = $this->y;
17807              // clone current object
17808              $this->objcopy = $this->objclone($this);
17809          }
17810  
17811          /**
17812           * Delete the copy of the current TCPDF object used for undo operation.
17813           * @access public
17814           * @since 4.5.029 (2009-03-19)
17815           */
17816  		public function commitTransaction() {
17817              if (isset($this->objcopy)) {
17818                  $this->objcopy->_destroy(true, true);
17819                  unset($this->objcopy);
17820              }
17821          }
17822  
17823          /**
17824           * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
17825           * @param boolean $self if true restores current class object to previous state without the need of reassignment via the returned value.
17826           * @return TCPDF object.
17827           * @access public
17828           * @since 4.5.029 (2009-03-19)
17829           */
17830  		public function rollbackTransaction($self=false) {
17831              if (isset($this->objcopy)) {
17832                  if (isset($this->objcopy->diskcache) AND $this->objcopy->diskcache) {
17833                      // truncate files to previous values
17834                      foreach ($this->objcopy->cache_file_length as $file => $length) {
17835                          $file = substr($file, 1);
17836                          $handle = fopen($file, 'r+');
17837                          ftruncate($handle, $length);
17838                      }
17839                  }
17840                  $this->_destroy(true, true);
17841                  if ($self) {
17842                      $objvars = get_object_vars($this->objcopy);
17843                      foreach ($objvars as $key => $value) {
17844                          $this->$key = $value;
17845                      }
17846                  }
17847                  return $this->objcopy;
17848              }
17849              return $this;
17850          }
17851  
17852          /**
17853           * Creates a copy of a class object
17854           * @param object $object class object to be cloned
17855           * @return cloned object
17856           * @access public
17857           * @since 4.5.029 (2009-03-19)
17858           */
17859  		public function objclone($object) {
17860              return @clone($object);
17861          }
17862  
17863          /**
17864           * Determine whether a string is empty.
17865           * @param string $str string to be checked
17866           * @return boolean true if string is empty
17867           * @access public
17868           * @since 4.5.044 (2009-04-16)
17869           */
17870  		public function empty_string($str) {
17871              return (is_null($str) OR (is_string($str) AND (strlen($str) == 0)));
17872          }
17873  
17874          /**
17875           * Find position of last occurrence of a substring in a string
17876           * @param string $haystack The string to search in.
17877           * @param string $needle substring to search.
17878           * @param int $offset May be specified to begin searching an arbitrary number of characters into the string.
17879           * @return Returns the position where the needle exists. Returns FALSE if the needle was not found.
17880           * @access public
17881           * @since 4.8.038 (2010-03-13)
17882           */
17883  		public function revstrpos($haystack, $needle, $offset = 0) {
17884              $length = strlen($haystack);
17885              $offset = ($offset > 0)?($length - $offset):abs($offset);
17886              $pos = strpos(strrev($haystack), strrev($needle), $offset);
17887              return ($pos === false)?false:($length - $pos - strlen($needle));
17888          }
17889  
17890          // --- MULTI COLUMNS METHODS -----------------------
17891  
17892          /**
17893           * Set multiple columns of the same size
17894           * @param int $numcols number of columns (set to zero to disable columns mode)
17895           * @param int $width column width
17896           * @param int $y column starting Y position (leave empty for current Y position)
17897           * @access public
17898            * @since 4.9.001 (2010-03-28)
17899           */
17900  		public function setEqualColumns($numcols=0, $width=0, $y='') {
17901              $this->columns = array();
17902              if ($numcols < 2) {
17903                  $numcols = 0;
17904                  $this->columns = array();
17905              } else {
17906                  // maximum column width
17907                  $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
17908                  if ($width > $maxwidth) {
17909                      $width = $maxwidth;
17910                  }
17911                  if ($this->empty_string($y)) {
17912                      $y = $this->y;
17913                  }
17914                  // space between columns
17915                  $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
17916                  // fill the columns array (with, space, starting Y position)
17917                  for ($i = 0; $i < $numcols; ++$i) {
17918                      $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
17919                  }
17920              }
17921              $this->num_columns = $numcols;
17922              $this->current_column = 0;
17923              $this->column_start_page = $this->page;
17924          }
17925  
17926          /**
17927           * Set columns array.
17928           * Each column is represented by and array with the following keys: (w = width, s = space between columns, y = column top position).
17929           * @param array $columns
17930           * @access public
17931            * @since 4.9.001 (2010-03-28)
17932           */
17933  		public function setColumnsArray($columns) {
17934              $this->columns = $columns;
17935              $this->num_columns = count($columns);
17936              $this->current_column = 0;
17937              $this->column_start_page = $this->page;
17938          }
17939  
17940          /**
17941           * Set position at a given column
17942           * @param int $col column number (from 0 to getNumberOfColumns()-1); empty string = current column.
17943           * @access public
17944            * @since 4.9.001 (2010-03-28)
17945           */
17946  		public function selectColumn($col='') {
17947              if (is_string($col)) {
17948                  $col = $this->current_column;
17949              } elseif($col >= $this->num_columns) {
17950                  $col = 0;
17951              }
17952              if ($this->num_columns > 1) {
17953                  if ($col != $this->current_column) {
17954                      // move pointer at column top on the first page
17955                      if ($this->column_start_page == $this->page) {
17956                          $this->y = $this->columns[$col]['y'];
17957                      } else {
17958                          $this->y = $this->tMargin;
17959                      }
17960                  }
17961                  // set X position of the current column by case
17962                  $listindent = ($this->listindentlevel * $this->listindent);
17963                  if ($this->rtl) {
17964                      $x = $this->w - $this->original_rMargin - ($col * ($this->columns[$col]['w'] + $this->columns[$col]['s']));
17965                      $this->SetRightMargin($this->w - $x + $listindent);
17966                      $this->SetLeftMargin($x - $this->columns[$col]['w']);
17967                      $this->x = $x - $listindent;
17968                  } else {
17969                      $x = $this->original_lMargin + ($col * ($this->columns[$col]['w'] + $this->columns[$col]['s']));
17970                      $this->SetLeftMargin($x + $listindent);
17971                      $this->SetRightMargin($this->w - $x - $this->columns[$col]['w']);
17972                      $this->x = $x + $listindent;
17973                  }
17974                  $this->columns[$col]['x'] = $x;
17975              }
17976              $this->current_column = $col;
17977              // fix for HTML mode
17978              $this->newline = true;
17979              // print HTML table header (if any)
17980              if (!$this->empty_string($this->thead) AND (!$this->inthead)) {
17981                  // print table header
17982                  $this->writeHTML($this->thead, false, false, false, false, '');
17983              }
17984          }
17985  
17986          /**
17987           * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17988           * @param array $pararray parameters array
17989           * @return sting containing serialized data
17990           * @access public
17991            * @since 4.9.006 (2010-04-02)
17992           */
17993  		public function serializeTCPDFtagParameters($pararray) {
17994              return urlencode(serialize($pararray));
17995          }
17996  
17997          /**
17998           * Set Text rendering mode.
17999           * @param int $stroke outline size in user units (0 = disable).
18000           * @param boolean $fill if true fills the text (default).
18001           * @param boolean $clip if true activate clipping mode
18002           * @access public
18003           * @since 4.9.008 (2009-04-02)
18004           */
18005  		public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
18006              // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
18007              // convert text rendering parameters
18008              if ($stroke < 0) {
18009                  $stroke = 0;
18010              }
18011              if ($fill === true) {
18012                  if ($stroke > 0) {
18013                      if ($clip === true) {
18014                          // Fill, then stroke text and add to path for clipping
18015                          $textrendermode = 6;
18016                      } else {
18017                          // Fill, then stroke text
18018                          $textrendermode = 2;
18019                      }
18020                      $textstrokewidth = $stroke;
18021                  } else {
18022                      if ($clip === true) {
18023                          // Fill text and add to path for clipping
18024                          $textrendermode = 4;
18025                      } else {
18026                          // Fill text
18027                          $textrendermode = 0;
18028                      }
18029                  }
18030              } else {
18031                  if ($stroke > 0) {
18032                      if ($clip === true) {
18033                          // Stroke text and add to path for clipping
18034                          $textrendermode = 5;
18035                      } else {
18036                          // Stroke text
18037                          $textrendermode = 1;
18038                      }
18039                      $textstrokewidth = $stroke;
18040                  } else {
18041                      if ($clip === true) {
18042                          // Add text to path for clipping
18043                          $textrendermode = 7;
18044                      } else {
18045                          // Neither fill nor stroke text (invisible)
18046                          $textrendermode = 3;
18047                      }
18048                  }
18049              }
18050              $this->textrendermode = $textrendermode;
18051              $this->textstrokewidth = $stroke * $this->k;
18052          }
18053  
18054          /**
18055           * Returns an array of chars containing soft hyphens.
18056           * @param array $word array of chars
18057           * @param array $patterns Array of hypenation patterns.
18058           * @param array $dictionary Array of words to be returned without applying the hyphenation algoritm.
18059           * @param int $leftmin Minimum number of character to leave on the left of the word without applying the hyphens.
18060           * @param int $rightmin Minimum number of character to leave on the right of the word without applying the hyphens.
18061           * @param int $charmin Minimum word lenght to apply the hyphenation algoritm.
18062           * @param int $charmax Maximum lenght of broken piece of word.
18063           * @return array text with soft hyphens
18064           * @author Nicola Asuni
18065           * @since 4.9.012 (2010-04-12)
18066           * @access protected
18067           */
18068  		protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
18069              $hyphenword = array(); // hyphens positions
18070              $numchars = count($word);
18071              if ($numchars <= $charmin) {
18072                  return $word;
18073              }
18074              $word_string = $this->UTF8ArrSubString($word);
18075              // some words will be returned as-is
18076              $pattern = '/^([a-zA-Z0-9_\.\-]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
18077              if (preg_match($pattern, $word_string) > 0) {
18078                  // email
18079                  return $word;
18080              }
18081              $pattern = '/(([a-zA-Z0-9\-]+\.)?)((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/';
18082              if (preg_match($pattern, $word_string) > 0) {
18083                  // URL
18084                  return $word;
18085              }
18086              if (isset($dictionary[$word_string])) {
18087                  return $this->UTF8StringToArray($dictionary[$word_string]);
18088              }
18089              // suround word with '_' characters
18090              $tmpword = array_merge(array(95), $word, array(95));
18091              $tmpnumchars = $numchars + 2;
18092              $maxpos = $tmpnumchars - $charmin;
18093              for ($pos = 0; $pos < $maxpos; ++$pos) {
18094                  $imax = min(($tmpnumchars - $pos), $charmax);
18095                  for ($i = $charmin; $i <= $imax; ++$i) {
18096                      $subword = strtolower($this->UTF8ArrSubString($tmpword, $pos, $pos + $i));
18097                      if (isset($patterns[$subword])) {
18098                          $pattern = $this->UTF8StringToArray($patterns[$subword]);
18099                          $pattern_length = count($pattern);
18100                          $digits = 1;
18101                          for ($j = 0; $j < $pattern_length; ++$j) {
18102                              // check if $pattern[$j] is a number
18103                              if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
18104                                  if ($j == 0) {
18105                                      $zero = $pos - 1;
18106                                  } else {
18107                                      $zero = $pos + $j - $digits;
18108                                  }
18109                                  if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] != $pattern[$j])) {
18110                                      $hyphenword[$zero] = $this->unichr($pattern[$j]);
18111                                  }
18112                                  ++$digits;
18113                              }
18114                          }
18115                      }
18116                  }
18117              }
18118              $inserted = 0;
18119              $maxpos = $numchars - $rightmin;
18120              for($i = $leftmin; $i <= $maxpos; ++$i) {
18121                  if(isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
18122                      // 173 = soft hyphen character
18123                      array_splice($word, $i + $inserted, 0, 173);
18124                      ++$inserted;
18125                  }
18126              }
18127              return $word;
18128          }
18129  
18130          /**
18131           * Returns an array of hyphenation patterns.
18132           * @param string $file TEX file containing hypenation patterns. TEX pattrns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
18133           * @return array of hyphenation patterns
18134           * @author Nicola Asuni
18135           * @since 4.9.012 (2010-04-12)
18136           * @access public
18137           */
18138  		public function getHyphenPatternsFromTEX($file) {
18139              // TEX patterns are available at:
18140              // http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
18141              $data = file_get_contents($file);
18142              $patterns = array();
18143              // remove comments
18144              $data = preg_replace('/\%[^\n]*/', '', $data);
18145              // extract the patterns part
18146              preg_match('/\\\\patterns\{([^\}]*)\}/i', $data, $matches);
18147              $data = trim(substr($matches[0], 10, -1));
18148              // extract each pattern
18149              $patterns_array = preg_split('/[\s]+/', $data);
18150              // create new language array of patterns
18151              $patterns = array();
18152              foreach($patterns_array as $val) {
18153                  if (!$this->empty_string($val)) {
18154                      $val = trim($val);
18155                      $val = str_replace('\'', '\\\'', $val);
18156                      $key = preg_replace('/[0-9]+/', '', $val);
18157                      $patterns[$key] = $val;
18158                  }
18159              }
18160              return $patterns;
18161          }
18162  
18163          /**
18164           * Returns text with soft hyphens.
18165           * @param string $text text to process
18166           * @param mixed $patterns Array of hypenation patterns or a TEX file containing hypenation patterns. TEX patterns can be downloaded from http://www.ctan.org/tex-archive/language/hyph-utf8/tex/generic/hyph-utf8/patterns/
18167           * @param array $dictionary Array of words to be returned without applying the hyphenation algoritm.
18168           * @param int $leftmin Minimum number of character to leave on the left of the word without applying the hyphens.
18169           * @param int $rightmin Minimum number of character to leave on the right of the word without applying the hyphens.
18170           * @param int $charmin Minimum word lenght to apply the hyphenation algoritm.
18171           * @param int $charmax Maximum lenght of broken piece of word.
18172           * @return array text with soft hyphens
18173           * @author Nicola Asuni
18174           * @since 4.9.012 (2010-04-12)
18175           * @access public
18176           */
18177  		public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
18178              global $unicode;
18179              $text = $this->unhtmlentities($text);
18180              $word = array(); // last word
18181              $txtarr = array(); // text to be returned
18182              $intag = false; // true if we are inside an HTML tag
18183              if (!is_array($patterns)) {
18184                  $patterns = $this->getHyphenPatternsFromTEX($patterns);
18185              }
18186              // get array of characters
18187              $unichars = $this->UTF8StringToArray($text);
18188              // for each char
18189              foreach ($unichars as $char) {
18190                  if ((!$intag) AND $unicode[$char] == 'L') {
18191                      // letter character
18192                      $word[] = $char;
18193                  } else {
18194                      // other type of character
18195                      if (!$this->empty_string($word)) {
18196                          // hypenate the word
18197                          $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
18198                          $word = array();
18199                      }
18200                      $txtarr[] = $char;
18201                      if (chr($char) == '<') {
18202                          // we are inside an HTML tag
18203                          $intag = true;
18204                      } elseif ($intag AND (chr($char) == '>')) {
18205                          // end of HTML tag
18206                          $intag = false;
18207                      }
18208                  }
18209              }
18210              if (!$this->empty_string($word)) {
18211                  // hypenate the word
18212                  $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
18213              }
18214              // convert char array to string and return
18215              return $this->UTF8ArrSubString($txtarr);
18216          }
18217  
18218          /**
18219           * Enable/disable rasterization of SVG images using ImageMagick library.
18220           * @param boolean $mode if true enable rasterization, false otherwise.
18221           * @access public
18222           * @since 5.0.000 (2010-04-27)
18223           */
18224  		public function setRasterizeVectorImages($mode) {
18225              $this->rasterize_vector_images = $mode;
18226          }
18227  
18228          /**
18229           * Get the Path-Painting Operators.
18230           * @param string $style Style of rendering. Possible values are:
18231           * <ul>
18232           *   <li>S or D: Stroke the path.</li>
18233           *   <li>s or d: Close and stroke the path.</li>
18234           *   <li>f or F: Fill the path, using the nonzero winding number rule to determine the region to fill.</li>
18235           *   <li>f* or F*: Fill the path, using the even-odd rule to determine the region to fill.</li>
18236           *   <li>B or FD or DF: Fill and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
18237           *   <li>B* or F*D or DF*: Fill and then stroke the path, using the even-odd rule to determine the region to fill.</li>
18238           *   <li>b or fd or df: Close, fill, and then stroke the path, using the nonzero winding number rule to determine the region to fill.</li>
18239           *   <li>b or f*d or df*: Close, fill, and then stroke the path, using the even-odd rule to determine the region to fill.</li>
18240           *   <li>CNZ: Clipping mode using the even-odd rule to determine which regions lie inside the clipping path.</li>
18241           *   <li>CEO: Clipping mode using the nonzero winding number rule to determine which regions lie inside the clipping path</li>
18242           *   <li>n: End the path object without filling or stroking it.</li>
18243           * </ul>
18244           * @param string $default default style
18245           * @param boolean $mode if true enable rasterization, false otherwise.
18246           * @author Nicola Asuni
18247           * @access protected
18248           * @since 5.0.000 (2010-04-30)
18249           */
18250  		protected function getPathPaintOperator($style, $default='S') {
18251              $op = '';
18252              switch($style) {
18253                  case 'S':
18254                  case 'D': {
18255                      $op = 'S';
18256                      break;
18257                  }
18258                  case 's':
18259                  case 'd': {
18260                      $op = 's';
18261                      break;
18262                  }
18263                  case 'f':
18264                  case 'F': {
18265                      $op = 'f';
18266                      break;
18267                  }
18268                  case 'f*':
18269                  case 'F*': {
18270                      $op = 'f*';
18271                      break;
18272                  }
18273                  case 'B':
18274                  case 'FD':
18275                  case 'DF': {
18276                      $op = 'B';
18277                      break;
18278                  }
18279                  case 'B*':
18280                  case 'F*D':
18281                  case 'DF*': {
18282                      $op = 'B*';
18283                      break;
18284                  }
18285                  case 'b':
18286                  case 'fd':
18287                  case 'df': {
18288                      $op = 'b';
18289                      break;
18290                  }
18291                  case 'b*':
18292                  case 'f*d':
18293                  case 'df*': {
18294                      $op = 'b*';
18295                      break;
18296                  }
18297                  case 'CNZ': {
18298                      $op = 'W n';
18299                      break;
18300                  }
18301                  case 'CEO': {
18302                      $op = 'W* n';
18303                      break;
18304                  }
18305                  case 'n': {
18306                      $op = 'n';
18307                      break;
18308                  }
18309                  default: {
18310                      if (!empty($default)) {
18311                          $op = $this->getPathPaintOperator($default, '');
18312                      } else {
18313                          $op = '';
18314                      }
18315                  }
18316              }
18317              return $op;
18318          }
18319  
18320          // -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
18321          // SVG METHODS
18322          // -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-
18323  
18324          /**
18325           * Embedd a Scalable Vector Graphics (SVG) image.
18326           * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of SVG images using ImageMagick library.
18327           * @param string $file Name of the SVG file.
18328           * @param float $x Abscissa of the upper-left corner.
18329           * @param float $y Ordinate of the upper-left corner.
18330           * @param float $w Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
18331           * @param float $h Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
18332           * @param mixed $link URL or identifier returned by AddLink().
18333           * @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>
18334           * @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>
18335           * @param mixed $border Indicates if borders must be drawn around the image. 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>
18336           * @param boolean $fitonpage if true the image is resized to not exceed page dimensions.
18337           * @author Nicola Asuni
18338           * @since 5.0.000 (2010-05-02)
18339           * @access public
18340           */
18341  		public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
18342              if ($this->rasterize_vector_images) {
18343                  // convert SVG to raster image using GD or ImageMagick libraries
18344                  return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
18345              }
18346              $this->svgdir = dirname($file);
18347              $svgdata = file_get_contents($file);
18348              if ($svgdata === false) {
18349                  $this->Error('SVG file not found: '.$file);
18350              }
18351              if ($x === '') {
18352                  $x = $this->x;
18353              }
18354              if ($y === '') {
18355                  $y = $this->y;
18356              }
18357              $k = $this->k;
18358              $ox = 0;
18359              $oy = 0;
18360              $ow = $w;
18361              $oh = $h;
18362              $aspect_ratio_align = 'xMidYMid';
18363              $aspect_ratio_ms = 'meet';
18364              $regs = array();
18365              // get original image width and height
18366              preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
18367              if (isset($regs[1]) AND !empty($regs[1])) {
18368                  $tmp = array();
18369                  if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
18370                      $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
18371                  }
18372                  $tmp = array();
18373                  if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
18374                      $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
18375                  }
18376                  $tmp = array();
18377                  if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
18378                      $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
18379                  }
18380                  $tmp = array();
18381                  if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
18382                      $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
18383                  }
18384                  $tmp = array();
18385                  $view_box = array();
18386                  if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
18387                      if (count($tmp) == 5) {
18388                          array_shift($tmp);
18389                          foreach ($tmp as $key => $val) {
18390                              $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
18391                          }
18392                          $ox = $view_box[0];
18393                          $oy = $view_box[1];
18394                      }
18395                      // get aspect ratio
18396                      $tmp = array();
18397                      if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
18398                          $aspect_ratio = preg_split("/[\s]+/si", $tmp[1]);
18399                          switch (count($aspect_ratio)) {
18400                              case 3: {
18401                                  $aspect_ratio_align = $aspect_ratio[1];
18402                                  $aspect_ratio_ms = $aspect_ratio[2];
18403                                  break;
18404                              }
18405                              case 2: {
18406                                  $aspect_ratio_align = $aspect_ratio[0];
18407                                  $aspect_ratio_ms = $aspect_ratio[1];
18408                                  break;
18409                              }
18410                              case 1: {
18411                                  $aspect_ratio_align = $aspect_ratio[0];
18412                                  $aspect_ratio_ms = 'meet';
18413                                  break;
18414                              }
18415                          }
18416                      }
18417                  }
18418              }
18419              // calculate image width and height on document
18420              if (($w <= 0) AND ($h <= 0)) {
18421                  // convert image size to document unit
18422                  $w = $ow;
18423                  $h = $oh;
18424              } elseif ($w <= 0) {
18425                  $w = $h * $ow / $oh;
18426              } elseif ($h <= 0) {
18427                  $h = $w * $oh / $ow;
18428              }
18429              // Check whether we need a new page first as this does not fit
18430              $prev_x = $this->x;
18431              if ($this->checkPageBreak($h, $y)) {
18432                  $y = $this->y;
18433                  if ($this->rtl) {
18434                      $x += ($prev_x - $this->x);
18435                  } else {
18436                      $x += ($this->x - $prev_x);
18437                  }
18438              }
18439              // resize image to be contained on a single page
18440              if ($fitonpage) {
18441                  $ratio_wh = $w / $h;
18442                  if (($y + $h) > $this->PageBreakTrigger) {
18443                      $h = $this->PageBreakTrigger - $y;
18444                      $w = $h * $ratio_wh;
18445                  }
18446                  if (($x + $w) > ($this->w - $this->rMargin)) {
18447                      $w = $this->w - $this->rMargin - $x;
18448                      $h = $w / $ratio_wh;
18449                  }
18450              }
18451              // set alignment
18452              $this->img_rb_y = $y + $h;
18453              // set alignment
18454              if ($this->rtl) {
18455                  if ($palign == 'L') {
18456                      $ximg = $this->lMargin;
18457                  } elseif ($palign == 'C') {
18458                      $ximg = ($this->w - $w) / 2;
18459                  } elseif ($palign == 'R') {
18460                      $ximg = $this->w - $this->rMargin - $w;
18461                  } else {
18462                      $ximg = $this->w - $x - $w;
18463                  }
18464                  $this->img_rb_x = $ximg;
18465              } else {
18466                  if ($palign == 'L') {
18467                      $ximg = $this->lMargin;
18468                  } elseif ($palign == 'C') {
18469                      $ximg = ($this->w - $w) / 2;
18470                  } elseif ($palign == 'R') {
18471                      $ximg = $this->w - $this->rMargin - $w;
18472                  } else {
18473                      $ximg = $x;
18474                  }
18475                  $this->img_rb_x = $ximg + $w;
18476              }
18477              // store current graphic vars
18478              $gvars = $this->getGraphicVars();
18479              // store SVG position and scale factors
18480              $svgoffset_x = ($ximg - $ox) * $this->k;
18481              $svgoffset_y = -($y - $oy) * $this->k;
18482              if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
18483                  $ow = $view_box[2];
18484                  $oh = $view_box[3];
18485              }
18486              $svgscale_x = $w / $ow;
18487              $svgscale_y = $h / $oh;
18488              // scaling and alignment
18489              if ($aspect_ratio_align != 'none') {
18490                  // store current scaling values
18491                  $svgscale_old_x = $svgscale_x;
18492                  $svgscale_old_y = $svgscale_y;
18493                  // force uniform scaling
18494                  if ($aspect_ratio_ms == 'slice') {
18495                      // the entire viewport is covered by the viewBox
18496                      if ($svgscale_x > $svgscale_y) {
18497                          $svgscale_y = $svgscale_x;
18498                      } elseif ($svgscale_x < $svgscale_y) {
18499                          $svgscale_x = $svgscale_y;
18500                      }
18501                  } else { // meet
18502                      // the entire viewBox is visible within the viewport
18503                      if ($svgscale_x < $svgscale_y) {
18504                          $svgscale_y = $svgscale_x;
18505                      } elseif ($svgscale_x > $svgscale_y) {
18506                          $svgscale_x = $svgscale_y;
18507                      }
18508                  }
18509                  // correct X alignment
18510                  switch (substr($aspect_ratio_align, 1, 3)) {
18511                      case 'Min': {
18512                          // do nothing
18513                          break;
18514                      }
18515                      case 'Max': {
18516                          $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
18517                          break;
18518                      }
18519                      default:
18520                      case 'Mid': {
18521                          $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
18522                          break;
18523                      }
18524                  }
18525                  // correct Y alignment
18526                  switch (substr($aspect_ratio_align, 5)) {
18527                      case 'Min': {
18528                          // do nothing
18529                          break;
18530                      }
18531                      case 'Max': {
18532                          $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
18533                          break;
18534                      }
18535                      default:
18536                      case 'Mid': {
18537                          $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
18538                          break;
18539                      }
18540                  }
18541              }
18542              // save the current graphic state
18543              $this->_out('q'.$this->epsmarker);
18544              // set initial clipping mask
18545              $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
18546              // scale and translate
18547              $e = $ox * $this->k * (1 - $svgscale_x);
18548              $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
18549              $this->_out(sprintf('%.3F %.3F %.3F %.3F %.3F %.3F cm', $svgscale_x, 0, 0, $svgscale_y, $e + $svgoffset_x, $f + $svgoffset_y));
18550              // creates a new XML parser to be used by the other XML functions
18551              $this->parser = xml_parser_create('UTF-8');
18552              // the following function allows to use parser inside object
18553              xml_set_object($this->parser, $this);
18554              // disable case-folding for this XML parser
18555              xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
18556              // sets the element handler functions for the XML parser
18557              xml_set_element_handler($this->parser, 'startSVGElementHandler', 'endSVGElementHandler');
18558              // sets the character data handler function for the XML parser
18559              xml_set_character_data_handler($this->parser, 'segSVGContentHandler');
18560              // start parsing an XML document
18561              if(!xml_parse($this->parser, $svgdata)) {
18562                  $error_message = sprintf("SVG Error: %s at line %d", xml_error_string(xml_get_error_code($this->parser)), xml_get_current_line_number($this->parser));
18563                  $this->Error($error_message);
18564              }
18565              // free this XML parser
18566              xml_parser_free($this->parser);
18567              // restore previous graphic state
18568              $this->_out($this->epsmarker.'Q');
18569              // restore  graphic vars
18570              $this->setGraphicVars($gvars);
18571              if (!empty($border)) {
18572                  $bx = $x;
18573                  $by = $y;
18574                  $this->x = $ximg;
18575                  if ($this->rtl) {
18576                      $this->x += $w;
18577                  }
18578                  $this->y = $y;
18579                  $this->Cell($w, $h, '', $border, 0, '', 0, '', 0);
18580                  $this->x = $bx;
18581                  $this->y = $by;
18582              }
18583              if ($link) {
18584                  $this->Link($ximg, $y, $w, $h, $link, 0);
18585              }
18586              // set pointer to align the successive text/objects
18587              switch($align) {
18588                  case 'T':{
18589                      $this->y = $y;
18590                      $this->x = $this->img_rb_x;
18591                      break;
18592                  }
18593                  case 'M':{
18594                      $this->y = $y + round($h/2);
18595                      $this->x = $this->img_rb_x;
18596                      break;
18597                  }
18598                  case 'B':{
18599                      $this->y = $this->img_rb_y;
18600                      $this->x = $this->img_rb_x;
18601                      break;
18602                  }
18603                  case 'N':{
18604                      $this->SetY($this->img_rb_y);
18605                      break;
18606                  }
18607                  default:{
18608                      break;
18609                  }
18610              }
18611              $this->endlinex = $this->img_rb_x;
18612          }
18613  
18614          /**
18615           * Get the tranformation matrix from SVG transform attribute
18616           * @param string transformation
18617           * @return array of transformations
18618           * @author Nicola Asuni
18619           * @since 5.0.000 (2010-05-02)
18620           * @access protected
18621           */
18622  		protected function getSVGTransformMatrix($attribute) {
18623              // identity matrix
18624              $tm = array(1, 0, 0, 1, 0, 0);
18625              $continue = true;
18626              while ($continue) {
18627                  $continue = false;
18628                  // matrix
18629                  $regs = array();
18630                  if (preg_match('/matrix\(([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18631                      $attribute = str_replace($regs[0], '', $attribute);
18632                      $continue = true;
18633                      $a = $regs[1];
18634                      $b = $regs[2];
18635                      $c = $regs[3];
18636                      $d = $regs[4];
18637                      $e = $regs[5];
18638                      $f = $regs[6];
18639                      $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
18640                  }
18641                  // translate x
18642                  $regs = array();
18643                  if (preg_match('/translate\(([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18644                      $attribute = str_replace($regs[0], 'translate('.$regs[1].',0)', $attribute);
18645                      $continue = true;
18646                  }
18647                  // translate x,y
18648                  $regs = array();
18649                  if (preg_match('/translate\(([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18650                      $attribute = str_replace($regs[0], '', $attribute);
18651                      $continue = true;
18652                      $e = $regs[1];
18653                      $f = $regs[2];
18654                      $tm = $this->getTransformationMatrixProduct($tm, array(1, 0, 0, 1, $e, $f));
18655                  }
18656                  // scale x
18657                  $regs = array();
18658                  if (preg_match('/scale\(([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18659                      $attribute = str_replace($regs[0], 'scale('.$regs[1].','.$regs[1].')', $attribute);
18660                      $continue = true;
18661                  }
18662                  // scale x,y
18663                  $regs = array();
18664                  if (preg_match('/scale\(([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18665                      $attribute = str_replace($regs[0], '', $attribute);
18666                      $continue = true;
18667                      $a = $regs[1];
18668                      if (isset($regs[2]) AND (strlen(trim($regs[2])) > 0)) {
18669                          $d = $regs[2];
18670                      } else {
18671                          $d = $a;
18672                      }
18673                      $tm = $this->getTransformationMatrixProduct($tm, array($a, 0, 0, $d, 0, 0));
18674                  }
18675                  // rotate ang
18676                  $regs = array();
18677                  if (preg_match('/rotate\(([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18678                      $attribute = str_replace($regs[0], 'rotate('.$regs[1].',0,0)', $attribute);
18679                      $continue = true;
18680                  }
18681                  // rotate ang,x,y
18682                  $regs = array();
18683                  if (preg_match('/rotate\(([0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)[\,\s]+([a-z0-9\-\.]+)\)/si', $attribute, $regs)) {
18684                      $attribute = str_replace($regs[0], '', $attribute);
18685                      $continue = true;
18686                      $ang = deg2rad($regs[1]);
18687                      $a = cos($ang);
18688                      $b = sin($ang);
18689                      $c = -$b;
18690                      $d = $a;
18691                      $x = $regs[2];
18692                      $y = $regs[3];
18693                      $e = ($x * (1 - $a)) - ($y * $c);
18694                      $f = ($y * (1 - $d)) - ($x * $b);
18695                      $tm = $this->getTransformationMatrixProduct($tm, array($a, $b, $c, $d, $e, $f));
18696                  }
18697                  // skewX
18698                  $regs = array();
18699                  if (preg_match('/skewX\(([0-9\-\.]+)\)/si', $attribute, $regs)) {
18700                      $attribute = str_replace($regs[0], '', $attribute);
18701                      $continue = true;
18702                      $c = tan(deg2rad($regs[1]));
18703                      $tm = $this->getTransformationMatrixProduct($tm, array(1, 0, $c, 1, 0, 0));
18704                  }
18705                  // skewY
18706                  $regs = array();
18707                  if (preg_match('/skewY\(([0-9\-\.]+)\)/si', $attribute, $regs)) {
18708                      $attribute = str_replace($regs[0], '', $attribute);
18709                      $continue = true;
18710                      $b = tan(deg2rad($regs[1]));
18711                      $tm = $this->getTransformationMatrixProduct($tm, array(1, $b, 0, 1, 0, 0));
18712                  }
18713              }
18714              return $tm;
18715          }
18716  
18717          /**
18718           * Get the product of two SVG tranformation matrices
18719           * @param array $ta first SVG tranformation matrix
18720           * @param array $tb second SVG tranformation matrix
18721           * @return transformation array
18722           * @author Nicola Asuni
18723           * @since 5.0.000 (2010-05-02)
18724           * @access protected
18725           */
18726  		protected function getTransformationMatrixProduct($ta, $tb)  {
18727              $tm = array();
18728              $tm[0] = ($ta[0] * $tb[0]) + ($ta[2] * $tb[1]);
18729              $tm[1] = ($ta[1] * $tb[0]) + ($ta[3] * $tb[1]);
18730              $tm[2] = ($ta[0] * $tb[2]) + ($ta[2] * $tb[3]);
18731              $tm[3] = ($ta[1] * $tb[2]) + ($ta[3] * $tb[3]);
18732              $tm[4] = ($ta[0] * $tb[4]) + ($ta[2] * $tb[5]) + $ta[4];
18733              $tm[5] = ($ta[1] * $tb[4]) + ($ta[3] * $tb[5]) + $ta[5];
18734              return $tm;
18735          }
18736  
18737          /**
18738           * Convert SVG transformation matrix to PDF.
18739           * @param array $tm original SVG transformation matrix
18740           * @return array transformation matrix
18741           * @access protected
18742           * @since 5.0.000 (2010-05-02)
18743           */
18744  		protected function convertSVGtMatrix($tm) {
18745              $a = $tm[0];
18746              $b = -$tm[1];
18747              $c = -$tm[2];
18748              $d = $tm[3];
18749              $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
18750              $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
18751              $x = 0;
18752              $y = $this->h * $this->k;
18753              $e = ($x * (1 - $a)) - ($y * $c) + $e;
18754              $f = ($y * (1 - $d)) - ($x * $b) + $f;
18755              return array($a, $b, $c, $d, $e, $f);
18756          }
18757  
18758          /**
18759           * Apply SVG graphic transformation matrix.
18760           * @param array $tm original SVG transformation matrix
18761           * @access protected
18762           * @since 5.0.000 (2010-05-02)
18763           */
18764  		protected function SVGTransform($tm) {
18765              $this->Transform($this->convertSVGtMatrix($tm));
18766          }
18767  
18768          /**
18769           * Apply the requested SVG styles (*** TO BE COMPLETED ***)
18770           * @param array $svgstyle array of SVG styles to apply
18771           * @param array $prevsvgstyle array of previous SVG style
18772           * @param int $x X origin of the bounding box
18773           * @param int $y Y origin of the bounding box
18774           * @param int $w width of the bounding box
18775           * @param int $h height of the bounding box
18776           * @param string $clip_function clip function
18777           * @param array $clip_params array of parameters for clipping function
18778           * @return object style
18779           * @author Nicola Asuni
18780           * @since 5.0.000 (2010-05-02)
18781           * @access protected
18782           */
18783  		protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array())  {
18784              $objstyle = '';
18785              if(!isset($svgstyle['opacity'])) {
18786                  return $objstyle;
18787              }
18788              // clip-path
18789              $regs = array();
18790              if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
18791                  $clip_path = $this->svgclippaths[$regs[1]];
18792                  foreach ($clip_path as $cp) {
18793                      $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs']);
18794                  }
18795              }
18796              //$this->SetAlpha(1); // reset alpha
18797              // opacity
18798              if ($svgstyle['opacity'] != 1) {
18799                  $this->SetAlpha($svgstyle['opacity']);
18800              }
18801              // color
18802              $fill_color = $this->convertHTMLColorToDec($svgstyle['color']);
18803              $this->SetFillColorArray($fill_color);
18804              // text color
18805              $text_color = $this->convertHTMLColorToDec($svgstyle['text-color']);
18806              $this->SetTextColorArray($text_color);
18807              // clip
18808              if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
18809                  $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
18810                  $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
18811                  $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
18812                  $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
18813                  $cx = $x + $left;
18814                  $cy = $y + $top;
18815                  $cw = $w - $left - $right;
18816                  $ch = $h - $top - $bottom;
18817                  if ($svgstyle['clip-rule'] == 'evenodd') {
18818                      $clip_rule = 'CNZ';
18819                  } else {
18820                      $clip_rule = 'CEO';
18821                  }
18822                  $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
18823              }
18824              // fill
18825              $regs = array();
18826              if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
18827                  // gradient
18828                  $gradient = $this->svggradients[$regs[1]];
18829                  if (isset($gradient['xref'])) {
18830                      // reference to another gradient definition
18831                      $newgradient = $this->svggradients[$gradient['xref']];
18832                      $newgradient['coords'] = $gradient['coords'];
18833                      $newgradient['mode'] = $gradient['mode'];
18834                      $newgradient['gradientUnits'] = $gradient['gradientUnits'];
18835                      if (isset($gradient['gradientTransform'])) {
18836                          $newgradient['gradientTransform'] = $gradient['gradientTransform'];
18837                      }
18838                      $gradient = $newgradient;
18839                  }
18840                  //save current Graphic State
18841                  $this->_out('q');
18842                  //set clipping area
18843                  if (!empty($clip_function) AND method_exists($this, $clip_function)) {
18844                      $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
18845                      if (is_array($bbox) AND (count($bbox) == 4)) {
18846                          list($x, $y, $w, $h) = $bbox;
18847                      }
18848                  }
18849                  if ($gradient['mode'] == 'measure') {
18850                      if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
18851                          $gtm = $gradient['gradientTransform'];
18852                          // apply transformation matrix
18853                          $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
18854                          $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
18855                          $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
18856                          $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
18857                          if (isset($gradient['coords'][4])) {
18858                              $gradient['coords'][4] = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
18859                          }
18860                          $gradient['coords'][0] = $xa;
18861                          $gradient['coords'][1] = $ya;
18862                          $gradient['coords'][2] = $xb;
18863                          $gradient['coords'][3] = $yb;
18864  
18865                      }
18866                      // convert SVG coordinates to user units
18867                      $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
18868                      $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
18869                      $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
18870                      $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
18871                      if (isset($gradient['coords'][4])) {
18872                          $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
18873                      }
18874                      // shift units
18875                      if ($gradient['gradientUnits'] == 'objectBoundingBox') {
18876                          // convert to SVG coordinate system
18877                          $gradient['coords'][0] += $x;
18878                          $gradient['coords'][1] += $y;
18879                          $gradient['coords'][2] += $x;
18880                          $gradient['coords'][3] += $y;
18881                      }
18882                      // calculate percentages
18883                      $gradient['coords'][0] = ($gradient['coords'][0] - $x) / $w;
18884                      $gradient['coords'][1] = ($gradient['coords'][1] - $y) / $h;
18885                      $gradient['coords'][2] = ($gradient['coords'][2] - $x) / $w;
18886                      $gradient['coords'][3] = ($gradient['coords'][3] - $y) / $h;
18887                      if (isset($gradient['coords'][4])) {
18888                          $gradient['coords'][4] /= $w;
18889                      }
18890                      // fix values
18891                      foreach($gradient['coords'] as $key => $val) {
18892                          if ($val < 0) {
18893                              $gradient['coords'][$key] = 0;
18894                          } elseif ($val > 1) {
18895                              $gradient['coords'][$key] = 1;
18896                          }
18897                      }
18898                      if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
18899                          // single color (no shading)
18900                          $gradient['coords'][0] = 1;
18901                          $gradient['coords'][1] = 0;
18902                          $gradient['coords'][2] = 0.999;
18903                          $gradient['coords'][3] = 0;
18904                      }
18905                  }
18906                  // swap Y coordinates
18907                  $tmp = $gradient['coords'][1];
18908                  $gradient['coords'][1] = $gradient['coords'][3];
18909                  $gradient['coords'][3] = $tmp;
18910                  // set transformation map for gradient
18911                  if (($gradient['type'] == 3) AND ($gradient['mode'] == 'measure')) {
18912                      // gradient is always circular
18913                      $cy = $this->h - $y - ($gradient['coords'][1] * ($w + $h));
18914                      $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $w*$this->k, $x*$this->k, $cy*$this->k));
18915                  } else {
18916                      $this->_out(sprintf('%.3F 0 0 %.3F %.3F %.3F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k));
18917                  }
18918                  if (count($gradient['stops']) > 1) {
18919                      $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
18920                  }
18921              } elseif ($svgstyle['fill'] != 'none') {
18922                  $fill_color = $this->convertHTMLColorToDec($svgstyle['fill']);
18923                  if ($svgstyle['fill-opacity'] != 1) {
18924                      $this->SetAlpha($svgstyle['fill-opacity']);
18925                  }
18926                  $this->SetFillColorArray($fill_color);
18927                  if ($svgstyle['fill-rule'] == 'evenodd') {
18928                      $objstyle .= 'F*';
18929                  } else {
18930                      $objstyle .= 'F';
18931                  }
18932              }
18933              // stroke
18934              if ($svgstyle['stroke'] != 'none') {
18935                  $stroke_style = array(
18936                      'color' => $this->convertHTMLColorToDec($svgstyle['stroke']),
18937                      'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
18938                      'cap' => $svgstyle['stroke-linecap'],
18939                      'join' => $svgstyle['stroke-linejoin']
18940                      );
18941                  if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
18942                      $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
18943                  }
18944                  $this->SetLineStyle($stroke_style);
18945                  $objstyle .= 'D';
18946              }
18947              // font
18948              $regs = array();
18949              if (!empty($svgstyle['font'])) {
18950                  if (preg_match('/font-family[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
18951                      $font_family = trim($regs[1]);
18952                  } else {
18953                      $font_family = $svgstyle['font-family'];
18954                  }
18955                  if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
18956                      $font_size = trim($regs[1]);
18957                  } else {
18958                      $font_size = $svgstyle['font-size'];
18959                  }
18960                  if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
18961                      $font_style = trim($regs[1]);
18962                  } else {
18963                      $font_style = $svgstyle['font-style'];
18964                  }
18965                  if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
18966                      $font_weight = trim($regs[1]);
18967                  } else {
18968                      $font_weight = $svgstyle['font-weight'];
18969                  }
18970              } else {
18971                  $font_family = $svgstyle['font-family'];
18972                  $font_size = $svgstyle['font-size'];
18973                  $font_style = $svgstyle['font-style'];
18974                  $font_weight = $svgstyle['font-weight'];
18975              }
18976              $font_size = $this->getHTMLUnitToUnits($font_size, $prevsvgstyle['font-size'], $this->svgunit, false) * $this->k;
18977              switch ($font_style) {
18978                  case 'italic': {
18979                      $font_style = 'I';
18980                      break;
18981                  }
18982                  case 'oblique': {
18983                      $font_style = 'I';
18984                      break;
18985                  }
18986                  default:
18987                  case 'normal': {
18988                      $font_style = '';
18989                      break;
18990                  }
18991              }
18992              switch ($font_weight) {
18993                  case 'bold':
18994                  case 'bolder': {
18995                      $font_style .= 'B';
18996                      break;
18997                  }
18998              }
18999              switch ($svgstyle['text-decoration']) {
19000                  case 'underline': {
19001                      $font_style .= 'U';
19002                      break;
19003                  }
19004                  case 'overline': {
19005                      $font_style .= 'O';
19006                      break;
19007                  }
19008                  case 'line-through': {
19009                      $font_style .= 'D';
19010                      break;
19011                  }
19012                  default:
19013                  case 'none': {
19014                      break;
19015                  }
19016              }
19017              $this->SetFont($font_family, $font_style, $font_size);
19018              return $objstyle;
19019          }
19020  
19021          /**
19022           * Draws an SVG path
19023           * @param string $d attribute d of the path SVG element
19024           * @param string $style Style of rendering. Possible values are:
19025           * <ul>
19026           *     <li>D or empty string: Draw (default).</li>
19027           *     <li>F: Fill.</li>
19028           *     <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
19029           *     <li>DF or FD: Draw and fill.</li>
19030           *     <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
19031           *     <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
19032           *     <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
19033           * </ul>
19034           * @return array of container box measures (x, y, w, h)
19035           * @author Nicola Asuni
19036           * @since 5.0.000 (2010-05-02)
19037           * @access protected
19038           */
19039  		protected function SVGPath($d, $style='') {
19040              // set fill/stroke style
19041              $op = $this->getPathPaintOperator($style, '');
19042              if (empty($op)) {
19043                  return;
19044              }
19045              $paths = array();
19046              preg_match_all('/([a-zA-Z])[\s]*([^a-zA-Z\"]*)/si', $d, $paths, PREG_SET_ORDER);
19047              $x = 0;
19048              $y = 0;
19049              $x1 = 0;
19050              $y1 = 0;
19051              $x2 = 0;
19052              $y2 = 0;
19053              $xmin = 2147483647;
19054              $xmax = 0;
19055              $ymin = 2147483647;
19056              $ymax = 0;
19057              $relcoord = false;
19058              // draw curve pieces
19059              foreach ($paths as $key => $val) {
19060                  // get curve type
19061                  $cmd = trim($val[1]);
19062                  if (strtolower($cmd) == $cmd) {
19063                      // use relative coordinated instead of absolute
19064                      $relcoord = true;
19065                      $xoffset = $x;
19066                      $yoffset = $y;
19067                  } else {
19068                      $relcoord = false;
19069                      $xoffset = 0;
19070                      $yoffset = 0;
19071                  }
19072                  $params = array();
19073                  if (isset($val[2])) {
19074                      // get curve parameters
19075                      $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
19076                      $params = array();
19077                      foreach ($rawparams as $ck => $cp) {
19078                          $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
19079                      }
19080                  }
19081                  switch (strtoupper($cmd)) {
19082                      case 'M': { // moveto
19083                          foreach ($params as $ck => $cp) {
19084                              if (($ck % 2) == 0) {
19085                                  $x = $cp + $xoffset;
19086                              } else {
19087                                  $y = $cp + $yoffset;
19088                                  if ($ck == 1) {
19089                                      $this->_outPoint($x, $y);
19090                                  } else {
19091                                      $this->_outLine($x, $y);
19092                                  }
19093                                  $xmin = min($xmin, $x);
19094                                  $ymin = min($ymin, $y);
19095                                  $xmax = max($xmax, $x);
19096                                  $ymax = max($ymax, $y);
19097                                  if ($relcoord) {
19098                                      $xoffset = $x;
19099                                      $yoffset = $y;
19100                                  }
19101                              }
19102                          }
19103                          break;
19104                      }
19105                      case 'L': { // lineto
19106                          foreach ($params as $ck => $cp) {
19107                              if (($ck % 2) == 0) {
19108                                  $x = $cp + $xoffset;
19109                              } else {
19110                                  $y = $cp + $yoffset;
19111                                  $this->_outLine($x, $y);
19112                                  $xmin = min($xmin, $x);
19113                                  $ymin = min($ymin, $y);
19114                                  $xmax = max($xmax, $x);
19115                                  $ymax = max($ymax, $y);
19116                                  if ($relcoord) {
19117                                      $xoffset = $x;
19118                                      $yoffset = $y;
19119                                  }
19120                              }
19121                          }
19122                          break;
19123                      }
19124                      case 'H': { // horizontal lineto
19125                          foreach ($params as $ck => $cp) {
19126                              $x = $cp + $xoffset;
19127                              $this->_outLine($x, $y);
19128                              $xmin = min($xmin, $x);
19129                              $xmax = max($xmax, $x);
19130                              if ($relcoord) {
19131                                  $xoffset = $x;
19132                              }
19133                          }
19134                          break;
19135                      }
19136                      case 'V': { // vertical lineto
19137                          foreach ($params as $ck => $cp) {
19138                              $y = $cp + $yoffset;
19139                              $this->_outLine($x, $y);
19140                              $ymin = min($ymin, $y);
19141                              $ymax = max($ymax, $y);
19142                              if ($relcoord) {
19143                                  $yoffset = $y;
19144                              }
19145                          }
19146                          break;
19147                      }
19148                      case 'C': { // curveto
19149                          foreach ($params as $ck => $cp) {
19150                              $params[$ck] = $cp;
19151                              if ((($ck + 1) % 6) == 0) {
19152                                  $x1 = $params[($ck - 5)] + $xoffset;
19153                                  $y1 = $params[($ck - 4)] + $yoffset;
19154                                  $x2 = $params[($ck - 3)] + $xoffset;
19155                                  $y2 = $params[($ck - 2)] + $yoffset;
19156                                  $x = $params[($ck - 1)] + $xoffset;
19157                                  $y = $params[($ck)] + $yoffset;
19158                                  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
19159                                  $xmin = min($xmin, $x, $x1, $x2);
19160                                  $ymin = min($ymin, $y, $y1, $y2);
19161                                  $xmax = max($xmax, $x, $x1, $x2);
19162                                  $ymax = max($ymax, $y, $y1, $y2);
19163                                  if ($relcoord) {
19164                                      $xoffset = $x;
19165                                      $yoffset = $y;
19166                                  }
19167                              }
19168                          }
19169                          break;
19170                      }
19171                      case 'S': { // shorthand/smooth curveto
19172                          foreach ($params as $ck => $cp) {
19173                              $params[$ck] = $cp;
19174                              if ((($ck + 1) % 4) == 0) {
19175                                  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
19176                                      $x1 = (2 * $x) - $x2;
19177                                      $y1 = (2 * $y) - $y2;
19178                                  } else {
19179                                      $x1 = $x;
19180                                      $y1 = $y;
19181                                  }
19182                                  $x2 = $params[($ck - 3)] + $xoffset;
19183                                  $y2 = $params[($ck - 2)] + $yoffset;
19184                                  $x = $params[($ck - 1)] + $xoffset;
19185                                  $y = $params[($ck)] + $yoffset;
19186                                  $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
19187                                  $xmin = min($xmin, $x, $x1, $x2);
19188                                  $ymin = min($ymin, $y, $y1, $y2);
19189                                  $xmax = max($xmax, $x, $x1, $x2);
19190                                  $ymax = max($ymax, $y, $y1, $y2);
19191                                  if ($relcoord) {
19192                                      $xoffset = $x;
19193                                      $yoffset = $y;
19194                                  }
19195                              }
19196                          }
19197                          break;
19198                      }
19199                      case 'Q': { // quadratic Bézier curveto
19200                          foreach ($params as $ck => $cp) {
19201                              $params[$ck] = $cp;
19202                              if ((($ck + 1) % 4) == 0) {
19203                                  // convert quadratic points to cubic points
19204                                  $x1 = $params[($ck - 3)] + $xoffset;
19205                                  $y1 = $params[($ck - 2)] + $yoffset;
19206                                  $xa = ($x + (2 * $x1)) / 3;
19207                                  $ya = ($y + (2 * $y1)) / 3;
19208                                  $x = $params[($ck - 1)] + $xoffset;
19209                                  $y = $params[($ck)] + $yoffset;
19210                                  $xb = ($x + (2 * $x1)) / 3;
19211                                  $yb = ($y + (2 * $y1)) / 3;
19212                                  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
19213                                  $xmin = min($xmin, $x, $xa, $xb);
19214                                  $ymin = min($ymin, $y, $ya, $yb);
19215                                  $xmax = max($xmax, $x, $xa, $xb);
19216                                  $ymax = max($ymax, $y, $ya, $yb);
19217                                  if ($relcoord) {
19218                                      $xoffset = $x;
19219                                      $yoffset = $y;
19220                                  }
19221                              }
19222                          }
19223                          break;
19224                      }
19225                      case 'T': { // shorthand/smooth quadratic Bézier curveto
19226                          foreach ($params as $ck => $cp) {
19227                              $params[$ck] = $cp;
19228                              if (($ck % 2) != 0) {
19229                                  if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
19230                                      $x1 = (2 * $x) - $x1;
19231                                      $y1 = (2 * $y) - $y1;
19232                                  } else {
19233                                      $x1 = $x;
19234                                      $y1 = $y;
19235                                  }
19236                                  // convert quadratic points to cubic points
19237                                  $xa = ($x + (2 * $x1)) / 3;
19238                                  $ya = ($y + (2 * $y1)) / 3;
19239                                  $x = $params[($ck - 1)] + $xoffset;
19240                                  $y = $params[($ck)] + $yoffset;
19241                                  $xb = ($x + (2 * $x1)) / 3;
19242                                  $yb = ($y + (2 * $y1)) / 3;
19243                                  $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
19244                                  $xmin = min($xmin, $x, $x1, $x2);
19245                                  $ymin = min($ymin, $y, $y1, $y2);
19246                                  $xmax = max($xmax, $x, $x1, $x2);
19247                                  $ymax = max($ymax, $y, $y1, $y2);
19248                                  if ($relcoord) {
19249                                      $xoffset = $x;
19250                                      $yoffset = $y;
19251                                  }
19252                              }
19253                          }
19254                          break;
19255                      }
19256                      case 'A': { // elliptical arc
19257                          foreach ($params as $ck => $cp) {
19258                              $params[$ck] = $cp;
19259                              if ((($ck + 1) % 7) == 0) {
19260                                  $x0 = $x;
19261                                  $y0 = $y;
19262                                  $rx = abs($params[($ck - 6)]);
19263                                  $ry = abs($params[($ck - 5)]);
19264                                  $ang = -$rawparams[($ck - 4)];
19265                                  $angle = deg2rad($ang);
19266                                  $fa = $rawparams[($ck - 3)]; // large-arc-flag
19267                                  $fs = $rawparams[($ck - 2)]; // sweep-flag
19268                                  $x = $params[($ck - 1)] + $xoffset;
19269                                  $y = $params[$ck] + $yoffset;
19270                                  $cos_ang = cos($angle);
19271                                  $sin_ang = sin($angle);
19272                                  $a = ($x0 - $x) / 2;
19273                                  $b = ($y0 - $y) / 2;
19274                                  $xa = ($a * $cos_ang) - ($b * $sin_ang);
19275                                  $ya = ($a * $sin_ang) + ($b * $cos_ang);
19276                                  $rx2 = $rx * $rx;
19277                                  $ry2 = $ry * $ry;
19278                                  $xa2 = $xa * $xa;
19279                                  $ya2 = $ya * $ya;
19280                                  $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
19281                                  if ($delta > 1) {
19282                                      $rx *= sqrt($delta);
19283                                      $ry *= sqrt($delta);
19284                                      $rx2 = $rx * $rx;
19285                                      $ry2 = $ry * $ry;
19286                                  }
19287                                  $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
19288                                  if ($numerator < 0) {
19289                                      $root = 0;
19290                                  } else {
19291                                      $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
19292                                  }
19293                                  if ($fa == $fs) {
19294                                      $root *= -1;
19295                                  }
19296                                  $cax = $root * (($rx * $ya) / $ry);
19297                                  $cay = -$root * (($ry * $xa) / $rx);
19298                                  // coordinates of ellipse center
19299                                  $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
19300                                  $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
19301                                  // get angles
19302                                  $angs = $this->getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
19303                                  $dang = $this->getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
19304                                  if (($fs == 0) AND ($dang > 0)) {
19305                                      $dang -= (2 * M_PI);
19306                                  } elseif (($fs == 1) AND ($dang < 0)) {
19307                                      $dang += (2 * M_PI);
19308                                  }
19309                                  $angf = $angs - $dang;
19310                                  if (($fs == 1) AND ($angs > $angf)) {
19311                                      $tmp = $angs;
19312                                      $angs = $angf;
19313                                      $angf = $tmp;
19314                                  }
19315                                  $angs = rad2deg($angs);
19316                                  $angf = rad2deg($angf);
19317                                  $pie = false;
19318                                  if ((isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
19319                                      $pie = true;
19320                                  }
19321                                  $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2);
19322                                  $this->_outPoint($x, $y);
19323                                  $xmin = min($xmin, $x);
19324                                  $ymin = min($ymin, $y);
19325                                  $xmax = max($xmax, $x);
19326                                  $ymax = max($ymax, $y);
19327                                  if ($relcoord) {
19328                                      $xoffset = $x;
19329                                      $yoffset = $y;
19330                                  }
19331                              }
19332                          }
19333                          break;
19334                      }
19335                      case 'Z': {
19336                          $this->_out('h');
19337                          break;
19338                      }
19339                  }
19340              } // end foreach
19341              if (!empty($op)) {
19342                  $this->_out($op);
19343              }
19344              return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
19345          }
19346  
19347          /**
19348           * Returns the angle in  radiants between two vectors
19349           * @param int $x1 X coordiante of first vector point
19350           * @param int $y1 Y coordiante of first vector point
19351           * @param int $x2 X coordiante of second vector point
19352           * @param int $y2 Y coordiante of second vector point
19353           * @author Nicola Asuni
19354           * @since 5.0.000 (2010-05-04)
19355           * @access protected
19356           */
19357  		protected function getVectorsAngle($x1, $y1, $x2, $y2) {
19358              $dprod = ($x1 * $x2) + ($y1 * $y2);
19359              $dist1 = sqrt(($x1 * $x1) + ($y1 * $y1));
19360              $dist2 = sqrt(($x2 * $x2) + ($y2 * $y2));
19361              $angle = acos($dprod / ($dist1 * $dist2));
19362              if (is_nan($angle)) {
19363                  $angle = M_PI;
19364              }
19365              if ((($x1 * $y2) - ($x2 * $y1)) < 0) {
19366                  $angle *= -1;
19367              }
19368              return $angle;
19369          }
19370  
19371          /**
19372           * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
19373           * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
19374           * @param string $name The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. 
19375           * @param array $attribs The third parameter, attribs, contains an associative array with the element's attributes (if any). The keys of this array are the attribute names, the values are the attribute values. Attribute names are case-folded on the same criteria as element names. Attribute values are not case-folded. The original order of the attributes can be retrieved by walking through attribs the normal way, using each(). The first key in the array was the first attribute, and so on. 
19376           * @author Nicola Asuni
19377           * @since 5.0.000 (2010-05-02)
19378           * @access protected
19379           */
19380  		protected function startSVGElementHandler($parser, $name, $attribs) {
19381              // check if we are in clip mode
19382              if ($this->svgclipmode) {
19383                  $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs);
19384                  return;
19385              }
19386              if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
19387                  $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
19388                  return;
19389              }
19390              $clipping = false;
19391              if ($parser == 'clip-path') {
19392                  // set clipping mode
19393                  $clipping = true;
19394              }
19395              // get styling properties
19396              $prev_svgstyle = $this->svgstyles[(count($this->svgstyles) - 1)]; // previous style
19397              $svgstyle = array(); // current style
19398              if (isset($attribs['style'])) {
19399                  $attribs['style'] = ';'.$attribs['style'];
19400              }
19401              foreach ($prev_svgstyle as $key => $val) {
19402                  if (isset($attribs[$key])) {
19403                      if ($attribs[$key] == 'inherit') {
19404                          $svgstyle[$key] = $val;
19405                      } else {
19406                          $svgstyle[$key] = $attribs[$key];
19407                      }
19408                  } elseif (isset($attribs['style'])) {
19409                      // CSS style syntax
19410                      $attrval = array();
19411                      if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"\s]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
19412                          if ($attrval[1] == 'inherit') {
19413                              $svgstyle[$key] = $val;
19414                          } else {
19415                              $svgstyle[$key] = $attrval[1];
19416                          }
19417                      } else {
19418                          // default value
19419                          $svgstyle[$key] = $this->svgstyles[0][$key];
19420                      }
19421                  } else {
19422                      if (in_array($key, $this->svginheritprop)) {
19423                          // inherit previous value
19424                          $svgstyle[$key] = $val;
19425                      } else {
19426                          // default value
19427                          $svgstyle[$key] = $this->svgstyles[0][$key];
19428                      }
19429                  }
19430              }
19431              // transformation matrix
19432              $tm = $this->svgstyles[(count($this->svgstyles) - 1)]['transfmatrix'];
19433              if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
19434                  $tm = $this->getTransformationMatrixProduct($tm, $this->getSVGTransformMatrix($attribs['transform']));
19435              }
19436              $svgstyle['transfmatrix'] = $tm;
19437              // process tag
19438              switch($name) {
19439                  case 'defs': {
19440                      $this->svgdefsmode = true;
19441                      break;
19442                  }
19443                  // clipPath
19444                  case 'clipPath': {
19445                      $this->svgclipmode = true;
19446                      $this->svgclipid = $attribs['id'];
19447                      $this->svgclippaths[$this->svgclipid] = array();
19448                      break;
19449                  }
19450                  case 'svg': {
19451                      // start of SVG object
19452                      break;
19453                  }
19454                  case 'g': {
19455                      // group together related graphics elements
19456                      array_push($this->svgstyles, $svgstyle);
19457                      $this->StartTransform();
19458                      $this->setSVGStyles($svgstyle, $prev_svgstyle);
19459                      break;
19460                  }
19461                  case 'linearGradient': {
19462                      $this->svggradientid = $attribs['id'];
19463                      $this->svggradients[$this->svggradientid] = array();
19464                      $this->svggradients[$this->svggradientid]['type'] = 2;
19465                      $this->svggradients[$this->svggradientid]['stops'] = array();
19466                      if (isset($attribs['gradientUnits'])) {
19467                          $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
19468                      } else {
19469                          $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
19470                      }
19471                      //$attribs['spreadMethod']
19472                      $x1 = (isset($attribs['x1'])?$attribs['x1']:0);
19473                      $y1 = (isset($attribs['y1'])?$attribs['y1']:0);
19474                      $x2 = (isset($attribs['x2'])?$attribs['x2']:1);
19475                      $y2 = (isset($attribs['y2'])?$attribs['y2']:0);
19476                      if (isset($attribs['x1']) AND (substr($attribs['x1'], -1) != '%')) {
19477                          $this->svggradients[$this->svggradientid]['mode'] = 'measure';
19478                      } else {
19479                          $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
19480                      }
19481                      if (isset($attribs['gradientTransform'])) {
19482                          $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
19483                      }
19484                      $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
19485                      if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
19486                          // gradient is defined on another place
19487                          $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
19488                      }
19489                      break;
19490                  }
19491                  case 'radialGradient': {
19492                      $this->svggradientid = $attribs['id'];
19493                      $this->svggradients[$this->svggradientid] = array();
19494                      $this->svggradients[$this->svggradientid]['type'] = 3;
19495                      $this->svggradients[$this->svggradientid]['stops'] = array();
19496                      if (isset($attribs['gradientUnits'])) {
19497                          $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
19498                      } else {
19499                          $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
19500                      }
19501                      //$attribs['spreadMethod']
19502                      $cx = (isset($attribs['cx'])?$attribs['cx']:0.5);
19503                      $cy = (isset($attribs['cy'])?$attribs['cy']:0.5);
19504                      $fx = (isset($attribs['fx'])?$attribs['fx']:$cx);
19505                      $fy = (isset($attribs['fy'])?$attribs['fy']:$cy);
19506                      $r = (isset($attribs['r'])?$attribs['r']:0.5);
19507                      if (isset($attribs['cx']) AND (substr($attribs['cx'], -1) != '%')) {
19508                          $this->svggradients[$this->svggradientid]['mode'] = 'measure';
19509                      } else {
19510                          $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
19511                      }
19512                      if (isset($attribs['gradientTransform'])) {
19513                          $this->svggradients[$this->svggradientid]['gradientTransform'] = $this->getSVGTransformMatrix($attribs['gradientTransform']);
19514                      }
19515                      $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
19516                      if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
19517                          // gradient is defined on another place
19518                          $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
19519                      }
19520                      break;
19521                  }
19522                  case 'stop': {
19523                      // gradient stops
19524                      if (substr($attribs['offset'], -1) == '%') {
19525                          $offset = floatval(substr($attribs['offset'], -1)) / 100;
19526                      } else {
19527                          $offset = floatval($attribs['offset']);
19528                          if ($offset > 1) {
19529                              $offset /= 100;
19530                          }
19531                      }
19532                      $stop_color = isset($svgstyle['stop-color'])?$this->convertHTMLColorToDec($svgstyle['stop-color']):'black';
19533                      $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
19534                      $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
19535                      break;
19536                  }
19537                  // paths
19538                  case 'path': {
19539                      $d = trim($attribs['d']);
19540                      if ($clipping) {
19541                          $this->SVGTransform($tm);
19542                          $this->SVGPath($d, 'CNZ');
19543                      } else {
19544                          $this->StartTransform();
19545                          $this->SVGTransform($tm);
19546                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, 0, 0, 1, 1, 'SVGPath', array($d, 'CNZ'));
19547                          if (!empty($obstyle)) {
19548                              $this->SVGPath($d, $obstyle);
19549                          }
19550                          $this->StopTransform();
19551                      }
19552                      break;
19553                  }
19554                  // shapes
19555                  case 'rect': {
19556                      $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
19557                      $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
19558                      $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
19559                      $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
19560                      $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
19561                      $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
19562                      if ($clipping) {
19563                          $this->SVGTransform($tm);
19564                          $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
19565                      } else {
19566                          $this->StartTransform();
19567                          $this->SVGTransform($tm);
19568                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
19569                          if (!empty($obstyle)) {
19570                              $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
19571                          }
19572                          $this->StopTransform();
19573                      }
19574                      break;
19575                  }
19576                  case 'circle': {
19577                      $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
19578                      $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
19579                      $r = (isset($attribs['r'])?$this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false):0);
19580                      $x = $cx - $r;
19581                      $y = $cy - $r;
19582                      $w = 2 * $r;
19583                      $h = $w;
19584                      if ($clipping) {
19585                          $this->SVGTransform($tm);
19586                          $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
19587                      } else {
19588                          $this->StartTransform();
19589                          $this->SVGTransform($tm);
19590                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
19591                          if (!empty($obstyle)) {
19592                              $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
19593                          }
19594                          $this->StopTransform();
19595                      }
19596                      break;
19597                  }
19598                  case 'ellipse': {
19599                      $cx = (isset($attribs['cx'])?$this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false):0);
19600                      $cy = (isset($attribs['cy'])?$this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false):0);
19601                      $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
19602                      $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):0);
19603                      $x = $cx - $rx;
19604                      $y = $cy - $ry;
19605                      $w = 2 * $rx;
19606                      $h = 2 * $ry;
19607                      if ($clipping) {
19608                          $this->SVGTransform($tm);
19609                          $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
19610                      } else {
19611                          $this->StartTransform();
19612                          $this->SVGTransform($tm);
19613                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
19614                          if (!empty($obstyle)) {
19615                              $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
19616                          }
19617                          $this->StopTransform();
19618                      }
19619                      break;
19620                  }
19621                  case 'line': {
19622                      $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
19623                      $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
19624                      $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
19625                      $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
19626                      $x = $x1;
19627                      $y = $y1;
19628                      $w = abs($x2 - $x1);
19629                      $h = abs($y2 - $y1);
19630                      if (!$clipping) {
19631                          $this->StartTransform();
19632                          $this->SVGTransform($tm);
19633                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
19634                          $this->Line($x1, $y1, $x2, $y2);
19635                          $this->StopTransform();
19636                      }
19637                      break;
19638                  }
19639                  case 'polyline':
19640                  case 'polygon': {
19641                      $points = (isset($attribs['points'])?$attribs['points']:'0 0');
19642                      $points = trim($points);
19643                      // note that point may use a complex syntax not covered here
19644                      $points = preg_split('/[\,\s]+/si', $points);
19645                      if (count($points) < 4) {
19646                          break;
19647                      }
19648                      $p = array();
19649                      $xmin = 2147483647;
19650                      $xmax = 0;
19651                      $ymin = 2147483647;
19652                      $ymax = 0;
19653                      foreach ($points as $key => $val) {
19654                          $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
19655                          if (($key % 2) == 0) {
19656                              // X coordinate
19657                              $xmin = min($xmin, $p[$key]);
19658                              $xmax = max($xmax, $p[$key]);
19659                          } else {
19660                              // Y coordinate
19661                              $ymin = min($ymin, $p[$key]);
19662                              $ymax = max($ymax, $p[$key]);
19663                          }
19664                      }
19665                      $x = $xmin;
19666                      $y = $ymin;
19667                      $w = ($xmax - $xmin);
19668                      $h = ($ymax - $ymin);
19669                      if ($name == 'polyline') {
19670                          $this->StartTransform();
19671                          $this->SVGTransform($tm);
19672                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
19673                          $this->PolyLine($p, 'D', array(), array());
19674                          $this->StopTransform();
19675                      } else { // polygon
19676                          if ($clipping) {
19677                              $this->SVGTransform($tm);
19678                              $this->Polygon($p, 'CNZ', array(), array(), true);
19679                          } else {
19680                              $this->StartTransform();
19681                              $this->SVGTransform($tm);
19682                              $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
19683                              if (!empty($obstyle)) {
19684                                  $this->Polygon($p, $obstyle, array(), array(), true);
19685                              }
19686                              $this->StopTransform();
19687                          }
19688                      }
19689                      break;
19690                  }
19691                  // image
19692                  case 'image': {
19693                      if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
19694                          break;
19695                      }
19696                      $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
19697                      $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
19698                      $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
19699                      $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
19700                      $img = $attribs['xlink:href'];
19701                      if (!$clipping) {
19702                          $this->StartTransform();
19703                          $this->SVGTransform($tm);
19704                          $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
19705                          // fix image path
19706                          if (!$this->empty_string($this->svgdir) AND (($img{0} == '.') OR (basename($img) == $img))) {
19707                              // replace relative path with full server path
19708                              $img = $this->svgdir.'/'.$img;
19709                          }
19710                          if (($img{0} == '/') AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
19711                              $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
19712                              if (($findroot === false) OR ($findroot > 1)) {
19713                                  // replace relative path with full server path
19714                                  $img = $_SERVER['DOCUMENT_ROOT'].$img;
19715                              }
19716                          }
19717                          $img = urldecode($img);
19718                          $testscrtype = @parse_url($img);
19719                          if (!isset($testscrtype['query']) OR empty($testscrtype['query'])) {
19720                              // convert URL to server path
19721                              $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
19722                          }
19723                          $this->Image($img, $x, $y, $w, $h);
19724                          $this->StopTransform();
19725                      }
19726                      break;
19727                  }
19728                  // text
19729                  case 'text':
19730                  case 'tspan': {
19731                      // only basic support - advanced features must be implemented
19732                      $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
19733                      $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
19734                      $svgstyle['text-color'] = $svgstyle['fill'];
19735                      $this->svgtext = '';
19736                      $this->StartTransform();
19737                      $this->SVGTransform($tm);
19738                      $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
19739                      $this->SetXY($x, $y, true);
19740                      break;
19741                  }
19742                  // use
19743                  case 'use': {
19744                      if (isset($attribs['xlink:href'])) {
19745                          $use = $this->svgdefs[substr($attribs['xlink:href'], 1)];
19746                          if (isset($attribs['xlink:href'])) {
19747                              unset($attribs['xlink:href']);
19748                          }
19749                          if (isset($attribs['id'])) {
19750                              unset($attribs['id']);
19751                          }
19752                          $attribs = array_merge($use['attribs'], $attribs);
19753                          $this->startSVGElementHandler($parser, $use['name'], $use['attribs']);
19754                      }
19755                      break;
19756                  }
19757                  default: {
19758                      break;
19759                  }
19760              }
19761          }
19762  
19763          /**
19764           * Sets the closing SVG element handler function for the XML parser.
19765           * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
19766           * @param string $name The second parameter, name, contains the name of the element for which this handler is called. If case-folding is in effect for this parser, the element name will be in uppercase letters. 
19767           * @author Nicola Asuni
19768           * @since 5.0.000 (2010-05-02)
19769           * @access protected
19770           */
19771  		protected function endSVGElementHandler($parser, $name) {
19772              switch($name) {
19773                  case 'defs': {
19774                      $this->svgdefsmode = false;
19775                      break;
19776                  }
19777                  // clipPath
19778                  case 'clipPath': {
19779                      $this->svgclipmode = false;
19780                      break;
19781                  }
19782                  case 'g': {
19783                      // ungroup: remove last style from array
19784                      array_pop($this->svgstyles);
19785                      $this->StopTransform();
19786                      break;
19787                  }
19788                  case 'text':
19789                  case 'tspan': {
19790                      // print text
19791                      $this->Cell(0, 0, trim($this->svgtext), 0, 0, '', 0, '', 0, false, 'L', 'T');
19792                      $this->StopTransform();
19793                      break;
19794                  }
19795                  default: {
19796                      break;
19797                  }
19798              }
19799          }
19800  
19801          /**
19802           * Sets the character data handler function for the XML parser.
19803           * @param resource $parser The first parameter, parser, is a reference to the XML parser calling the handler.
19804           * @param string $data The second parameter, data, contains the character data as a string. 
19805           * @author Nicola Asuni
19806           * @since 5.0.000 (2010-05-02)
19807           * @access protected
19808           */
19809  		protected function segSVGContentHandler($parser, $data) {
19810              $this->svgtext .= $data;
19811          }
19812  
19813      } // END OF TCPDF CLASS
19814  }
19815  //============================================================+
19816  // END OF FILE
19817  //============================================================+
19818  ?>


Generated: Tue Mar 17 22:47:18 2015 Cross-referenced by PHPXref 0.7.1