Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  //============================================================+
   3  // File name   : tcpdf.php
   4  // Version     : 6.3.2
   5  // Begin       : 2002-08-03
   6  // Last Update : 2019-09-20
   7  // Author      : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
   8  // License     : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
   9  // -------------------------------------------------------------------
  10  // Copyright (C) 2002-2019 Nicola Asuni - Tecnick.com LTD
  11  //
  12  // This file is part of TCPDF software library.
  13  //
  14  // TCPDF is free software: you can redistribute it and/or modify it
  15  // under the terms of the GNU Lesser General Public License as
  16  // published by the Free Software Foundation, either version 3 of the
  17  // License, or (at your option) any later version.
  18  //
  19  // TCPDF is distributed in the hope that it will be useful, but
  20  // WITHOUT ANY WARRANTY; without even the implied warranty of
  21  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  22  // See the GNU Lesser General Public License for more details.
  23  //
  24  // You should have received a copy of the License
  25  // along with TCPDF. If not, see
  26  // <http://www.tecnick.com/pagefiles/tcpdf/LICENSE.TXT>.
  27  //
  28  // See LICENSE.TXT file for more information.
  29  // -------------------------------------------------------------------
  30  //
  31  // Description :
  32  //   This is a PHP class for generating PDF documents without requiring external extensions.
  33  //
  34  // NOTE:
  35  //   This class was originally derived in 2002 from the Public
  36  //   Domain FPDF class by Olivier Plathey (http://www.fpdf.org),
  37  //   but now is almost entirely rewritten and contains thousands of
  38  //   new lines of code and hundreds new features.
  39  //
  40  // Main features:
  41  //  * no external libraries are required for the basic functions;
  42  //  * all standard page formats, custom page formats, custom margins and units of measure;
  43  //  * UTF-8 Unicode and Right-To-Left languages;
  44  //  * TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;
  45  //  * font subsetting;
  46  //  * methods to publish some XHTML + CSS code, Javascript and Forms;
  47  //  * images, graphic (geometric figures) and transformation methods;
  48  //  * 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 ImageMagick (http://www.imagemagick.org/www/formats.html)
  49  //  * 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 Extension, 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, Datamatrix, QR-Code, PDF417;
  50  //  * JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;
  51  //  * automatic page header and footer management;
  52  //  * document encryption up to 256 bit and digital signature certifications;
  53  //  * transactions to UNDO commands;
  54  //  * PDF annotations, including links, text and file attachments;
  55  //  * text rendering modes (fill, stroke and clipping);
  56  //  * multiple columns mode;
  57  //  * no-write page regions;
  58  //  * bookmarks, named destinations and table of content;
  59  //  * text hyphenation;
  60  //  * text stretching and spacing (tracking);
  61  //  * automatic page break, line break and text alignments including justification;
  62  //  * automatic page numbering and page groups;
  63  //  * move and delete pages;
  64  //  * page compression (requires php-zlib extension);
  65  //  * XOBject Templates;
  66  //  * Layers and object visibility.
  67  //	 * PDF/A-1b support
  68  //============================================================+
  69  
  70  /**
  71   * @file
  72   * This is a PHP class for generating PDF documents without requiring external extensions.<br>
  73   * 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>
  74   * <h3>TCPDF main features are:</h3>
  75   * <ul>
  76   * <li>no external libraries are required for the basic functions;</li>
  77   * <li>all standard page formats, custom page formats, custom margins and units of measure;</li>
  78   * <li>UTF-8 Unicode and Right-To-Left languages;</li>
  79   * <li>TrueTypeUnicode, TrueType, Type1 and CID-0 fonts;</li>
  80   * <li>font subsetting;</li>
  81   * <li>methods to publish some XHTML + CSS code, Javascript and Forms;</li>
  82   * <li>images, graphic (geometric figures) and transformation methods;
  83   * <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 ImageMagick (http://www.imagemagick.org/www/formats.html)</li>
  84   * <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 Extension, 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, Datamatrix, QR-Code, PDF417;</li>
  85   * <li>JPEG and PNG ICC profiles, Grayscale, RGB, CMYK, Spot Colors and Transparencies;</li>
  86   * <li>automatic page header and footer management;</li>
  87   * <li>document encryption up to 256 bit and digital signature certifications;</li>
  88   * <li>transactions to UNDO commands;</li>
  89   * <li>PDF annotations, including links, text and file attachments;</li>
  90   * <li>text rendering modes (fill, stroke and clipping);</li>
  91   * <li>multiple columns mode;</li>
  92   * <li>no-write page regions;</li>
  93   * <li>bookmarks, named destinations and table of content;</li>
  94   * <li>text hyphenation;</li>
  95   * <li>text stretching and spacing (tracking);</li>
  96   * <li>automatic page break, line break and text alignments including justification;</li>
  97   * <li>automatic page numbering and page groups;</li>
  98   * <li>move and delete pages;</li>
  99   * <li>page compression (requires php-zlib extension);</li>
 100   * <li>XOBject Templates;</li>
 101   * <li>Layers and object visibility;</li>
 102   * <li>PDF/A-1b support.</li>
 103   * </ul>
 104   * Tools to encode your unicode fonts are on fonts/utils directory.</p>
 105   * @package com.tecnick.tcpdf
 106   * @author Nicola Asuni
 107   * @version 6.3.2
 108   */
 109  
 110  // TCPDF configuration
 111  require_once(dirname(__FILE__).'/tcpdf_autoconfig.php');
 112  // TCPDF static font methods and data
 113  require_once(dirname(__FILE__).'/include/tcpdf_font_data.php');
 114  // TCPDF static font methods and data
 115  require_once(dirname(__FILE__).'/include/tcpdf_fonts.php');
 116  // TCPDF static color methods and data
 117  require_once(dirname(__FILE__).'/include/tcpdf_colors.php');
 118  // TCPDF static image methods and data
 119  require_once(dirname(__FILE__).'/include/tcpdf_images.php');
 120  // TCPDF static methods and data
 121  require_once(dirname(__FILE__).'/include/tcpdf_static.php');
 122  
 123  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 124  
 125  /**
 126   * @class TCPDF
 127   * PHP class for generating PDF documents without requiring external extensions.
 128   * 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>
 129   * @package com.tecnick.tcpdf
 130   * @brief PHP class for generating PDF documents without requiring external extensions.
 131   * @version 6.3.2
 132   * @author Nicola Asuni - info@tecnick.com
 133   * @IgnoreAnnotation("protected")
 134   * @IgnoreAnnotation("public")
 135   * @IgnoreAnnotation("pre")
 136   */
 137  class TCPDF {
 138  
 139  	 // Protected properties
 140  
 141  	 /**
 142  	  * Current page number.
 143  	  * @protected
 144  	  */
 145  	 protected $page;
 146  
 147  	 /**
 148  	  * Current object number.
 149  	  * @protected
 150  	  */
 151  	 protected $n;
 152  
 153  	 /**
 154  	  * Array of object offsets.
 155  	  * @protected
 156  	  */
 157  	 protected $offsets = array();
 158  
 159  	 /**
 160  	  * Array of object IDs for each page.
 161  	  * @protected
 162  	  */
 163  	 protected $pageobjects = array();
 164  
 165  	 /**
 166  	  * Buffer holding in-memory PDF.
 167  	  * @protected
 168  	  */
 169  	 protected $buffer;
 170  
 171  	 /**
 172  	  * Array containing pages.
 173  	  * @protected
 174  	  */
 175  	 protected $pages = array();
 176  
 177  	 /**
 178  	  * Current document state.
 179  	  * @protected
 180  	  */
 181  	 protected $state;
 182  
 183  	 /**
 184  	  * Compression flag.
 185  	  * @protected
 186  	  */
 187  	 protected $compress;
 188  
 189  	 /**
 190  	  * Current page orientation (P = Portrait, L = Landscape).
 191  	  * @protected
 192  	  */
 193  	 protected $CurOrientation;
 194  
 195  	 /**
 196  	  * Page dimensions.
 197  	  * @protected
 198  	  */
 199  	 protected $pagedim = array();
 200  
 201  	 /**
 202  	  * Scale factor (number of points in user unit).
 203  	  * @protected
 204  	  */
 205  	 protected $k;
 206  
 207  	 /**
 208  	  * Width of page format in points.
 209  	  * @protected
 210  	  */
 211  	 protected $fwPt;
 212  
 213  	 /**
 214  	  * Height of page format in points.
 215  	  * @protected
 216  	  */
 217  	 protected $fhPt;
 218  
 219  	 /**
 220  	  * Current width of page in points.
 221  	  * @protected
 222  	  */
 223  	 protected $wPt;
 224  
 225  	 /**
 226  	  * Current height of page in points.
 227  	  * @protected
 228  	  */
 229  	 protected $hPt;
 230  
 231  	 /**
 232  	  * Current width of page in user unit.
 233  	  * @protected
 234  	  */
 235  	 protected $w;
 236  
 237  	 /**
 238  	  * Current height of page in user unit.
 239  	  * @protected
 240  	  */
 241  	 protected $h;
 242  
 243  	 /**
 244  	  * Left margin.
 245  	  * @protected
 246  	  */
 247  	 protected $lMargin;
 248  
 249  	 /**
 250  	  * Right margin.
 251  	  * @protected
 252  	  */
 253  	 protected $rMargin;
 254  
 255  	 /**
 256  	  * Cell left margin (used by regions).
 257  	  * @protected
 258  	  */
 259  	 protected $clMargin;
 260  
 261  	 /**
 262  	  * Cell right margin (used by regions).
 263  	  * @protected
 264  	  */
 265  	 protected $crMargin;
 266  
 267  	 /**
 268  	  * Top margin.
 269  	  * @protected
 270  	  */
 271  	 protected $tMargin;
 272  
 273  	 /**
 274  	  * Page break margin.
 275  	  * @protected
 276  	  */
 277  	 protected $bMargin;
 278  
 279  	 /**
 280  	  * Array of cell internal paddings ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
 281  	  * @since 5.9.000 (2010-10-03)
 282  	  * @protected
 283  	  */
 284  	 protected $cell_padding = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
 285  
 286  	 /**
 287  	  * Array of cell margins ('T' => top, 'R' => right, 'B' => bottom, 'L' => left).
 288  	  * @since 5.9.000 (2010-10-04)
 289  	  * @protected
 290  	  */
 291  	 protected $cell_margin = array('T' => 0, 'R' => 0, 'B' => 0, 'L' => 0);
 292  
 293  	 /**
 294  	  * Current horizontal position in user unit for cell positioning.
 295  	  * @protected
 296  	  */
 297  	 protected $x;
 298  
 299  	 /**
 300  	  * Current vertical position in user unit for cell positioning.
 301  	  * @protected
 302  	  */
 303  	 protected $y;
 304  
 305  	 /**
 306  	  * Height of last cell printed.
 307  	  * @protected
 308  	  */
 309  	 protected $lasth;
 310  
 311  	 /**
 312  	  * Line width in user unit.
 313  	  * @protected
 314  	  */
 315  	 protected $LineWidth;
 316  
 317  	 /**
 318  	  * Array of standard font names.
 319  	  * @protected
 320  	  */
 321  	 protected $CoreFonts;
 322  
 323  	 /**
 324  	  * Array of used fonts.
 325  	  * @protected
 326  	  */
 327  	 protected $fonts = array();
 328  
 329  	 /**
 330  	  * Array of font files.
 331  	  * @protected
 332  	  */
 333  	 protected $FontFiles = array();
 334  
 335  	 /**
 336  	  * Array of encoding differences.
 337  	  * @protected
 338  	  */
 339  	 protected $diffs = array();
 340  
 341  	 /**
 342  	  * Array of used images.
 343  	  * @protected
 344  	  */
 345  	 protected $images = array();
 346  
 347  	 /**
 348  	  * Depth of the svg tag, to keep track if the svg tag is a subtag or the root tag.
 349  	  * @protected
 350  	  */
 351  	 protected $svg_tag_depth = 0;
 352  
 353  	 /**
 354  	  * Array of Annotations in pages.
 355  	  * @protected
 356  	  */
 357  	 protected $PageAnnots = array();
 358  
 359  	 /**
 360  	  * Array of internal links.
 361  	  * @protected
 362  	  */
 363  	 protected $links = array();
 364  
 365  	 /**
 366  	  * Current font family.
 367  	  * @protected
 368  	  */
 369  	 protected $FontFamily;
 370  
 371  	 /**
 372  	  * Current font style.
 373  	  * @protected
 374  	  */
 375  	 protected $FontStyle;
 376  
 377  	 /**
 378  	  * Current font ascent (distance between font top and baseline).
 379  	  * @protected
 380  	  * @since 2.8.000 (2007-03-29)
 381  	  */
 382  	 protected $FontAscent;
 383  
 384  	 /**
 385  	  * Current font descent (distance between font bottom and baseline).
 386  	  * @protected
 387  	  * @since 2.8.000 (2007-03-29)
 388  	  */
 389  	 protected $FontDescent;
 390  
 391  	 /**
 392  	  * Underlining flag.
 393  	  * @protected
 394  	  */
 395  	 protected $underline;
 396  
 397  	 /**
 398  	  * Overlining flag.
 399  	  * @protected
 400  	  */
 401  	 protected $overline;
 402  
 403  	 /**
 404  	  * Current font info.
 405  	  * @protected
 406  	  */
 407  	 protected $CurrentFont;
 408  
 409  	 /**
 410  	  * Current font size in points.
 411  	  * @protected
 412  	  */
 413  	 protected $FontSizePt;
 414  
 415  	 /**
 416  	  * Current font size in user unit.
 417  	  * @protected
 418  	  */
 419  	 protected $FontSize;
 420  
 421  	 /**
 422  	  * Commands for drawing color.
 423  	  * @protected
 424  	  */
 425  	 protected $DrawColor;
 426  
 427  	 /**
 428  	  * Commands for filling color.
 429  	  * @protected
 430  	  */
 431  	 protected $FillColor;
 432  
 433  	 /**
 434  	  * Commands for text color.
 435  	  * @protected
 436  	  */
 437  	 protected $TextColor;
 438  
 439  	 /**
 440  	  * Indicates whether fill and text colors are different.
 441  	  * @protected
 442  	  */
 443  	 protected $ColorFlag;
 444  
 445  	 /**
 446  	  * Automatic page breaking.
 447  	  * @protected
 448  	  */
 449  	 protected $AutoPageBreak;
 450  
 451  	 /**
 452  	  * Threshold used to trigger page breaks.
 453  	  * @protected
 454  	  */
 455  	 protected $PageBreakTrigger;
 456  
 457  	 /**
 458  	  * Flag set when processing page header.
 459  	  * @protected
 460  	  */
 461  	 protected $InHeader = false;
 462  
 463  	 /**
 464  	  * Flag set when processing page footer.
 465  	  * @protected
 466  	  */
 467  	 protected $InFooter = false;
 468  
 469  	 /**
 470  	  * Zoom display mode.
 471  	  * @protected
 472  	  */
 473  	 protected $ZoomMode;
 474  
 475  	 /**
 476  	  * Layout display mode.
 477  	  * @protected
 478  	  */
 479  	 protected $LayoutMode;
 480  
 481  	 /**
 482  	  * If true set the document information dictionary in Unicode.
 483  	  * @protected
 484  	  */
 485  	 protected $docinfounicode = true;
 486  
 487  	 /**
 488  	  * Document title.
 489  	  * @protected
 490  	  */
 491  	 protected $title = '';
 492  
 493  	 /**
 494  	  * Document subject.
 495  	  * @protected
 496  	  */
 497  	 protected $subject = '';
 498  
 499  	 /**
 500  	  * Document author.
 501  	  * @protected
 502  	  */
 503  	 protected $author = '';
 504  
 505  	 /**
 506  	  * Document keywords.
 507  	  * @protected
 508  	  */
 509  	 protected $keywords = '';
 510  
 511  	 /**
 512  	  * Document creator.
 513  	  * @protected
 514  	  */
 515  	 protected $creator = '';
 516  
 517  	 /**
 518  	  * Starting page number.
 519  	  * @protected
 520  	  */
 521  	 protected $starting_page_number = 1;
 522  
 523  	 /**
 524  	  * The right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image.
 525  	  * @since 2002-07-31
 526  	  * @author Nicola Asuni
 527  	  * @protected
 528  	  */
 529  	 protected $img_rb_x;
 530  
 531  	 /**
 532  	  * The right-bottom corner Y coordinate of last inserted image.
 533  	  * @since 2002-07-31
 534  	  * @author Nicola Asuni
 535  	  * @protected
 536  	  */
 537  	 protected $img_rb_y;
 538  
 539  	 /**
 540  	  * Adjusting factor to convert pixels to user units.
 541  	  * @since 2004-06-14
 542  	  * @author Nicola Asuni
 543  	  * @protected
 544  	  */
 545  	 protected $imgscale = 1;
 546  
 547  	 /**
 548  	  * Boolean flag set to true when the input text is unicode (require unicode fonts).
 549  	  * @since 2005-01-02
 550  	  * @author Nicola Asuni
 551  	  * @protected
 552  	  */
 553  	 protected $isunicode = false;
 554  
 555  	 /**
 556  	  * PDF version.
 557  	  * @since 1.5.3
 558  	  * @protected
 559  	  */
 560  	 protected $PDFVersion = '1.7';
 561  
 562  	 /**
 563  	  * ID of the stored default header template (-1 = not set).
 564  	  * @protected
 565  	  */
 566  	 protected $header_xobjid = false;
 567  
 568  	 /**
 569  	  * If true reset the Header Xobject template at each page
 570  	  * @protected
 571  	  */
 572  	 protected $header_xobj_autoreset = false;
 573  
 574  	 /**
 575  	  * Minimum distance between header and top page margin.
 576  	  * @protected
 577  	  */
 578  	 protected $header_margin;
 579  
 580  	 /**
 581  	  * Minimum distance between footer and bottom page margin.
 582  	  * @protected
 583  	  */
 584  	 protected $footer_margin;
 585  
 586  	 /**
 587  	  * Original left margin value.
 588  	  * @protected
 589  	  * @since 1.53.0.TC013
 590  	  */
 591  	 protected $original_lMargin;
 592  
 593  	 /**
 594  	  * Original right margin value.
 595  	  * @protected
 596  	  * @since 1.53.0.TC013
 597  	  */
 598  	 protected $original_rMargin;
 599  
 600  	 /**
 601  	  * Default font used on page header.
 602  	  * @protected
 603  	  */
 604  	 protected $header_font;
 605  
 606  	 /**
 607  	  * Default font used on page footer.
 608  	  * @protected
 609  	  */
 610  	 protected $footer_font;
 611  
 612  	 /**
 613  	  * Language templates.
 614  	  * @protected
 615  	  */
 616  	 protected $l;
 617  
 618  	 /**
 619  	  * Barcode to print on page footer (only if set).
 620  	  * @protected
 621  	  */
 622  	 protected $barcode = false;
 623  
 624  	 /**
 625  	  * Boolean flag to print/hide page header.
 626  	  * @protected
 627  	  */
 628  	 protected $print_header = true;
 629  
 630  	 /**
 631  	  * Boolean flag to print/hide page footer.
 632  	  * @protected
 633  	  */
 634  	 protected $print_footer = true;
 635  
 636  	 /**
 637  	  * Header image logo.
 638  	  * @protected
 639  	  */
 640  	 protected $header_logo = '';
 641  
 642  	 /**
 643  	  * Width of header image logo in user units.
 644  	  * @protected
 645  	  */
 646  	 protected $header_logo_width = 30;
 647  
 648  	 /**
 649  	  * Title to be printed on default page header.
 650  	  * @protected
 651  	  */
 652  	 protected $header_title = '';
 653  
 654  	 /**
 655  	  * String to pring on page header after title.
 656  	  * @protected
 657  	  */
 658  	 protected $header_string = '';
 659  
 660  	 /**
 661  	  * Color for header text (RGB array).
 662  	  * @since 5.9.174 (2012-07-25)
 663  	  * @protected
 664  	  */
 665  	 protected $header_text_color = array(0,0,0);
 666  
 667  	 /**
 668  	  * Color for header line (RGB array).
 669  	  * @since 5.9.174 (2012-07-25)
 670  	  * @protected
 671  	  */
 672  	 protected $header_line_color = array(0,0,0);
 673  
 674  	 /**
 675  	  * Color for footer text (RGB array).
 676  	  * @since 5.9.174 (2012-07-25)
 677  	  * @protected
 678  	  */
 679  	 protected $footer_text_color = array(0,0,0);
 680  
 681  	 /**
 682  	  * Color for footer line (RGB array).
 683  	  * @since 5.9.174 (2012-07-25)
 684  	  * @protected
 685  	  */
 686  	 protected $footer_line_color = array(0,0,0);
 687  
 688  	 /**
 689  	  * Text shadow data array.
 690  	  * @since 5.9.174 (2012-07-25)
 691  	  * @protected
 692  	  */
 693  	 protected $txtshadow = array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal');
 694  
 695  	 /**
 696  	  * Default number of columns for html table.
 697  	  * @protected
 698  	  */
 699  	 protected $default_table_columns = 4;
 700  
 701  	 // variables for html parser
 702  
 703  	 /**
 704  	  * HTML PARSER: array to store current link and rendering styles.
 705  	  * @protected
 706  	  */
 707  	 protected $HREF = array();
 708  
 709  	 /**
 710  	  * List of available fonts on filesystem.
 711  	  * @protected
 712  	  */
 713  	 protected $fontlist = array();
 714  
 715  	 /**
 716  	  * Current foreground color.
 717  	  * @protected
 718  	  */
 719  	 protected $fgcolor;
 720  
 721  	 /**
 722  	  * HTML PARSER: array of boolean values, true in case of ordered list (OL), false otherwise.
 723  	  * @protected
 724  	  */
 725  	 protected $listordered = array();
 726  
 727  	 /**
 728  	  * HTML PARSER: array count list items on nested lists.
 729  	  * @protected
 730  	  */
 731  	 protected $listcount = array();
 732  
 733  	 /**
 734  	  * HTML PARSER: current list nesting level.
 735  	  * @protected
 736  	  */
 737  	 protected $listnum = 0;
 738  
 739  	 /**
 740  	  * HTML PARSER: indent amount for lists.
 741  	  * @protected
 742  	  */
 743  	 protected $listindent = 0;
 744  
 745  	 /**
 746  	  * HTML PARSER: current list indententation level.
 747  	  * @protected
 748  	  */
 749  	 protected $listindentlevel = 0;
 750  
 751  	 /**
 752  	  * Current background color.
 753  	  * @protected
 754  	  */
 755  	 protected $bgcolor;
 756  
 757  	 /**
 758  	  * Temporary font size in points.
 759  	  * @protected
 760  	  */
 761  	 protected $tempfontsize = 10;
 762  
 763  	 /**
 764  	  * Spacer string for LI tags.
 765  	  * @protected
 766  	  */
 767  	 protected $lispacer = '';
 768  
 769  	 /**
 770  	  * Default encoding.
 771  	  * @protected
 772  	  * @since 1.53.0.TC010
 773  	  */
 774  	 protected $encoding = 'UTF-8';
 775  
 776  	 /**
 777  	  * Boolean flag to indicate if the document language is Right-To-Left.
 778  	  * @protected
 779  	  * @since 2.0.000
 780  	  */
 781  	 protected $rtl = false;
 782  
 783  	 /**
 784  	  * Boolean flag used to force RTL or LTR string direction.
 785  	  * @protected
 786  	  * @since 2.0.000
 787  	  */
 788  	 protected $tmprtl = false;
 789  
 790  	 // --- Variables used for document encryption:
 791  
 792  	 /**
 793  	  * IBoolean flag indicating whether document is protected.
 794  	  * @protected
 795  	  * @since 2.0.000 (2008-01-02)
 796  	  */
 797  	 protected $encrypted;
 798  
 799  	 /**
 800  	  * Array containing encryption settings.
 801  	  * @protected
 802  	  * @since 5.0.005 (2010-05-11)
 803  	  */
 804  	 protected $encryptdata = array();
 805  
 806  	 /**
 807  	  * Last RC4 key encrypted (cached for optimisation).
 808  	  * @protected
 809  	  * @since 2.0.000 (2008-01-02)
 810  	  */
 811  	 protected $last_enc_key;
 812  
 813  	 /**
 814  	  * Last RC4 computed key.
 815  	  * @protected
 816  	  * @since 2.0.000 (2008-01-02)
 817  	  */
 818  	 protected $last_enc_key_c;
 819  
 820  	 /**
 821  	  * File ID (used on document trailer).
 822  	  * @protected
 823  	  * @since 5.0.005 (2010-05-12)
 824  	  */
 825  	 protected $file_id;
 826  
 827  	 // --- bookmark ---
 828  
 829  	 /**
 830  	  * Outlines for bookmark.
 831  	  * @protected
 832  	  * @since 2.1.002 (2008-02-12)
 833  	  */
 834  	 protected $outlines = array();
 835  
 836  	 /**
 837  	  * Outline root for bookmark.
 838  	  * @protected
 839  	  * @since 2.1.002 (2008-02-12)
 840  	  */
 841  	 protected $OutlineRoot;
 842  
 843  	 // --- javascript and form ---
 844  
 845  	 /**
 846  	  * Javascript code.
 847  	  * @protected
 848  	  * @since 2.1.002 (2008-02-12)
 849  	  */
 850  	 protected $javascript = '';
 851  
 852  	 /**
 853  	  * Javascript counter.
 854  	  * @protected
 855  	  * @since 2.1.002 (2008-02-12)
 856  	  */
 857  	 protected $n_js;
 858  
 859  	 /**
 860  	  * line through state
 861  	  * @protected
 862  	  * @since 2.8.000 (2008-03-19)
 863  	  */
 864  	 protected $linethrough;
 865  
 866  	 /**
 867  	  * Array with additional document-wide usage rights for the document.
 868  	  * @protected
 869  	  * @since 5.8.014 (2010-08-23)
 870  	  */
 871  	 protected $ur = array();
 872  
 873  	 /**
 874  	  * DPI (Dot Per Inch) Document Resolution (do not change).
 875  	  * @protected
 876  	  * @since 3.0.000 (2008-03-27)
 877  	  */
 878  	 protected $dpi = 72;
 879  
 880  	 /**
 881  	  * Array of page numbers were a new page group was started (the page numbers are the keys of the array).
 882  	  * @protected
 883  	  * @since 3.0.000 (2008-03-27)
 884  	  */
 885  	 protected $newpagegroup = array();
 886  
 887  	 /**
 888  	  * Array that contains the number of pages in each page group.
 889  	  * @protected
 890  	  * @since 3.0.000 (2008-03-27)
 891  	  */
 892  	 protected $pagegroups = array();
 893  
 894  	 /**
 895  	  * Current page group number.
 896  	  * @protected
 897  	  * @since 3.0.000 (2008-03-27)
 898  	  */
 899  	 protected $currpagegroup = 0;
 900  
 901  	 /**
 902  	  * Array of transparency objects and parameters.
 903  	  * @protected
 904  	  * @since 3.0.000 (2008-03-27)
 905  	  */
 906  	 protected $extgstates;
 907  
 908  	 /**
 909  	  * Set the default JPEG compression quality (1-100).
 910  	  * @protected
 911  	  * @since 3.0.000 (2008-03-27)
 912  	  */
 913  	 protected $jpeg_quality;
 914  
 915  	 /**
 916  	  * Default cell height ratio.
 917  	  * @protected
 918  	  * @since 3.0.014 (2008-05-23)
 919  	  */
 920  	 protected $cell_height_ratio = K_CELL_HEIGHT_RATIO;
 921  
 922  	 /**
 923  	  * PDF viewer preferences.
 924  	  * @protected
 925  	  * @since 3.1.000 (2008-06-09)
 926  	  */
 927  	 protected $viewer_preferences;
 928  
 929  	 /**
 930  	  * A name object specifying how the document should be displayed when opened.
 931  	  * @protected
 932  	  * @since 3.1.000 (2008-06-09)
 933  	  */
 934  	 protected $PageMode;
 935  
 936  	 /**
 937  	  * Array for storing gradient information.
 938  	  * @protected
 939  	  * @since 3.1.000 (2008-06-09)
 940  	  */
 941  	 protected $gradients = array();
 942  
 943  	 /**
 944  	  * Array used to store positions inside the pages buffer (keys are the page numbers).
 945  	  * @protected
 946  	  * @since 3.2.000 (2008-06-26)
 947  	  */
 948  	 protected $intmrk = array();
 949  
 950  	 /**
 951  	  * Array used to store positions inside the pages buffer (keys are the page numbers).
 952  	  * @protected
 953  	  * @since 5.7.000 (2010-08-03)
 954  	  */
 955  	 protected $bordermrk = array();
 956  
 957  	 /**
 958  	  * Array used to store page positions to track empty pages (keys are the page numbers).
 959  	  * @protected
 960  	  * @since 5.8.007 (2010-08-18)
 961  	  */
 962  	 protected $emptypagemrk = array();
 963  
 964  	 /**
 965  	  * Array used to store content positions inside the pages buffer (keys are the page numbers).
 966  	  * @protected
 967  	  * @since 4.6.021 (2009-07-20)
 968  	  */
 969  	 protected $cntmrk = array();
 970  
 971  	 /**
 972  	  * Array used to store footer positions of each page.
 973  	  * @protected
 974  	  * @since 3.2.000 (2008-07-01)
 975  	  */
 976  	 protected $footerpos = array();
 977  
 978  	 /**
 979  	  * Array used to store footer length of each page.
 980  	  * @protected
 981  	  * @since 4.0.014 (2008-07-29)
 982  	  */
 983  	 protected $footerlen = array();
 984  
 985  	 /**
 986  	  * Boolean flag to indicate if a new line is created.
 987  	  * @protected
 988  	  * @since 3.2.000 (2008-07-01)
 989  	  */
 990  	 protected $newline = true;
 991  
 992  	 /**
 993  	  * End position of the latest inserted line.
 994  	  * @protected
 995  	  * @since 3.2.000 (2008-07-01)
 996  	  */
 997  	 protected $endlinex = 0;
 998  
 999  	 /**
1000  	  * PDF string for width value of the last line.
1001  	  * @protected
1002  	  * @since 4.0.006 (2008-07-16)
1003  	  */
1004  	 protected $linestyleWidth = '';
1005  
1006  	 /**
1007  	  * PDF string for CAP value of the last line.
1008  	  * @protected
1009  	  * @since 4.0.006 (2008-07-16)
1010  	  */
1011  	 protected $linestyleCap = '0 J';
1012  
1013  	 /**
1014  	  * PDF string for join value of the last line.
1015  	  * @protected
1016  	  * @since 4.0.006 (2008-07-16)
1017  	  */
1018  	 protected $linestyleJoin = '0 j';
1019  
1020  	 /**
1021  	  * PDF string for dash value of the last line.
1022  	  * @protected
1023  	  * @since 4.0.006 (2008-07-16)
1024  	  */
1025  	 protected $linestyleDash = '[] 0 d';
1026  
1027  	 /**
1028  	  * Boolean flag to indicate if marked-content sequence is open.
1029  	  * @protected
1030  	  * @since 4.0.013 (2008-07-28)
1031  	  */
1032  	 protected $openMarkedContent = false;
1033  
1034  	 /**
1035  	  * Count the latest inserted vertical spaces on HTML.
1036  	  * @protected
1037  	  * @since 4.0.021 (2008-08-24)
1038  	  */
1039  	 protected $htmlvspace = 0;
1040  
1041  	 /**
1042  	  * Array of Spot colors.
1043  	  * @protected
1044  	  * @since 4.0.024 (2008-09-12)
1045  	  */
1046  	 protected $spot_colors = array();
1047  
1048  	 /**
1049  	  * Symbol used for HTML unordered list items.
1050  	  * @protected
1051  	  * @since 4.0.028 (2008-09-26)
1052  	  */
1053  	 protected $lisymbol = '';
1054  
1055  	 /**
1056  	  * String used to mark the beginning and end of EPS image blocks.
1057  	  * @protected
1058  	  * @since 4.1.000 (2008-10-18)
1059  	  */
1060  	 protected $epsmarker = 'x#!#EPS#!#x';
1061  
1062  	 /**
1063  	  * Array of transformation matrix.
1064  	  * @protected
1065  	  * @since 4.2.000 (2008-10-29)
1066  	  */
1067  	 protected $transfmatrix = array();
1068  
1069  	 /**
1070  	  * Current key for transformation matrix.
1071  	  * @protected
1072  	  * @since 4.8.005 (2009-09-17)
1073  	  */
1074  	 protected $transfmatrix_key = 0;
1075  
1076  	 /**
1077  	  * Booklet mode for double-sided pages.
1078  	  * @protected
1079  	  * @since 4.2.000 (2008-10-29)
1080  	  */
1081  	 protected $booklet = false;
1082  
1083  	 /**
1084  	  * Epsilon value used for float calculations.
1085  	  * @protected
1086  	  * @since 4.2.000 (2008-10-29)
1087  	  */
1088  	 protected $feps = 0.005;
1089  
1090  	 /**
1091  	  * Array used for custom vertical spaces for HTML tags.
1092  	  * @protected
1093  	  * @since 4.2.001 (2008-10-30)
1094  	  */
1095  	 protected $tagvspaces = array();
1096  
1097  	 /**
1098  	  * HTML PARSER: custom indent amount for lists. Negative value means disabled.
1099  	  * @protected
1100  	  * @since 4.2.007 (2008-11-12)
1101  	  */
1102  	 protected $customlistindent = -1;
1103  
1104  	 /**
1105  	  * Boolean flag to indicate if the border of the cell sides that cross the page should be removed.
1106  	  * @protected
1107  	  * @since 4.2.010 (2008-11-14)
1108  	  */
1109  	 protected $opencell = true;
1110  
1111  	 /**
1112  	  * Array of files to embedd.
1113  	  * @protected
1114  	  * @since 4.4.000 (2008-12-07)
1115  	  */
1116  	 protected $embeddedfiles = array();
1117  
1118  	 /**
1119  	  * Boolean flag to indicate if we are inside a PRE tag.
1120  	  * @protected
1121  	  * @since 4.4.001 (2008-12-08)
1122  	  */
1123  	 protected $premode = false;
1124  
1125  	 /**
1126  	  * Array used to store positions of graphics transformation blocks inside the page buffer.
1127  	  * keys are the page numbers
1128  	  * @protected
1129  	  * @since 4.4.002 (2008-12-09)
1130  	  */
1131  	 protected $transfmrk = array();
1132  
1133  	 /**
1134  	  * Default color for html links.
1135  	  * @protected
1136  	  * @since 4.4.003 (2008-12-09)
1137  	  */
1138  	 protected $htmlLinkColorArray = array(0, 0, 255);
1139  
1140  	 /**
1141  	  * Default font style to add to html links.
1142  	  * @protected
1143  	  * @since 4.4.003 (2008-12-09)
1144  	  */
1145  	 protected $htmlLinkFontStyle = 'U';
1146  
1147  	 /**
1148  	  * Counts the number of pages.
1149  	  * @protected
1150  	  * @since 4.5.000 (2008-12-31)
1151  	  */
1152  	 protected $numpages = 0;
1153  
1154  	 /**
1155  	  * Array containing page lengths in bytes.
1156  	  * @protected
1157  	  * @since 4.5.000 (2008-12-31)
1158  	  */
1159  	 protected $pagelen = array();
1160  
1161  	 /**
1162  	  * Counts the number of pages.
1163  	  * @protected
1164  	  * @since 4.5.000 (2008-12-31)
1165  	  */
1166  	 protected $numimages = 0;
1167  
1168  	 /**
1169  	  * Store the image keys.
1170  	  * @protected
1171  	  * @since 4.5.000 (2008-12-31)
1172  	  */
1173  	 protected $imagekeys = array();
1174  
1175  	 /**
1176  	  * Length of the buffer in bytes.
1177  	  * @protected
1178  	  * @since 4.5.000 (2008-12-31)
1179  	  */
1180  	 protected $bufferlen = 0;
1181  
1182  	 /**
1183  	  * Counts the number of fonts.
1184  	  * @protected
1185  	  * @since 4.5.000 (2009-01-02)
1186  	  */
1187  	 protected $numfonts = 0;
1188  
1189  	 /**
1190  	  * Store the font keys.
1191  	  * @protected
1192  	  * @since 4.5.000 (2009-01-02)
1193  	  */
1194  	 protected $fontkeys = array();
1195  
1196  	 /**
1197  	  * Store the font object IDs.
1198  	  * @protected
1199  	  * @since 4.8.001 (2009-09-09)
1200  	  */
1201  	 protected $font_obj_ids = array();
1202  
1203  	 /**
1204  	  * Store the fage status (true when opened, false when closed).
1205  	  * @protected
1206  	  * @since 4.5.000 (2009-01-02)
1207  	  */
1208  	 protected $pageopen = array();
1209  
1210  	 /**
1211  	  * Default monospace font.
1212  	  * @protected
1213  	  * @since 4.5.025 (2009-03-10)
1214  	  */
1215  	 protected $default_monospaced_font = 'courier';
1216  
1217  	 /**
1218  	  * Cloned copy of the current class object.
1219  	  * @protected
1220  	  * @since 4.5.029 (2009-03-19)
1221  	  */
1222  	 protected $objcopy;
1223  
1224  	 /**
1225  	  * Array used to store the lengths of cache files.
1226  	  * @protected
1227  	  * @since 4.5.029 (2009-03-19)
1228  	  */
1229  	 protected $cache_file_length = array();
1230  
1231  	 /**
1232  	  * Table header content to be repeated on each new page.
1233  	  * @protected
1234  	  * @since 4.5.030 (2009-03-20)
1235  	  */
1236  	 protected $thead = '';
1237  
1238  	 /**
1239  	  * Margins used for table header.
1240  	  * @protected
1241  	  * @since 4.5.030 (2009-03-20)
1242  	  */
1243  	 protected $theadMargins = array();
1244  
1245  	 /**
1246  	  * Boolean flag to enable document digital signature.
1247  	  * @protected
1248  	  * @since 4.6.005 (2009-04-24)
1249  	  */
1250  	 protected $sign = false;
1251  
1252  	 /**
1253  	  * Digital signature data.
1254  	  * @protected
1255  	  * @since 4.6.005 (2009-04-24)
1256  	  */
1257  	 protected $signature_data = array();
1258  
1259  	 /**
1260  	  * Digital signature max length.
1261  	  * @protected
1262  	  * @since 4.6.005 (2009-04-24)
1263  	  */
1264  	 protected $signature_max_length = 11742;
1265  
1266  	 /**
1267  	  * Data for digital signature appearance.
1268  	  * @protected
1269  	  * @since 5.3.011 (2010-06-16)
1270  	  */
1271  	 protected $signature_appearance = array('page' => 1, 'rect' => '0 0 0 0');
1272  
1273  	 /**
1274  	  * Array of empty digital signature appearances.
1275  	  * @protected
1276  	  * @since 5.9.101 (2011-07-06)
1277  	  */
1278  	 protected $empty_signature_appearance = array();
1279  
1280  	 /**
1281  	  * Boolean flag to enable document timestamping with TSA.
1282  	  * @protected
1283  	  * @since 6.0.085 (2014-06-19)
1284  	  */
1285  	 protected $tsa_timestamp = false;
1286  
1287  	 /**
1288  	  * Timestamping data.
1289  	  * @protected
1290  	  * @since 6.0.085 (2014-06-19)
1291  	  */
1292  	 protected $tsa_data = array();
1293  
1294  	 /**
1295  	  * Regular expression used to find blank characters (required for word-wrapping).
1296  	  * @protected
1297  	  * @since 4.6.006 (2009-04-28)
1298  	  */
1299  	 protected $re_spaces = '/[^\S\xa0]/';
1300  
1301  	 /**
1302  	  * Array of $re_spaces parts.
1303  	  * @protected
1304  	  * @since 5.5.011 (2010-07-09)
1305  	  */
1306  	 protected $re_space = array('p' => '[^\S\xa0]', 'm' => '');
1307  
1308  	 /**
1309  	  * Digital signature object ID.
1310  	  * @protected
1311  	  * @since 4.6.022 (2009-06-23)
1312  	  */
1313  	 protected $sig_obj_id = 0;
1314  
1315  	 /**
1316  	  * ID of page objects.
1317  	  * @protected
1318  	  * @since 4.7.000 (2009-08-29)
1319  	  */
1320  	 protected $page_obj_id = array();
1321  
1322  	 /**
1323  	  * List of form annotations IDs.
1324  	  * @protected
1325  	  * @since 4.8.000 (2009-09-07)
1326  	  */
1327  	 protected $form_obj_id = array();
1328  
1329  	 /**
1330  	  * 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.
1331  	  * @protected
1332  	  * @since 4.8.000 (2009-09-07)
1333  	  */
1334  	 protected $default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
1335  
1336  	 /**
1337  	  * Javascript objects array.
1338  	  * @protected
1339  	  * @since 4.8.000 (2009-09-07)
1340  	  */
1341  	 protected $js_objects = array();
1342  
1343  	 /**
1344  	  * Current form action (used during XHTML rendering).
1345  	  * @protected
1346  	  * @since 4.8.000 (2009-09-07)
1347  	  */
1348  	 protected $form_action = '';
1349  
1350  	 /**
1351  	  * Current form encryption type (used during XHTML rendering).
1352  	  * @protected
1353  	  * @since 4.8.000 (2009-09-07)
1354  	  */
1355  	 protected $form_enctype = 'application/x-www-form-urlencoded';
1356  
1357  	 /**
1358  	  * Current method to submit forms.
1359  	  * @protected
1360  	  * @since 4.8.000 (2009-09-07)
1361  	  */
1362  	 protected $form_mode = 'post';
1363  
1364  	 /**
1365  	  * List of fonts used on form fields (fontname => fontkey).
1366  	  * @protected
1367  	  * @since 4.8.001 (2009-09-09)
1368  	  */
1369  	 protected $annotation_fonts = array();
1370  
1371  	 /**
1372  	  * List of radio buttons parent objects.
1373  	  * @protected
1374  	  * @since 4.8.001 (2009-09-09)
1375  	  */
1376  	 protected $radiobutton_groups = array();
1377  
1378  	 /**
1379  	  * List of radio group objects IDs.
1380  	  * @protected
1381  	  * @since 4.8.001 (2009-09-09)
1382  	  */
1383  	 protected $radio_groups = array();
1384  
1385  	 /**
1386  	  * Text indentation value (used for text-indent CSS attribute).
1387  	  * @protected
1388  	  * @since 4.8.006 (2009-09-23)
1389  	  */
1390  	 protected $textindent = 0;
1391  
1392  	 /**
1393  	  * Store page number when startTransaction() is called.
1394  	  * @protected
1395  	  * @since 4.8.006 (2009-09-23)
1396  	  */
1397  	 protected $start_transaction_page = 0;
1398  
1399  	 /**
1400  	  * Store Y position when startTransaction() is called.
1401  	  * @protected
1402  	  * @since 4.9.001 (2010-03-28)
1403  	  */
1404  	 protected $start_transaction_y = 0;
1405  
1406  	 /**
1407  	  * True when we are printing the thead section on a new page.
1408  	  * @protected
1409  	  * @since 4.8.027 (2010-01-25)
1410  	  */
1411  	 protected $inthead = false;
1412  
1413  	 /**
1414  	  * Array of column measures (width, space, starting Y position).
1415  	  * @protected
1416  	  * @since 4.9.001 (2010-03-28)
1417  	  */
1418  	 protected $columns = array();
1419  
1420  	 /**
1421  	  * Number of colums.
1422  	  * @protected
1423  	  * @since 4.9.001 (2010-03-28)
1424  	  */
1425  	 protected $num_columns = 1;
1426  
1427  	 /**
1428  	  * Current column number.
1429  	  * @protected
1430  	  * @since 4.9.001 (2010-03-28)
1431  	  */
1432  	 protected $current_column = 0;
1433  
1434  	 /**
1435  	  * Starting page for columns.
1436  	  * @protected
1437  	  * @since 4.9.001 (2010-03-28)
1438  	  */
1439  	 protected $column_start_page = 0;
1440  
1441  	 /**
1442  	  * Maximum page and column selected.
1443  	  * @protected
1444  	  * @since 5.8.000 (2010-08-11)
1445  	  */
1446  	 protected $maxselcol = array('page' => 0, 'column' => 0);
1447  
1448  	 /**
1449  	  * Array of: X difference between table cell x start and starting page margin, cellspacing, cellpadding.
1450  	  * @protected
1451  	  * @since 5.8.000 (2010-08-11)
1452  	  */
1453  	 protected $colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
1454  
1455  	 /**
1456  	  * 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.
1457  	  * @protected
1458  	  * @since 4.9.008 (2010-04-03)
1459  	  */
1460  	 protected $textrendermode = 0;
1461  
1462  	 /**
1463  	  * Text stroke width in doc units.
1464  	  * @protected
1465  	  * @since 4.9.008 (2010-04-03)
1466  	  */
1467  	 protected $textstrokewidth = 0;
1468  
1469  	 /**
1470  	  * Current stroke color.
1471  	  * @protected
1472  	  * @since 4.9.008 (2010-04-03)
1473  	  */
1474  	 protected $strokecolor;
1475  
1476  	 /**
1477  	  * Default unit of measure for document.
1478  	  * @protected
1479  	  * @since 5.0.000 (2010-04-22)
1480  	  */
1481  	 protected $pdfunit = 'mm';
1482  
1483  	 /**
1484  	  * Boolean flag true when we are on TOC (Table Of Content) page.
1485  	  * @protected
1486  	  */
1487  	 protected $tocpage = false;
1488  
1489  	 /**
1490  	  * Boolean flag: if true convert vector images (SVG, EPS) to raster image using GD or ImageMagick library.
1491  	  * @protected
1492  	  * @since 5.0.000 (2010-04-26)
1493  	  */
1494  	 protected $rasterize_vector_images = false;
1495  
1496  	 /**
1497  	  * Boolean flag: if true enables font subsetting by default.
1498  	  * @protected
1499  	  * @since 5.3.002 (2010-06-07)
1500  	  */
1501  	 protected $font_subsetting = true;
1502  
1503  	 /**
1504  	  * Array of default graphic settings.
1505  	  * @protected
1506  	  * @since 5.5.008 (2010-07-02)
1507  	  */
1508  	 protected $default_graphic_vars = array();
1509  
1510  	 /**
1511  	  * Array of XObjects.
1512  	  * @protected
1513  	  * @since 5.8.014 (2010-08-23)
1514  	  */
1515  	 protected $xobjects = array();
1516  
1517  	 /**
1518  	  * Boolean value true when we are inside an XObject.
1519  	  * @protected
1520  	  * @since 5.8.017 (2010-08-24)
1521  	  */
1522  	 protected $inxobj = false;
1523  
1524  	 /**
1525  	  * Current XObject ID.
1526  	  * @protected
1527  	  * @since 5.8.017 (2010-08-24)
1528  	  */
1529  	 protected $xobjid = '';
1530  
1531  	 /**
1532  	  * Percentage of character stretching.
1533  	  * @protected
1534  	  * @since 5.9.000 (2010-09-29)
1535  	  */
1536  	 protected $font_stretching = 100;
1537  
1538  	 /**
1539  	  * Increases or decreases the space between characters in a text by the specified amount (tracking).
1540  	  * @protected
1541  	  * @since 5.9.000 (2010-09-29)
1542  	  */
1543  	 protected $font_spacing = 0;
1544  
1545  	 /**
1546  	  * Array of no-write regions.
1547  	  * ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right)
1548  	  * @protected
1549  	  * @since 5.9.003 (2010-10-14)
1550  	  */
1551  	 protected $page_regions = array();
1552  
1553  	 /**
1554  	  * Boolean value true when page region check is active.
1555  	  * @protected
1556  	  */
1557  	 protected $check_page_regions = true;
1558  
1559  	 /**
1560  	  * Array of PDF layers data.
1561  	  * @protected
1562  	  * @since 5.9.102 (2011-07-13)
1563  	  */
1564  	 protected $pdflayers = array();
1565  
1566  	 /**
1567  	  * A dictionary of names and corresponding destinations (Dests key on document Catalog).
1568  	  * @protected
1569  	  * @since 5.9.097 (2011-06-23)
1570  	  */
1571  	 protected $dests = array();
1572  
1573  	 /**
1574  	  * Object ID for Named Destinations
1575  	  * @protected
1576  	  * @since 5.9.097 (2011-06-23)
1577  	  */
1578  	 protected $n_dests;
1579  
1580  	 /**
1581  	  * Embedded Files Names
1582  	  * @protected
1583  	  * @since 5.9.204 (2013-01-23)
1584  	  */
1585  	 protected $efnames = array();
1586  
1587  	 /**
1588  	  * Directory used for the last SVG image.
1589  	  * @protected
1590  	  * @since 5.0.000 (2010-05-05)
1591  	  */
1592  	 protected $svgdir = '';
1593  
1594  	 /**
1595  	  *  Deafult unit of measure for SVG.
1596  	  * @protected
1597  	  * @since 5.0.000 (2010-05-02)
1598  	  */
1599  	 protected $svgunit = 'px';
1600  
1601  	 /**
1602  	  * Array of SVG gradients.
1603  	  * @protected
1604  	  * @since 5.0.000 (2010-05-02)
1605  	  */
1606  	 protected $svggradients = array();
1607  
1608  	 /**
1609  	  * ID of last SVG gradient.
1610  	  * @protected
1611  	  * @since 5.0.000 (2010-05-02)
1612  	  */
1613  	 protected $svggradientid = 0;
1614  
1615  	 /**
1616  	  * Boolean value true when in SVG defs group.
1617  	  * @protected
1618  	  * @since 5.0.000 (2010-05-02)
1619  	  */
1620  	 protected $svgdefsmode = false;
1621  
1622  	 /**
1623  	  * Array of SVG defs.
1624  	  * @protected
1625  	  * @since 5.0.000 (2010-05-02)
1626  	  */
1627  	 protected $svgdefs = array();
1628  
1629  	 /**
1630  	  * Boolean value true when in SVG clipPath tag.
1631  	  * @protected
1632  	  * @since 5.0.000 (2010-04-26)
1633  	  */
1634  	 protected $svgclipmode = false;
1635  
1636  	 /**
1637  	  * Array of SVG clipPath commands.
1638  	  * @protected
1639  	  * @since 5.0.000 (2010-05-02)
1640  	  */
1641  	 protected $svgclippaths = array();
1642  
1643  	 /**
1644  	  * Array of SVG clipPath tranformation matrix.
1645  	  * @protected
1646  	  * @since 5.8.022 (2010-08-31)
1647  	  */
1648  	 protected $svgcliptm = array();
1649  
1650  	 /**
1651  	  * ID of last SVG clipPath.
1652  	  * @protected
1653  	  * @since 5.0.000 (2010-05-02)
1654  	  */
1655  	 protected $svgclipid = 0;
1656  
1657  	 /**
1658  	  * SVG text.
1659  	  * @protected
1660  	  * @since 5.0.000 (2010-05-02)
1661  	  */
1662  	 protected $svgtext = '';
1663  
1664  	 /**
1665  	  * SVG text properties.
1666  	  * @protected
1667  	  * @since 5.8.013 (2010-08-23)
1668  	  */
1669  	 protected $svgtextmode = array();
1670  
1671  	 /**
1672  	  * Array of SVG properties.
1673  	  * @protected
1674  	  * @since 5.0.000 (2010-05-02)
1675  	  */
1676  	 protected $svgstyles = array(array(
1677  	 	 'alignment-baseline' => 'auto',
1678  	 	 'baseline-shift' => 'baseline',
1679  	 	 'clip' => 'auto',
1680  	 	 'clip-path' => 'none',
1681  	 	 'clip-rule' => 'nonzero',
1682  	 	 'color' => 'black',
1683  	 	 'color-interpolation' => 'sRGB',
1684  	 	 'color-interpolation-filters' => 'linearRGB',
1685  	 	 'color-profile' => 'auto',
1686  	 	 'color-rendering' => 'auto',
1687  	 	 'cursor' => 'auto',
1688  	 	 'direction' => 'ltr',
1689  	 	 'display' => 'inline',
1690  	 	 'dominant-baseline' => 'auto',
1691  	 	 'enable-background' => 'accumulate',
1692  	 	 'fill' => 'black',
1693  	 	 'fill-opacity' => 1,
1694  	 	 'fill-rule' => 'nonzero',
1695  	 	 'filter' => 'none',
1696  	 	 'flood-color' => 'black',
1697  	 	 'flood-opacity' => 1,
1698  	 	 'font' => '',
1699  	 	 'font-family' => 'helvetica',
1700  	 	 'font-size' => 'medium',
1701  	 	 'font-size-adjust' => 'none',
1702  	 	 'font-stretch' => 'normal',
1703  	 	 'font-style' => 'normal',
1704  	 	 'font-variant' => 'normal',
1705  	 	 'font-weight' => 'normal',
1706  	 	 'glyph-orientation-horizontal' => '0deg',
1707  	 	 'glyph-orientation-vertical' => 'auto',
1708  	 	 'image-rendering' => 'auto',
1709  	 	 'kerning' => 'auto',
1710  	 	 'letter-spacing' => 'normal',
1711  	 	 'lighting-color' => 'white',
1712  	 	 'marker' => '',
1713  	 	 'marker-end' => 'none',
1714  	 	 'marker-mid' => 'none',
1715  	 	 'marker-start' => 'none',
1716  	 	 'mask' => 'none',
1717  	 	 'opacity' => 1,
1718  	 	 'overflow' => 'auto',
1719  	 	 'pointer-events' => 'visiblePainted',
1720  	 	 'shape-rendering' => 'auto',
1721  	 	 'stop-color' => 'black',
1722  	 	 'stop-opacity' => 1,
1723  	 	 'stroke' => 'none',
1724  	 	 'stroke-dasharray' => 'none',
1725  	 	 'stroke-dashoffset' => 0,
1726  	 	 'stroke-linecap' => 'butt',
1727  	 	 'stroke-linejoin' => 'miter',
1728  	 	 'stroke-miterlimit' => 4,
1729  	 	 'stroke-opacity' => 1,
1730  	 	 'stroke-width' => 1,
1731  	 	 'text-anchor' => 'start',
1732  	 	 'text-decoration' => 'none',
1733  	 	 'text-rendering' => 'auto',
1734  	 	 'unicode-bidi' => 'normal',
1735  	 	 'visibility' => 'visible',
1736  	 	 'word-spacing' => 'normal',
1737  	 	 'writing-mode' => 'lr-tb',
1738  	 	 'text-color' => 'black',
1739  	 	 'transfmatrix' => array(1, 0, 0, 1, 0, 0)
1740  	 	 ));
1741  
1742  	 /**
1743  	  * If true force sRGB color profile for all document.
1744  	  * @protected
1745  	  * @since 5.9.121 (2011-09-28)
1746  	  */
1747  	 protected $force_srgb = false;
1748  
1749  	 /**
1750  	  * If true set the document to PDF/A mode.
1751  	  * @protected
1752  	  * @since 5.9.121 (2011-09-27)
1753  	  */
1754  	 protected $pdfa_mode = false;
1755  
1756  	 /**
1757  	  * version of PDF/A mode (1 - 3).
1758  	  * @protected
1759  	  * @since 6.2.26 (2019-03-12)
1760  	  */
1761  	 protected $pdfa_version = 1;
1762  
1763  	 /**
1764  	  * Document creation date-time
1765  	  * @protected
1766  	  * @since 5.9.152 (2012-03-22)
1767  	  */
1768  	 protected $doc_creation_timestamp;
1769  
1770  	 /**
1771  	  * Document modification date-time
1772  	  * @protected
1773  	  * @since 5.9.152 (2012-03-22)
1774  	  */
1775  	 protected $doc_modification_timestamp;
1776  
1777  	 /**
1778  	  * Custom XMP data.
1779  	  * @protected
1780  	  * @since 5.9.128 (2011-10-06)
1781  	  */
1782  	 protected $custom_xmp = '';
1783  
1784  	 /**
1785  	  * Custom XMP RDF data.
1786  	  * @protected
1787  	  * @since 6.3.0 (2019-09-19)
1788  	  */
1789  	 protected $custom_xmp_rdf = '';
1790  
1791  	 /**
1792  	  * Overprint mode array.
1793  	  * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1794  	  * @protected
1795  	  * @since 5.9.152 (2012-03-23)
1796  	  */
1797  	 protected $overprint = array('OP' => false, 'op' => false, 'OPM' => 0);
1798  
1799  	 /**
1800  	  * Alpha mode array.
1801  	  * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
1802  	  * @protected
1803  	  * @since 5.9.152 (2012-03-23)
1804  	  */
1805  	 protected $alpha = array('CA' => 1, 'ca' => 1, 'BM' => '/Normal', 'AIS' => false);
1806  
1807  	 /**
1808  	  * Define the page boundaries boxes to be set on document.
1809  	  * @protected
1810  	  * @since 5.9.152 (2012-03-23)
1811  	  */
1812  	 protected $page_boxes = array('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox');
1813  
1814  	 /**
1815  	  * If true print TCPDF meta link.
1816  	  * @protected
1817  	  * @since 5.9.152 (2012-03-23)
1818  	  */
1819  	 protected $tcpdflink = true;
1820  
1821  	 /**
1822  	  * Cache array for computed GD gamma values.
1823  	  * @protected
1824  	  * @since 5.9.1632 (2012-06-05)
1825  	  */
1826  	 protected $gdgammacache = array();
1827  
1828      /**
1829       * Cache array for file content
1830       * @protected
1831       * @var array
1832       * @sinde 6.3.5 (2020-09-28)
1833       */
1834  	 protected $fileContentCache = array();
1835  
1836  	 /**
1837  	  * Whether to allow local file path in image html tags, when prefixed with file://
1838  	  *
1839  	  * @var bool
1840  	  * @protected
1841  	  * @since 6.4 (2020-07-23)
1842  	  */
1843  	 protected $allowLocalFiles = false;
1844  
1845  	 //------------------------------------------------------------
1846  	 // METHODS
1847  	 //------------------------------------------------------------
1848  
1849  	 /**
1850  	  * This is the class constructor.
1851  	  * It allows to set up the page format, the orientation and the measure unit used in all the methods (except for the font sizes).
1852  	  *
1853  	  * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
1854  	  * @param $unit (string) 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.
1855  	  * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
1856  	  * @param $unicode (boolean) TRUE means that the input text is unicode (default = true)
1857  	  * @param $encoding (string) Charset encoding (used only when converting back html entities); default is UTF-8.
1858  	  * @param $diskcache (boolean) DEPRECATED FEATURE
1859  	  * @param $pdfa (integer) If not false, set the document to PDF/A mode and the good version (1 or 3).
1860  	  * @public
1861  	  * @see getPageSizeFromFormat(), setPageFormat()
1862  	  */
1863  	public function __construct($orientation='P', $unit='mm', $format='A4', $unicode=true, $encoding='UTF-8', $diskcache=false, $pdfa=false) {
1864  	 	 // set file ID for trailer
1865  	 	 $serformat = (is_array($format) ? json_encode($format) : $format);
1866  	 	 $this->file_id = md5(TCPDF_STATIC::getRandomSeed('TCPDF'.$orientation.$unit.$serformat.$encoding));
1867  	 	 $this->font_obj_ids = array();
1868  	 	 $this->page_obj_id = array();
1869  	 	 $this->form_obj_id = array();
1870  
1871  	 	 // set pdf/a mode
1872  	 	 if ($pdfa != false) {
1873  	 	 	 $this->pdfa_mode = true;
1874  	 	 	 $this->pdfa_version = $pdfa;  // 1 or 3
1875  	 	 } else
1876  	 	 	 $this->pdfa_mode = false;
1877  
1878  	 	 $this->force_srgb = false;
1879  	 	 // set language direction
1880  	 	 $this->rtl = false;
1881  	 	 $this->tmprtl = false;
1882  	 	 // some checks
1883  	 	 $this->_dochecks();
1884  	 	 // initialization of properties
1885  	 	 $this->isunicode = $unicode;
1886  	 	 $this->page = 0;
1887  	 	 $this->transfmrk[0] = array();
1888  	 	 $this->pagedim = array();
1889  	 	 $this->n = 2;
1890  	 	 $this->buffer = '';
1891  	 	 $this->pages = array();
1892  	 	 $this->state = 0;
1893  	 	 $this->fonts = array();
1894  	 	 $this->FontFiles = array();
1895  	 	 $this->diffs = array();
1896  	 	 $this->images = array();
1897  	 	 $this->links = array();
1898  	 	 $this->gradients = array();
1899  	 	 $this->InFooter = false;
1900  	 	 $this->lasth = 0;
1901  	 	 $this->FontFamily = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
1902  	 	 $this->FontStyle = '';
1903  	 	 $this->FontSizePt = 12;
1904  	 	 $this->underline = false;
1905  	 	 $this->overline = false;
1906  	 	 $this->linethrough = false;
1907  	 	 $this->DrawColor = '0 G';
1908  	 	 $this->FillColor = '0 g';
1909  	 	 $this->TextColor = '0 g';
1910  	 	 $this->ColorFlag = false;
1911  	 	 $this->pdflayers = array();
1912  	 	 // encryption values
1913  	 	 $this->encrypted = false;
1914  	 	 $this->last_enc_key = '';
1915  	 	 // standard Unicode fonts
1916  	 	 $this->CoreFonts = array(
1917  	 	 	 'courier'=>'Courier',
1918  	 	 	 'courierB'=>'Courier-Bold',
1919  	 	 	 'courierI'=>'Courier-Oblique',
1920  	 	 	 'courierBI'=>'Courier-BoldOblique',
1921  	 	 	 'helvetica'=>'Helvetica',
1922  	 	 	 'helveticaB'=>'Helvetica-Bold',
1923  	 	 	 'helveticaI'=>'Helvetica-Oblique',
1924  	 	 	 'helveticaBI'=>'Helvetica-BoldOblique',
1925  	 	 	 'times'=>'Times-Roman',
1926  	 	 	 'timesB'=>'Times-Bold',
1927  	 	 	 'timesI'=>'Times-Italic',
1928  	 	 	 'timesBI'=>'Times-BoldItalic',
1929  	 	 	 'symbol'=>'Symbol',
1930  	 	 	 'zapfdingbats'=>'ZapfDingbats'
1931  	 	 );
1932  	 	 // set scale factor
1933  	 	 $this->setPageUnit($unit);
1934  	 	 // set page format and orientation
1935  	 	 $this->setPageFormat($format, $orientation);
1936  	 	 // page margins (1 cm)
1937  	 	 $margin = 28.35 / $this->k;
1938  	 	 $this->SetMargins($margin, $margin);
1939  	 	 $this->clMargin = $this->lMargin;
1940  	 	 $this->crMargin = $this->rMargin;
1941  	 	 // internal cell padding
1942  	 	 $cpadding = $margin / 10;
1943  	 	 $this->setCellPaddings($cpadding, 0, $cpadding, 0);
1944  	 	 // cell margins
1945  	 	 $this->setCellMargins(0, 0, 0, 0);
1946  	 	 // line width (0.2 mm)
1947  	 	 $this->LineWidth = 0.57 / $this->k;
1948  	 	 $this->linestyleWidth = sprintf('%F w', ($this->LineWidth * $this->k));
1949  	 	 $this->linestyleCap = '0 J';
1950  	 	 $this->linestyleJoin = '0 j';
1951  	 	 $this->linestyleDash = '[] 0 d';
1952  	 	 // automatic page break
1953  	 	 $this->SetAutoPageBreak(true, (2 * $margin));
1954  	 	 // full width display mode
1955  	 	 $this->SetDisplayMode('fullwidth');
1956  	 	 // compression
1957  	 	 $this->SetCompression();
1958  	 	 // set default PDF version number
1959  	 	 $this->setPDFVersion();
1960  	 	 $this->tcpdflink = true;
1961  	 	 $this->encoding = $encoding;
1962  	 	 $this->HREF = array();
1963  	 	 $this->getFontsList();
1964  	 	 $this->fgcolor = array('R' => 0, 'G' => 0, 'B' => 0);
1965  	 	 $this->strokecolor = array('R' => 0, 'G' => 0, 'B' => 0);
1966  	 	 $this->bgcolor = array('R' => 255, 'G' => 255, 'B' => 255);
1967  	 	 $this->extgstates = array();
1968  	 	 $this->setTextShadow();
1969  	 	 // signature
1970  	 	 $this->sign = false;
1971  	 	 $this->tsa_timestamp = false;
1972  	 	 $this->tsa_data = array();
1973  	 	 $this->signature_appearance = array('page' => 1, 'rect' => '0 0 0 0', 'name' => 'Signature');
1974  	 	 $this->empty_signature_appearance = array();
1975  	 	 // user's rights
1976  	 	 $this->ur['enabled'] = false;
1977  	 	 $this->ur['document'] = '/FullSave';
1978  	 	 $this->ur['annots'] = '/Create/Delete/Modify/Copy/Import/Export';
1979  	 	 $this->ur['form'] = '/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate';
1980  	 	 $this->ur['signature'] = '/Modify';
1981  	 	 $this->ur['ef'] = '/Create/Delete/Modify/Import';
1982  	 	 $this->ur['formex'] = '';
1983  	 	 // set default JPEG quality
1984  	 	 $this->jpeg_quality = 75;
1985  	 	 // initialize some settings
1986  	 	 TCPDF_FONTS::utf8Bidi(array(), '', false, $this->isunicode, $this->CurrentFont);
1987  	 	 // set default font
1988  	 	 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
1989  	 	 $this->setHeaderFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1990  	 	 $this->setFooterFont(array($this->FontFamily, $this->FontStyle, $this->FontSizePt));
1991  	 	 // check if PCRE Unicode support is enabled
1992  	 	 if ($this->isunicode AND (@preg_match('/\pL/u', 'a') == 1)) {
1993  	 	 	 // PCRE unicode support is turned ON
1994  	 	 	 // \s     : any whitespace character
1995  	 	 	 // \p{Z}  : any separator
1996  	 	 	 // \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
1997  	 	 	 // \xa0   : Unicode Character 'NO-BREAK SPACE' (U+00A0)
1998  	 	 	 //$this->setSpacesRE('/(?!\xa0)[\s\p{Z}\p{Lo}]/u');
1999  	 	 	 $this->setSpacesRE('/(?!\xa0)[\s\p{Z}]/u');
2000  	 	 } else {
2001  	 	 	 // PCRE unicode support is turned OFF
2002  	 	 	 $this->setSpacesRE('/[^\S\xa0]/');
2003  	 	 }
2004  	 	 $this->default_form_prop = array('lineWidth'=>1, 'borderStyle'=>'solid', 'fillColor'=>array(255, 255, 255), 'strokeColor'=>array(128, 128, 128));
2005  	 	 // set document creation and modification timestamp
2006  	 	 $this->doc_creation_timestamp = time();
2007  	 	 $this->doc_modification_timestamp = $this->doc_creation_timestamp;
2008  	 	 // get default graphic vars
2009  	 	 $this->default_graphic_vars = $this->getGraphicVars();
2010  	 	 $this->header_xobj_autoreset = false;
2011  	 	 $this->custom_xmp = '';
2012  	 	 $this->custom_xmp_rdf = '';
2013  	 	 // Call cleanup method after script execution finishes or exit() is called.
2014  	 	 // NOTE: This will not be executed if the process is killed with a SIGTERM or SIGKILL signal.
2015  	 	 register_shutdown_function(array($this, '_destroy'), true);
2016  	 }
2017  
2018  	 /**
2019  	  * Default destructor.
2020  	  * @public
2021  	  * @since 1.53.0.TC016
2022  	  */
2023  	public function __destruct() {
2024  	 	 // cleanup
2025  	 	 $this->_destroy(true);
2026  	 }
2027  
2028  	 /**
2029  	  * Set the units of measure for the document.
2030  	  * @param $unit (string) 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.
2031  	  * @public
2032  	  * @since 3.0.015 (2008-06-06)
2033  	  */
2034  	public function setPageUnit($unit) {
2035  	 	 $unit = strtolower($unit);
2036  	 	 //Set scale factor
2037  	 	 switch ($unit) {
2038  	 	 	 // points
2039  	 	 	 case 'px':
2040  	 	 	 case 'pt': {
2041  	 	 	 	 $this->k = 1;
2042  	 	 	 	 break;
2043  	 	 	 }
2044  	 	 	 // millimeters
2045  	 	 	 case 'mm': {
2046  	 	 	 	 $this->k = $this->dpi / 25.4;
2047  	 	 	 	 break;
2048  	 	 	 }
2049  	 	 	 // centimeters
2050  	 	 	 case 'cm': {
2051  	 	 	 	 $this->k = $this->dpi / 2.54;
2052  	 	 	 	 break;
2053  	 	 	 }
2054  	 	 	 // inches
2055  	 	 	 case 'in': {
2056  	 	 	 	 $this->k = $this->dpi;
2057  	 	 	 	 break;
2058  	 	 	 }
2059  	 	 	 // unsupported unit
2060  	 	 	 default : {
2061  	 	 	 	 $this->Error('Incorrect unit: '.$unit);
2062  	 	 	 	 break;
2063  	 	 	 }
2064  	 	 }
2065  	 	 $this->pdfunit = $unit;
2066  	 	 if (isset($this->CurOrientation)) {
2067  	 	 	 $this->setPageOrientation($this->CurOrientation);
2068  	 	 }
2069  	 }
2070  
2071  	 /**
2072  	  * Change the format of the current page
2073  	  * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() documentation or an array of two numbers (width, height) or an array containing the following measures and options:<ul>
2074  	  * <li>['format'] = page format name (one of the above);</li>
2075  	  * <li>['Rotate'] : The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li>
2076  	  * <li>['PZ'] : The page's preferred zoom (magnification) factor.</li>
2077  	  * <li>['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed:</li>
2078  	  * <li>['MediaBox']['llx'] : lower-left x coordinate</li>
2079  	  * <li>['MediaBox']['lly'] : lower-left y coordinate</li>
2080  	  * <li>['MediaBox']['urx'] : upper-right x coordinate</li>
2081  	  * <li>['MediaBox']['ury'] : upper-right y coordinate</li>
2082  	  * <li>['CropBox'] : the visible region of default user space:</li>
2083  	  * <li>['CropBox']['llx'] : lower-left x coordinate</li>
2084  	  * <li>['CropBox']['lly'] : lower-left y coordinate</li>
2085  	  * <li>['CropBox']['urx'] : upper-right x coordinate</li>
2086  	  * <li>['CropBox']['ury'] : upper-right y coordinate</li>
2087  	  * <li>['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment:</li>
2088  	  * <li>['BleedBox']['llx'] : lower-left x coordinate</li>
2089  	  * <li>['BleedBox']['lly'] : lower-left y coordinate</li>
2090  	  * <li>['BleedBox']['urx'] : upper-right x coordinate</li>
2091  	  * <li>['BleedBox']['ury'] : upper-right y coordinate</li>
2092  	  * <li>['TrimBox'] : the intended dimensions of the finished page after trimming:</li>
2093  	  * <li>['TrimBox']['llx'] : lower-left x coordinate</li>
2094  	  * <li>['TrimBox']['lly'] : lower-left y coordinate</li>
2095  	  * <li>['TrimBox']['urx'] : upper-right x coordinate</li>
2096  	  * <li>['TrimBox']['ury'] : upper-right y coordinate</li>
2097  	  * <li>['ArtBox'] : the extent of the page's meaningful content:</li>
2098  	  * <li>['ArtBox']['llx'] : lower-left x coordinate</li>
2099  	  * <li>['ArtBox']['lly'] : lower-left y coordinate</li>
2100  	  * <li>['ArtBox']['urx'] : upper-right x coordinate</li>
2101  	  * <li>['ArtBox']['ury'] : upper-right y coordinate</li>
2102  	  * <li>['BoxColorInfo'] :specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for each of the possible page boundaries other than the MediaBox:</li>
2103  	  * <li>['BoxColorInfo'][BOXTYPE]['C'] : an array of three numbers in the range 0-255, representing the components in the DeviceRGB colour space.</li>
2104  	  * <li>['BoxColorInfo'][BOXTYPE]['W'] : the guideline width in default user units</li>
2105  	  * <li>['BoxColorInfo'][BOXTYPE]['S'] : the guideline style: S = Solid; D = Dashed</li>
2106  	  * <li>['BoxColorInfo'][BOXTYPE]['D'] : dash array defining a pattern of dashes and gaps to be used in drawing dashed guidelines</li>
2107  	  * <li>['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation</li>
2108  	  * <li>['trans']['Dur'] : The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li>
2109  	  * <li>['trans']['S'] : transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li>
2110  	  * <li>['trans']['D'] : The duration of the transition effect, in seconds.</li>
2111  	  * <li>['trans']['Dm'] : (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li>
2112  	  * <li>['trans']['M'] : (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li>
2113  	  * <li>['trans']['Di'] : (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li>
2114  	  * <li>['trans']['SS'] : (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0.</li>
2115  	  * <li>['trans']['B'] : (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li>
2116  	  * </ul>
2117  	  * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul>
2118  	  * <li>P or Portrait (default)</li>
2119  	  * <li>L or Landscape</li>
2120  	  * <li>'' (empty string) for automatic orientation</li>
2121  	  * </ul>
2122  	  * @protected
2123  	  * @since 3.0.015 (2008-06-06)
2124  	  * @see getPageSizeFromFormat()
2125  	  */
2126  	protected function setPageFormat($format, $orientation='P') {
2127  	 	 if (!empty($format) AND isset($this->pagedim[$this->page])) {
2128  	 	 	 // remove inherited values
2129  	 	 	 unset($this->pagedim[$this->page]);
2130  	 	 }
2131  	 	 if (is_string($format)) {
2132  	 	 	 // get page measures from format name
2133  	 	 	 $pf = TCPDF_STATIC::getPageSizeFromFormat($format);
2134  	 	 	 $this->fwPt = $pf[0];
2135  	 	 	 $this->fhPt = $pf[1];
2136  	 	 } else {
2137  	 	 	 // the boundaries of the physical medium on which the page shall be displayed or printed
2138  	 	 	 if (isset($format['MediaBox'])) {
2139  	 	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', $format['MediaBox']['llx'], $format['MediaBox']['lly'], $format['MediaBox']['urx'], $format['MediaBox']['ury'], false, $this->k, $this->pagedim);
2140  	 	 	 	 $this->fwPt = (($format['MediaBox']['urx'] - $format['MediaBox']['llx']) * $this->k);
2141  	 	 	 	 $this->fhPt = (($format['MediaBox']['ury'] - $format['MediaBox']['lly']) * $this->k);
2142  	 	 	 } else {
2143  	 	 	 	 if (isset($format[0]) AND is_numeric($format[0]) AND isset($format[1]) AND is_numeric($format[1])) {
2144  	 	 	 	 	 $pf = array(($format[0] * $this->k), ($format[1] * $this->k));
2145  	 	 	 	 } else {
2146  	 	 	 	 	 if (!isset($format['format'])) {
2147  	 	 	 	 	 	 // default value
2148  	 	 	 	 	 	 $format['format'] = 'A4';
2149  	 	 	 	 	 }
2150  	 	 	 	 	 $pf = TCPDF_STATIC::getPageSizeFromFormat($format['format']);
2151  	 	 	 	 }
2152  	 	 	 	 $this->fwPt = $pf[0];
2153  	 	 	 	 $this->fhPt = $pf[1];
2154  	 	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2155  	 	 	 }
2156  	 	 	 // the visible region of default user space
2157  	 	 	 if (isset($format['CropBox'])) {
2158  	 	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $format['CropBox']['llx'], $format['CropBox']['lly'], $format['CropBox']['urx'], $format['CropBox']['ury'], false, $this->k, $this->pagedim);
2159  	 	 	 }
2160  	 	 	 // the region to which the contents of the page shall be clipped when output in a production environment
2161  	 	 	 if (isset($format['BleedBox'])) {
2162  	 	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $format['BleedBox']['llx'], $format['BleedBox']['lly'], $format['BleedBox']['urx'], $format['BleedBox']['ury'], false, $this->k, $this->pagedim);
2163  	 	 	 }
2164  	 	 	 // the intended dimensions of the finished page after trimming
2165  	 	 	 if (isset($format['TrimBox'])) {
2166  	 	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $format['TrimBox']['llx'], $format['TrimBox']['lly'], $format['TrimBox']['urx'], $format['TrimBox']['ury'], false, $this->k, $this->pagedim);
2167  	 	 	 }
2168  	 	 	 // the page's meaningful content (including potential white space)
2169  	 	 	 if (isset($format['ArtBox'])) {
2170  	 	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $format['ArtBox']['llx'], $format['ArtBox']['lly'], $format['ArtBox']['urx'], $format['ArtBox']['ury'], false, $this->k, $this->pagedim);
2171  	 	 	 }
2172  	 	 	 // specify the colours and other visual characteristics that should be used in displaying guidelines on the screen for the various page boundaries
2173  	 	 	 if (isset($format['BoxColorInfo'])) {
2174  	 	 	 	 $this->pagedim[$this->page]['BoxColorInfo'] = $format['BoxColorInfo'];
2175  	 	 	 }
2176  	 	 	 if (isset($format['Rotate']) AND (($format['Rotate'] % 90) == 0)) {
2177  	 	 	 	 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2178  	 	 	 	 $this->pagedim[$this->page]['Rotate'] = intval($format['Rotate']);
2179  	 	 	 }
2180  	 	 	 if (isset($format['PZ'])) {
2181  	 	 	 	 // The page's preferred zoom (magnification) factor
2182  	 	 	 	 $this->pagedim[$this->page]['PZ'] = floatval($format['PZ']);
2183  	 	 	 }
2184  	 	 	 if (isset($format['trans'])) {
2185  	 	 	 	 // The style and duration of the visual transition to use when moving from another page to the given page during a presentation
2186  	 	 	 	 if (isset($format['trans']['Dur'])) {
2187  	 	 	 	 	 // The page's display duration
2188  	 	 	 	 	 $this->pagedim[$this->page]['trans']['Dur'] = floatval($format['trans']['Dur']);
2189  	 	 	 	 }
2190  	 	 	 	 $stansition_styles = array('Split', 'Blinds', 'Box', 'Wipe', 'Dissolve', 'Glitter', 'R', 'Fly', 'Push', 'Cover', 'Uncover', 'Fade');
2191  	 	 	 	 if (isset($format['trans']['S']) AND in_array($format['trans']['S'], $stansition_styles)) {
2192  	 	 	 	 	 // The transition style that shall be used when moving to this page from another during a presentation
2193  	 	 	 	 	 $this->pagedim[$this->page]['trans']['S'] = $format['trans']['S'];
2194  	 	 	 	 	 $valid_effect = array('Split', 'Blinds');
2195  	 	 	 	 	 $valid_vals = array('H', 'V');
2196  	 	 	 	 	 if (isset($format['trans']['Dm']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['Dm'], $valid_vals)) {
2197  	 	 	 	 	 	 $this->pagedim[$this->page]['trans']['Dm'] = $format['trans']['Dm'];
2198  	 	 	 	 	 }
2199  	 	 	 	 	 $valid_effect = array('Split', 'Box', 'Fly');
2200  	 	 	 	 	 $valid_vals = array('I', 'O');
2201  	 	 	 	 	 if (isset($format['trans']['M']) AND in_array($format['trans']['S'], $valid_effect) AND in_array($format['trans']['M'], $valid_vals)) {
2202  	 	 	 	 	 	 $this->pagedim[$this->page]['trans']['M'] = $format['trans']['M'];
2203  	 	 	 	 	 }
2204  	 	 	 	 	 $valid_effect = array('Wipe', 'Glitter', 'Fly', 'Cover', 'Uncover', 'Push');
2205  	 	 	 	 	 if (isset($format['trans']['Di']) AND in_array($format['trans']['S'], $valid_effect)) {
2206  	 	 	 	 	 	 if (((($format['trans']['Di'] == 90) OR ($format['trans']['Di'] == 180)) AND ($format['trans']['S'] == 'Wipe'))
2207  	 	 	 	 	 	 	 OR (($format['trans']['Di'] == 315) AND ($format['trans']['S'] == 'Glitter'))
2208  	 	 	 	 	 	 	 OR (($format['trans']['Di'] == 0) OR ($format['trans']['Di'] == 270))) {
2209  	 	 	 	 	 	 	 $this->pagedim[$this->page]['trans']['Di'] = intval($format['trans']['Di']);
2210  	 	 	 	 	 	 }
2211  	 	 	 	 	 }
2212  	 	 	 	 	 if (isset($format['trans']['SS']) AND ($format['trans']['S'] == 'Fly')) {
2213  	 	 	 	 	 	 $this->pagedim[$this->page]['trans']['SS'] = floatval($format['trans']['SS']);
2214  	 	 	 	 	 }
2215  	 	 	 	 	 if (isset($format['trans']['B']) AND ($format['trans']['B'] === true) AND ($format['trans']['S'] == 'Fly')) {
2216  	 	 	 	 	 	 $this->pagedim[$this->page]['trans']['B'] = 'true';
2217  	 	 	 	 	 }
2218  	 	 	 	 } else {
2219  	 	 	 	 	 $this->pagedim[$this->page]['trans']['S'] = 'R';
2220  	 	 	 	 }
2221  	 	 	 	 if (isset($format['trans']['D'])) {
2222  	 	 	 	 	 // The duration of the transition effect, in seconds
2223  	 	 	 	 	 $this->pagedim[$this->page]['trans']['D'] = floatval($format['trans']['D']);
2224  	 	 	 	 } else {
2225  	 	 	 	 	 $this->pagedim[$this->page]['trans']['D'] = 1;
2226  	 	 	 	 }
2227  	 	 	 }
2228  	 	 }
2229  	 	 $this->setPageOrientation($orientation);
2230  	 }
2231  
2232  	 /**
2233  	  * Set page orientation.
2234  	  * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or Portrait (default)</li><li>L or Landscape</li><li>'' (empty string) for automatic orientation</li></ul>
2235  	  * @param $autopagebreak (boolean) Boolean indicating if auto-page-break mode should be on or off.
2236  	  * @param $bottommargin (float) bottom margin of the page.
2237  	  * @public
2238  	  * @since 3.0.015 (2008-06-06)
2239  	  */
2240  	public function setPageOrientation($orientation, $autopagebreak='', $bottommargin='') {
2241  	 	 if (!isset($this->pagedim[$this->page]['MediaBox'])) {
2242  	 	 	 // the boundaries of the physical medium on which the page shall be displayed or printed
2243  	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'MediaBox', 0, 0, $this->fwPt, $this->fhPt, true, $this->k, $this->pagedim);
2244  	 	 }
2245  	 	 if (!isset($this->pagedim[$this->page]['CropBox'])) {
2246  	 	 	 // the visible region of default user space
2247  	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'CropBox', $this->pagedim[$this->page]['MediaBox']['llx'], $this->pagedim[$this->page]['MediaBox']['lly'], $this->pagedim[$this->page]['MediaBox']['urx'], $this->pagedim[$this->page]['MediaBox']['ury'], true, $this->k, $this->pagedim);
2248  	 	 }
2249  	 	 if (!isset($this->pagedim[$this->page]['BleedBox'])) {
2250  	 	 	 // the region to which the contents of the page shall be clipped when output in a production environment
2251  	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'BleedBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2252  	 	 }
2253  	 	 if (!isset($this->pagedim[$this->page]['TrimBox'])) {
2254  	 	 	 // the intended dimensions of the finished page after trimming
2255  	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'TrimBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2256  	 	 }
2257  	 	 if (!isset($this->pagedim[$this->page]['ArtBox'])) {
2258  	 	 	 // the page's meaningful content (including potential white space)
2259  	 	 	 $this->pagedim = TCPDF_STATIC::setPageBoxes($this->page, 'ArtBox', $this->pagedim[$this->page]['CropBox']['llx'], $this->pagedim[$this->page]['CropBox']['lly'], $this->pagedim[$this->page]['CropBox']['urx'], $this->pagedim[$this->page]['CropBox']['ury'], true, $this->k, $this->pagedim);
2260  	 	 }
2261  	 	 if (!isset($this->pagedim[$this->page]['Rotate'])) {
2262  	 	 	 // The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.
2263  	 	 	 $this->pagedim[$this->page]['Rotate'] = 0;
2264  	 	 }
2265  	 	 if (!isset($this->pagedim[$this->page]['PZ'])) {
2266  	 	 	 // The page's preferred zoom (magnification) factor
2267  	 	 	 $this->pagedim[$this->page]['PZ'] = 1;
2268  	 	 }
2269  	 	 if ($this->fwPt > $this->fhPt) {
2270  	 	 	 // landscape
2271  	 	 	 $default_orientation = 'L';
2272  	 	 } else {
2273  	 	 	 // portrait
2274  	 	 	 $default_orientation = 'P';
2275  	 	 }
2276  	 	 $valid_orientations = array('P', 'L');
2277  	 	 if (empty($orientation)) {
2278  	 	 	 $orientation = $default_orientation;
2279  	 	 } else {
2280  	 	 	 $orientation = strtoupper($orientation[0]);
2281  	 	 }
2282  	 	 if (in_array($orientation, $valid_orientations) AND ($orientation != $default_orientation)) {
2283  	 	 	 $this->CurOrientation = $orientation;
2284  	 	 	 $this->wPt = $this->fhPt;
2285  	 	 	 $this->hPt = $this->fwPt;
2286  	 	 } else {
2287  	 	 	 $this->CurOrientation = $default_orientation;
2288  	 	 	 $this->wPt = $this->fwPt;
2289  	 	 	 $this->hPt = $this->fhPt;
2290  	 	 }
2291  	 	 if ((abs($this->pagedim[$this->page]['MediaBox']['urx'] - $this->hPt) < $this->feps) AND (abs($this->pagedim[$this->page]['MediaBox']['ury'] - $this->wPt) < $this->feps)){
2292  	 	 	 // swap X and Y coordinates (change page orientation)
2293  	 	 	 $this->pagedim = TCPDF_STATIC::swapPageBoxCoordinates($this->page, $this->pagedim);
2294  	 	 }
2295  	 	 $this->w = ($this->wPt / $this->k);
2296  	 	 $this->h = ($this->hPt / $this->k);
2297  	 	 if (TCPDF_STATIC::empty_string($autopagebreak)) {
2298  	 	 	 if (isset($this->AutoPageBreak)) {
2299  	 	 	 	 $autopagebreak = $this->AutoPageBreak;
2300  	 	 	 } else {
2301  	 	 	 	 $autopagebreak = true;
2302  	 	 	 }
2303  	 	 }
2304  	 	 if (TCPDF_STATIC::empty_string($bottommargin)) {
2305  	 	 	 if (isset($this->bMargin)) {
2306  	 	 	 	 $bottommargin = $this->bMargin;
2307  	 	 	 } else {
2308  	 	 	 	 // default value = 2 cm
2309  	 	 	 	 $bottommargin = 2 * 28.35 / $this->k;
2310  	 	 	 }
2311  	 	 }
2312  	 	 $this->SetAutoPageBreak($autopagebreak, $bottommargin);
2313  	 	 // store page dimensions
2314  	 	 $this->pagedim[$this->page]['w'] = $this->wPt;
2315  	 	 $this->pagedim[$this->page]['h'] = $this->hPt;
2316  	 	 $this->pagedim[$this->page]['wk'] = $this->w;
2317  	 	 $this->pagedim[$this->page]['hk'] = $this->h;
2318  	 	 $this->pagedim[$this->page]['tm'] = $this->tMargin;
2319  	 	 $this->pagedim[$this->page]['bm'] = $bottommargin;
2320  	 	 $this->pagedim[$this->page]['lm'] = $this->lMargin;
2321  	 	 $this->pagedim[$this->page]['rm'] = $this->rMargin;
2322  	 	 $this->pagedim[$this->page]['pb'] = $autopagebreak;
2323  	 	 $this->pagedim[$this->page]['or'] = $this->CurOrientation;
2324  	 	 $this->pagedim[$this->page]['olm'] = $this->original_lMargin;
2325  	 	 $this->pagedim[$this->page]['orm'] = $this->original_rMargin;
2326  	 }
2327  
2328  	 /**
2329  	  * Set regular expression to detect withespaces or word separators.
2330  	  * The pattern delimiter must be the forward-slash character "/".
2331  	  * Some example patterns are:
2332  	  * <pre>
2333  	  * Non-Unicode or missing PCRE unicode support: "/[^\S\xa0]/"
2334  	  * Unicode and PCRE unicode support: "/(?!\xa0)[\s\p{Z}]/u"
2335  	  * Unicode and PCRE unicode support in Chinese mode: "/(?!\xa0)[\s\p{Z}\p{Lo}]/u"
2336  	  * if PCRE unicode support is turned ON ("\P" is the negate class of "\p"):
2337  	  *      \s     : any whitespace character
2338  	  *      \p{Z}  : any separator
2339  	  *      \p{Lo} : Unicode letter or ideograph that does not have lowercase and uppercase variants. Is used to chunk chinese words.
2340  	  *      \xa0   : Unicode Character 'NO-BREAK SPACE' (U+00A0)
2341  	  * </pre>
2342  	  * @param $re (string) regular expression (leave empty for default).
2343  	  * @public
2344  	  * @since 4.6.016 (2009-06-15)
2345  	  */
2346  	public function setSpacesRE($re='/[^\S\xa0]/') {
2347  	 	 $this->re_spaces = $re;
2348  	 	 $re_parts = explode('/', $re);
2349  	 	 // get pattern parts
2350  	 	 $this->re_space = array();
2351  	 	 if (isset($re_parts[1]) AND !empty($re_parts[1])) {
2352  	 	 	 $this->re_space['p'] = $re_parts[1];
2353  	 	 } else {
2354  	 	 	 $this->re_space['p'] = '[\s]';
2355  	 	 }
2356  	 	 // set pattern modifiers
2357  	 	 if (isset($re_parts[2]) AND !empty($re_parts[2])) {
2358  	 	 	 $this->re_space['m'] = $re_parts[2];
2359  	 	 } else {
2360  	 	 	 $this->re_space['m'] = '';
2361  	 	 }
2362  	 }
2363  
2364  	 /**
2365  	  * Enable or disable Right-To-Left language mode
2366  	  * @param $enable (Boolean) if true enable Right-To-Left language mode.
2367  	  * @param $resetx (Boolean) if true reset the X position on direction change.
2368  	  * @public
2369  	  * @since 2.0.000 (2008-01-03)
2370  	  */
2371  	public function setRTL($enable, $resetx=true) {
2372  	 	 $enable = $enable ? true : false;
2373  	 	 $resetx = ($resetx AND ($enable != $this->rtl));
2374  	 	 $this->rtl = $enable;
2375  	 	 $this->tmprtl = false;
2376  	 	 if ($resetx) {
2377  	 	 	 $this->Ln(0);
2378  	 	 }
2379  	 }
2380  
2381  	 /**
2382  	  * Return the RTL status
2383  	  * @return boolean
2384  	  * @public
2385  	  * @since 4.0.012 (2008-07-24)
2386  	  */
2387  	public function getRTL() {
2388  	 	 return $this->rtl;
2389  	 }
2390  
2391  	 /**
2392  	  * Force temporary RTL language direction
2393  	  * @param $mode (mixed) can be false, 'L' for LTR or 'R' for RTL
2394  	  * @public
2395  	  * @since 2.1.000 (2008-01-09)
2396  	  */
2397  	public function setTempRTL($mode) {
2398  	 	 $newmode = false;
2399  	 	 switch (strtoupper($mode)) {
2400  	 	 	 case 'LTR':
2401  	 	 	 case 'L': {
2402  	 	 	 	 if ($this->rtl) {
2403  	 	 	 	 	 $newmode = 'L';
2404  	 	 	 	 }
2405  	 	 	 	 break;
2406  	 	 	 }
2407  	 	 	 case 'RTL':
2408  	 	 	 case 'R': {
2409  	 	 	 	 if (!$this->rtl) {
2410  	 	 	 	 	 $newmode = 'R';
2411  	 	 	 	 }
2412  	 	 	 	 break;
2413  	 	 	 }
2414  	 	 	 case false:
2415  	 	 	 default: {
2416  	 	 	 	 $newmode = false;
2417  	 	 	 	 break;
2418  	 	 	 }
2419  	 	 }
2420  	 	 $this->tmprtl = $newmode;
2421  	 }
2422  
2423  	 /**
2424  	  * Return the current temporary RTL status
2425  	  * @return boolean
2426  	  * @public
2427  	  * @since 4.8.014 (2009-11-04)
2428  	  */
2429  	public function isRTLTextDir() {
2430  	 	 return ($this->rtl OR ($this->tmprtl == 'R'));
2431  	 }
2432  
2433  	 /**
2434  	  * Set the last cell height.
2435  	  * @param $h (float) cell height.
2436  	  * @author Nicola Asuni
2437  	  * @public
2438  	  * @since 1.53.0.TC034
2439  	  */
2440  	public function setLastH($h) {
2441  	 	 $this->lasth = $h;
2442  	 }
2443  
2444  	 /**
2445  	  * Return the cell height
2446  	  * @param $fontsize (int) Font size in internal units
2447  	  * @param $padding (boolean) If true add cell padding
2448  	  * @public
2449  	  */
2450  	public function getCellHeight($fontsize, $padding=TRUE) {
2451  	 	 $height = ($fontsize * $this->cell_height_ratio);
2452  	 	 if ($padding) {
2453  	 	 	 $height += ($this->cell_padding['T'] + $this->cell_padding['B']);
2454  	 	 }
2455  	 	 return round($height, 6);
2456  	 }
2457  
2458  	 /**
2459  	  * Reset the last cell height.
2460  	  * @public
2461  	  * @since 5.9.000 (2010-10-03)
2462  	  */
2463  	public function resetLastH() {
2464  	 	 $this->lasth = $this->getCellHeight($this->FontSize);
2465  	 }
2466  
2467  	 /**
2468  	  * Get the last cell height.
2469  	  * @return last cell height
2470  	  * @public
2471  	  * @since 4.0.017 (2008-08-05)
2472  	  */
2473  	public function getLastH() {
2474  	 	 return $this->lasth;
2475  	 }
2476  
2477  	 /**
2478  	  * Set the adjusting factor to convert pixels to user units.
2479  	  * @param $scale (float) adjusting factor to convert pixels to user units.
2480  	  * @author Nicola Asuni
2481  	  * @public
2482  	  * @since 1.5.2
2483  	  */
2484  	public function setImageScale($scale) {
2485  	 	 $this->imgscale = $scale;
2486  	 }
2487  
2488  	 /**
2489  	  * Returns the adjusting factor to convert pixels to user units.
2490  	  * @return float adjusting factor to convert pixels to user units.
2491  	  * @author Nicola Asuni
2492  	  * @public
2493  	  * @since 1.5.2
2494  	  */
2495  	public function getImageScale() {
2496  	 	 return $this->imgscale;
2497  	 }
2498  
2499  	 /**
2500  	  * Returns an array of page dimensions:
2501  	  * <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 user units</li><li>$this->pagedim[$this->page]['hk'] = page height in user units</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><li>$this->pagedim[$this->page]['Rotate'] = The number of degrees by which the page shall be rotated clockwise when displayed or printed. The value shall be a multiple of 90.</li><li>$this->pagedim[$this->page]['PZ'] = The page's preferred zoom (magnification) factor.</li><li>$this->pagedim[$this->page]['trans'] : the style and duration of the visual transition to use when moving from another page to the given page during a presentation<ul><li>$this->pagedim[$this->page]['trans']['Dur'] = The page's display duration (also called its advance timing): the maximum length of time, in seconds, that the page shall be displayed during presentations before the viewer application shall automatically advance to the next page.</li><li>$this->pagedim[$this->page]['trans']['S'] = transition style : Split, Blinds, Box, Wipe, Dissolve, Glitter, R, Fly, Push, Cover, Uncover, Fade</li><li>$this->pagedim[$this->page]['trans']['D'] = The duration of the transition effect, in seconds.</li><li>$this->pagedim[$this->page]['trans']['Dm'] = (Split and Blinds transition styles only) The dimension in which the specified transition effect shall occur: H = Horizontal, V = Vertical. Default value: H.</li><li>$this->pagedim[$this->page]['trans']['M'] = (Split, Box and Fly transition styles only) The direction of motion for the specified transition effect: I = Inward from the edges of the page, O = Outward from the center of the pageDefault value: I.</li><li>$this->pagedim[$this->page]['trans']['Di'] = (Wipe, Glitter, Fly, Cover, Uncover and Push transition styles only) The direction in which the specified transition effect shall moves, expressed in degrees counterclockwise starting from a left-to-right direction. If the value is a number, it shall be one of: 0 = Left to right, 90 = Bottom to top (Wipe only), 180 = Right to left (Wipe only), 270 = Top to bottom, 315 = Top-left to bottom-right (Glitter only). If the value is a name, it shall be None, which is relevant only for the Fly transition when the value of SS is not 1.0. Default value: 0.</li><li>$this->pagedim[$this->page]['trans']['SS'] = (Fly transition style only) The starting or ending scale at which the changes shall be drawn. If M specifies an inward transition, the scale of the changes drawn shall progress from SS to 1.0 over the course of the transition. If M specifies an outward transition, the scale of the changes drawn shall progress from 1.0 to SS over the course of the transition. Default: 1.0. </li><li>$this->pagedim[$this->page]['trans']['B'] = (Fly transition style only) If true, the area that shall be flown in is rectangular and opaque. Default: false.</li></ul></li><li>$this->pagedim[$this->page]['MediaBox'] : the boundaries of the physical medium on which the page shall be displayed or printed<ul><li>$this->pagedim[$this->page]['MediaBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['MediaBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['CropBox'] : the visible region of default user space<ul><li>$this->pagedim[$this->page]['CropBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['CropBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['BleedBox'] : the region to which the contents of the page shall be clipped when output in a production environment<ul><li>$this->pagedim[$this->page]['BleedBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['BleedBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['TrimBox'] : the intended dimensions of the finished page after trimming<ul><li>$this->pagedim[$this->page]['TrimBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['TrimBox']['ury'] = upper-right y coordinate in points</li></ul></li><li>$this->pagedim[$this->page]['ArtBox'] : the extent of the page's meaningful content<ul><li>$this->pagedim[$this->page]['ArtBox']['llx'] = lower-left x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['lly'] = lower-left y coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['urx'] = upper-right x coordinate in points</li><li>$this->pagedim[$this->page]['ArtBox']['ury'] = upper-right y coordinate in points</li></ul></li></ul>
2502  	  * @param $pagenum (int) page number (empty = current page)
2503  	  * @return array of page dimensions.
2504  	  * @author Nicola Asuni
2505  	  * @public
2506  	  * @since 4.5.027 (2009-03-16)
2507  	  */
2508  	public function getPageDimensions($pagenum='') {
2509  	 	 if (empty($pagenum)) {
2510  	 	 	 $pagenum = $this->page;
2511  	 	 }
2512  	 	 return $this->pagedim[$pagenum];
2513  	 }
2514  
2515  	 /**
2516  	  * Returns the page width in units.
2517  	  * @param $pagenum (int) page number (empty = current page)
2518  	  * @return int page width.
2519  	  * @author Nicola Asuni
2520  	  * @public
2521  	  * @since 1.5.2
2522  	  * @see getPageDimensions()
2523  	  */
2524  	public function getPageWidth($pagenum='') {
2525  	 	 if (empty($pagenum)) {
2526  	 	 	 return $this->w;
2527  	 	 }
2528  	 	 return $this->pagedim[$pagenum]['w'];
2529  	 }
2530  
2531  	 /**
2532  	  * Returns the page height in units.
2533  	  * @param $pagenum (int) page number (empty = current page)
2534  	  * @return int page height.
2535  	  * @author Nicola Asuni
2536  	  * @public
2537  	  * @since 1.5.2
2538  	  * @see getPageDimensions()
2539  	  */
2540  	public function getPageHeight($pagenum='') {
2541  	 	 if (empty($pagenum)) {
2542  	 	 	 return $this->h;
2543  	 	 }
2544  	 	 return $this->pagedim[$pagenum]['h'];
2545  	 }
2546  
2547  	 /**
2548  	  * Returns the page break margin.
2549  	  * @param $pagenum (int) page number (empty = current page)
2550  	  * @return int page break margin.
2551  	  * @author Nicola Asuni
2552  	  * @public
2553  	  * @since 1.5.2
2554  	  * @see getPageDimensions()
2555  	  */
2556  	public function getBreakMargin($pagenum='') {
2557  	 	 if (empty($pagenum)) {
2558  	 	 	 return $this->bMargin;
2559  	 	 }
2560  	 	 return $this->pagedim[$pagenum]['bm'];
2561  	 }
2562  
2563  	 /**
2564  	  * Returns the scale factor (number of points in user unit).
2565  	  * @return int scale factor.
2566  	  * @author Nicola Asuni
2567  	  * @public
2568  	  * @since 1.5.2
2569  	  */
2570  	public function getScaleFactor() {
2571  	 	 return $this->k;
2572  	 }
2573  
2574  	 /**
2575  	  * Defines the left, top and right margins.
2576  	  * @param $left (float) Left margin.
2577  	  * @param $top (float) Top margin.
2578  	  * @param $right (float) Right margin. Default value is the left one.
2579  	  * @param $keepmargins (boolean) if true overwrites the default page margins
2580  	  * @public
2581  	  * @since 1.0
2582  	  * @see SetLeftMargin(), SetTopMargin(), SetRightMargin(), SetAutoPageBreak()
2583  	  */
2584  	public function SetMargins($left, $top, $right=-1, $keepmargins=false) {
2585  	 	 //Set left, top and right margins
2586  	 	 $this->lMargin = $left;
2587  	 	 $this->tMargin = $top;
2588  	 	 if ($right == -1) {
2589  	 	 	 $right = $left;
2590  	 	 }
2591  	 	 $this->rMargin = $right;
2592  	 	 if ($keepmargins) {
2593  	 	 	 // overwrite original values
2594  	 	 	 $this->original_lMargin = $this->lMargin;
2595  	 	 	 $this->original_rMargin = $this->rMargin;
2596  	 	 }
2597  	 }
2598  
2599  	 /**
2600  	  * 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.
2601  	  * @param $margin (float) The margin.
2602  	  * @public
2603  	  * @since 1.4
2604  	  * @see SetTopMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2605  	  */
2606  	public function SetLeftMargin($margin) {
2607  	 	 //Set left margin
2608  	 	 $this->lMargin = $margin;
2609  	 	 if (($this->page > 0) AND ($this->x < $margin)) {
2610  	 	 	 $this->x = $margin;
2611  	 	 }
2612  	 }
2613  
2614  	 /**
2615  	  * Defines the top margin. The method can be called before creating the first page.
2616  	  * @param $margin (float) The margin.
2617  	  * @public
2618  	  * @since 1.5
2619  	  * @see SetLeftMargin(), SetRightMargin(), SetAutoPageBreak(), SetMargins()
2620  	  */
2621  	public function SetTopMargin($margin) {
2622  	 	 //Set top margin
2623  	 	 $this->tMargin = $margin;
2624  	 	 if (($this->page > 0) AND ($this->y < $margin)) {
2625  	 	 	 $this->y = $margin;
2626  	 	 }
2627  	 }
2628  
2629  	 /**
2630  	  * Defines the right margin. The method can be called before creating the first page.
2631  	  * @param $margin (float) The margin.
2632  	  * @public
2633  	  * @since 1.5
2634  	  * @see SetLeftMargin(), SetTopMargin(), SetAutoPageBreak(), SetMargins()
2635  	  */
2636  	public function SetRightMargin($margin) {
2637  	 	 $this->rMargin = $margin;
2638  	 	 if (($this->page > 0) AND ($this->x > ($this->w - $margin))) {
2639  	 	 	 $this->x = $this->w - $margin;
2640  	 	 }
2641  	 }
2642  
2643  	 /**
2644  	  * Set the same internal Cell padding for top, right, bottom, left-
2645  	  * @param $pad (float) internal padding.
2646  	  * @public
2647  	  * @since 2.1.000 (2008-01-09)
2648  	  * @see getCellPaddings(), setCellPaddings()
2649  	  */
2650  	public function SetCellPadding($pad) {
2651  	 	 if ($pad >= 0) {
2652  	 	 	 $this->cell_padding['L'] = $pad;
2653  	 	 	 $this->cell_padding['T'] = $pad;
2654  	 	 	 $this->cell_padding['R'] = $pad;
2655  	 	 	 $this->cell_padding['B'] = $pad;
2656  	 	 }
2657  	 }
2658  
2659  	 /**
2660  	  * Set the internal Cell paddings.
2661  	  * @param $left (float) left padding
2662  	  * @param $top (float) top padding
2663  	  * @param $right (float) right padding
2664  	  * @param $bottom (float) bottom padding
2665  	  * @public
2666  	  * @since 5.9.000 (2010-10-03)
2667  	  * @see getCellPaddings(), SetCellPadding()
2668  	  */
2669  	public function setCellPaddings($left='', $top='', $right='', $bottom='') {
2670  	 	 if (($left !== '') AND ($left >= 0)) {
2671  	 	 	 $this->cell_padding['L'] = $left;
2672  	 	 }
2673  	 	 if (($top !== '') AND ($top >= 0)) {
2674  	 	 	 $this->cell_padding['T'] = $top;
2675  	 	 }
2676  	 	 if (($right !== '') AND ($right >= 0)) {
2677  	 	 	 $this->cell_padding['R'] = $right;
2678  	 	 }
2679  	 	 if (($bottom !== '') AND ($bottom >= 0)) {
2680  	 	 	 $this->cell_padding['B'] = $bottom;
2681  	 	 }
2682  	 }
2683  
2684  	 /**
2685  	  * Get the internal Cell padding array.
2686  	  * @return array of padding values
2687  	  * @public
2688  	  * @since 5.9.000 (2010-10-03)
2689  	  * @see setCellPaddings(), SetCellPadding()
2690  	  */
2691  	public function getCellPaddings() {
2692  	 	 return $this->cell_padding;
2693  	 }
2694  
2695  	 /**
2696  	  * Set the internal Cell margins.
2697  	  * @param $left (float) left margin
2698  	  * @param $top (float) top margin
2699  	  * @param $right (float) right margin
2700  	  * @param $bottom (float) bottom margin
2701  	  * @public
2702  	  * @since 5.9.000 (2010-10-03)
2703  	  * @see getCellMargins()
2704  	  */
2705  	public function setCellMargins($left='', $top='', $right='', $bottom='') {
2706  	 	 if (($left !== '') AND ($left >= 0)) {
2707  	 	 	 $this->cell_margin['L'] = $left;
2708  	 	 }
2709  	 	 if (($top !== '') AND ($top >= 0)) {
2710  	 	 	 $this->cell_margin['T'] = $top;
2711  	 	 }
2712  	 	 if (($right !== '') AND ($right >= 0)) {
2713  	 	 	 $this->cell_margin['R'] = $right;
2714  	 	 }
2715  	 	 if (($bottom !== '') AND ($bottom >= 0)) {
2716  	 	 	 $this->cell_margin['B'] = $bottom;
2717  	 	 }
2718  	 }
2719  
2720  	 /**
2721  	  * Get the internal Cell margin array.
2722  	  * @return array of margin values
2723  	  * @public
2724  	  * @since 5.9.000 (2010-10-03)
2725  	  * @see setCellMargins()
2726  	  */
2727  	public function getCellMargins() {
2728  	 	 return $this->cell_margin;
2729  	 }
2730  
2731  	 /**
2732  	  * Adjust the internal Cell padding array to take account of the line width.
2733  	  * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
2734  	  * @return void|array of adjustments
2735  	  * @public
2736  	  * @since 5.9.000 (2010-10-03)
2737  	  */
2738  	protected function adjustCellPadding($brd=0) {
2739  	 	 if (empty($brd)) {
2740  	 	 	 return;
2741  	 	 }
2742  	 	 if (is_string($brd)) {
2743  	 	 	 // convert string to array
2744  	 	 	 $slen = strlen($brd);
2745  	 	 	 $newbrd = array();
2746  	 	 	 for ($i = 0; $i < $slen; ++$i) {
2747  	 	 	 	 $newbrd[$brd[$i]] = true;
2748  	 	 	 }
2749  	 	 	 $brd = $newbrd;
2750  	 	 } elseif (
2751  	 	 	 ($brd === 1)
2752  	 	 	 || ($brd === true)
2753  	 	 	 || (is_numeric($brd) && ((int)$brd > 0))
2754  	 	 ) {
2755  	 	 	 $brd = array('LRTB' => true);
2756  	 	 }
2757  	 	 if (!is_array($brd)) {
2758  	 	 	 return;
2759  	 	 }
2760  	 	 // store current cell padding
2761  	 	 $cp = $this->cell_padding;
2762  	 	 // select border mode
2763  	 	 if (isset($brd['mode'])) {
2764  	 	 	 $mode = $brd['mode'];
2765  	 	 	 unset($brd['mode']);
2766  	 	 } else {
2767  	 	 	 $mode = 'normal';
2768  	 	 }
2769  	 	 // process borders
2770  	 	 foreach ($brd as $border => $style) {
2771  	 	 	 $line_width = $this->LineWidth;
2772  	 	 	 if (is_array($style) && isset($style['width'])) {
2773  	 	 	 	 // get border width
2774  	 	 	 	 $line_width = $style['width'];
2775  	 	 	 }
2776  	 	 	 $adj = 0; // line width inside the cell
2777  	 	 	 switch ($mode) {
2778  	 	 	 	 case 'ext': {
2779  	 	 	 	 	 $adj = 0;
2780  	 	 	 	 	 break;
2781  	 	 	 	 }
2782  	 	 	 	 case 'int': {
2783  	 	 	 	 	 $adj = $line_width;
2784  	 	 	 	 	 break;
2785  	 	 	 	 }
2786  	 	 	 	 case 'normal':
2787  	 	 	 	 default: {
2788  	 	 	 	 	 $adj = ($line_width / 2);
2789  	 	 	 	 	 break;
2790  	 	 	 	 }
2791  	 	 	 }
2792  	 	 	 // correct internal cell padding if required to avoid overlap between text and lines
2793  	 	 	 if (
2794  	 	 	 	 is_numeric($this->cell_padding['T'])
2795  	 	 	 	 && ($this->cell_padding['T'] < $adj)
2796  	 	 	 	 && (strpos($border, 'T') !== false)
2797  	 	 	 ) {
2798  	 	 	 	 $this->cell_padding['T'] = $adj;
2799  	 	 	 }
2800  	 	 	 if (
2801  	 	 	 	 is_numeric($this->cell_padding['R'])
2802  	 	 	 	 && ($this->cell_padding['R'] < $adj)
2803  	 	 	 	 && (strpos($border, 'R') !== false)
2804  	 	 	 ) {
2805  	 	 	 	 $this->cell_padding['R'] = $adj;
2806  	 	 	 }
2807  	 	 	 if (
2808  	 	 	 	 is_numeric($this->cell_padding['B'])
2809  	 	 	 	 && ($this->cell_padding['B'] < $adj)
2810  	 	 	 	 && (strpos($border, 'B') !== false)
2811  	 	 	 ) {
2812  	 	 	 	 $this->cell_padding['B'] = $adj;
2813  	 	 	 }
2814  	 	 	 if (
2815  	 	 	 	 is_numeric($this->cell_padding['L'])
2816  	 	 	 	 && ($this->cell_padding['L'] < $adj)
2817  	 	 	 	 && (strpos($border, 'L') !== false)
2818  	 	 	 ) {
2819  	 	 	 	 $this->cell_padding['L'] = $adj;
2820  	 	 	 }
2821  
2822  	 	 }
2823  
2824  	 	 return array(
2825  	 	 	 'T' => ($this->cell_padding['T'] - $cp['T']),
2826  	 	 	 'R' => ($this->cell_padding['R'] - $cp['R']),
2827  	 	 	 'B' => ($this->cell_padding['B'] - $cp['B']),
2828  	 	 	 'L' => ($this->cell_padding['L'] - $cp['L']),
2829  	 	 );
2830  	 }
2831  
2832  	 /**
2833  	  * 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.
2834  	  * @param $auto (boolean) Boolean indicating if mode should be on or off.
2835  	  * @param $margin (float) Distance from the bottom of the page.
2836  	  * @public
2837  	  * @since 1.0
2838  	  * @see Cell(), MultiCell(), AcceptPageBreak()
2839  	  */
2840  	public function SetAutoPageBreak($auto, $margin=0) {
2841  	 	 $this->AutoPageBreak = $auto ? true : false;
2842  	 	 $this->bMargin = $margin;
2843  	 	 $this->PageBreakTrigger = $this->h - $margin;
2844  	 }
2845  
2846  	 /**
2847  	  * Return the auto-page-break mode (true or false).
2848  	  * @return boolean auto-page-break mode
2849  	  * @public
2850  	  * @since 5.9.088
2851  	  */
2852  	public function getAutoPageBreak() {
2853  	 	 return $this->AutoPageBreak;
2854  	 }
2855  
2856  	 /**
2857  	  * Defines the way the document is to be displayed by the viewer.
2858  	  * @param $zoom (mixed) 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>
2859  	  * @param $layout (string) 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>
2860  	  * @param $mode (string) 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>
2861  	  * @public
2862  	  * @since 1.2
2863  	  */
2864  	public function SetDisplayMode($zoom, $layout='SinglePage', $mode='UseNone') {
2865  	 	 if (($zoom == 'fullpage') OR ($zoom == 'fullwidth') OR ($zoom == 'real') OR ($zoom == 'default') OR (!is_string($zoom))) {
2866  	 	 	 $this->ZoomMode = $zoom;
2867  	 	 } else {
2868  	 	 	 $this->Error('Incorrect zoom display mode: '.$zoom);
2869  	 	 }
2870  	 	 $this->LayoutMode = TCPDF_STATIC::getPageLayoutMode($layout);
2871  	 	 $this->PageMode = TCPDF_STATIC::getPageMode($mode);
2872  	 }
2873  
2874  	 /**
2875  	  * 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.
2876  	  * Note: the Zlib extension is required for this feature. If not present, compression will be turned off.
2877  	  * @param $compress (boolean) Boolean indicating if compression must be enabled.
2878  	  * @public
2879  	  * @since 1.4
2880  	  */
2881  	public function SetCompression($compress=true) {
2882  	 	 $this->compress = false;
2883  	 	 if (function_exists('gzcompress')) {
2884  	 	 	 if ($compress) {
2885  	 	 	 	 if ( !$this->pdfa_mode) {
2886  	 	 	 	 	 $this->compress = true;
2887  	 	 	 	 }
2888  	 	 	 }
2889  	 	 }
2890  	 }
2891  
2892  	 /**
2893  	  * Set flag to force sRGB_IEC61966-2.1 black scaled ICC color profile for the whole document.
2894  	  * @param $mode (boolean) If true force sRGB output intent.
2895  	  * @public
2896  	  * @since 5.9.121 (2011-09-28)
2897  	  */
2898  	public function setSRGBmode($mode=false) {
2899  	 	 $this->force_srgb = $mode ? true : false;
2900  	 }
2901  
2902  	 /**
2903  	  * Turn on/off Unicode mode for document information dictionary (meta tags).
2904  	  * This has effect only when unicode mode is set to false.
2905  	  * @param $unicode (boolean) if true set the meta information in Unicode
2906  	  * @since 5.9.027 (2010-12-01)
2907  	  * @public
2908  	  */
2909  	public function SetDocInfoUnicode($unicode=true) {
2910  	 	 $this->docinfounicode = $unicode ? true : false;
2911  	 }
2912  
2913  	 /**
2914  	  * Defines the title of the document.
2915  	  * @param $title (string) The title.
2916  	  * @public
2917  	  * @since 1.2
2918  	  * @see SetAuthor(), SetCreator(), SetKeywords(), SetSubject()
2919  	  */
2920  	public function SetTitle($title) {
2921  	 	 $this->title = $title;
2922  	 }
2923  
2924  	 /**
2925  	  * Defines the subject of the document.
2926  	  * @param $subject (string) The subject.
2927  	  * @public
2928  	  * @since 1.2
2929  	  * @see SetAuthor(), SetCreator(), SetKeywords(), SetTitle()
2930  	  */
2931  	public function SetSubject($subject) {
2932  	 	 $this->subject = $subject;
2933  	 }
2934  
2935  	 /**
2936  	  * Defines the author of the document.
2937  	  * @param $author (string) The name of the author.
2938  	  * @public
2939  	  * @since 1.2
2940  	  * @see SetCreator(), SetKeywords(), SetSubject(), SetTitle()
2941  	  */
2942  	public function SetAuthor($author) {
2943  	 	 $this->author = $author;
2944  	 }
2945  
2946  	 /**
2947  	  * Associates keywords with the document, generally in the form 'keyword1 keyword2 ...'.
2948  	  * @param $keywords (string) The list of keywords.
2949  	  * @public
2950  	  * @since 1.2
2951  	  * @see SetAuthor(), SetCreator(), SetSubject(), SetTitle()
2952  	  */
2953  	public function SetKeywords($keywords) {
2954  	 	 $this->keywords = $keywords;
2955  	 }
2956  
2957  	 /**
2958  	  * Defines the creator of the document. This is typically the name of the application that generates the PDF.
2959  	  * @param $creator (string) The name of the creator.
2960  	  * @public
2961  	  * @since 1.2
2962  	  * @see SetAuthor(), SetKeywords(), SetSubject(), SetTitle()
2963  	  */
2964  	public function SetCreator($creator) {
2965  	 	 $this->creator = $creator;
2966  	 }
2967  
2968  	 /**
2969  	  * Whether to allow local file path in image html tags, when prefixed with file://
2970  	  * 
2971  	  * @param $allowLocalFiles bool true, when local files should be allowed. Otherwise false.
2972  	  * @public
2973  	  * @since 6.4
2974  	  */
2975  	public function SetAllowLocalFiles($allowLocalFiles) {
2976  	 	 $this->allowLocalFiles = (bool) $allowLocalFiles;
2977  	 }
2978  
2979  
2980  	 /**
2981  	  * Throw an exception or print an error message and die if the K_TCPDF_PARSER_THROW_EXCEPTION_ERROR constant is set to true.
2982  	  * @param $msg (string) The error message
2983  	  * @public
2984  	  * @since 1.0
2985  	  */
2986  	public function Error($msg) {
2987  	 	 // unset all class variables
2988  	 	 $this->_destroy(true);
2989  	 	 if (defined('K_TCPDF_THROW_EXCEPTION_ERROR') AND !K_TCPDF_THROW_EXCEPTION_ERROR) {
2990  	 	 	 die('<strong>TCPDF ERROR: </strong>'.$msg);
2991  	 	 } else {
2992  	 	 	 throw new Exception('TCPDF ERROR: '.$msg);
2993  	 	 }
2994  	 }
2995  
2996  	 /**
2997  	  * This method begins the generation of the PDF document.
2998  	  * It is not necessary to call it explicitly because AddPage() does it automatically.
2999  	  * Note: no page is created by this method
3000  	  * @public
3001  	  * @since 1.0
3002  	  * @see AddPage(), Close()
3003  	  */
3004  	public function Open() {
3005  	 	 $this->state = 1;
3006  	 }
3007  
3008  	 /**
3009  	  * Terminates the PDF document.
3010  	  * It is not necessary to call this method explicitly because Output() does it automatically.
3011  	  * If the document contains no page, AddPage() is called to prevent from getting an invalid document.
3012  	  * @public
3013  	  * @since 1.0
3014  	  * @see Open(), Output()
3015  	  */
3016  	public function Close() {
3017  	 	 if ($this->state == 3) {
3018  	 	 	 return;
3019  	 	 }
3020  	 	 if ($this->page == 0) {
3021  	 	 	 $this->AddPage();
3022  	 	 }
3023  	 	 $this->endLayer();
3024  	 	 if ($this->tcpdflink) {
3025  	 	 	 // save current graphic settings
3026  	 	 	 $gvars = $this->getGraphicVars();
3027  	 	 	 $this->setEqualColumns();
3028  	 	 	 $this->lastpage(true);
3029  	 	 	 $this->SetAutoPageBreak(false);
3030  	 	 	 $this->x = 0;
3031  	 	 	 $this->y = $this->h - (1 / $this->k);
3032  	 	 	 $this->lMargin = 0;
3033  	 	 	 $this->_outSaveGraphicsState();
3034  	 	 	 $font = defined('PDF_FONT_NAME_MAIN')?PDF_FONT_NAME_MAIN:'helvetica';
3035  	 	 	 $this->SetFont($font, '', 1);
3036  	 	 	 $this->setTextRenderingMode(0, false, false);
3037  	 	 	 $msg = "\x50\x6f\x77\x65\x72\x65\x64\x20\x62\x79\x20\x54\x43\x50\x44\x46\x20\x28\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67\x29";
3038  	 	 	 $lnk = "\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x74\x63\x70\x64\x66\x2e\x6f\x72\x67";
3039  	 	 	 $this->Cell(0, 0, $msg, 0, 0, 'L', 0, $lnk, 0, false, 'D', 'B');
3040  	 	 	 $this->_outRestoreGraphicsState();
3041  	 	 	 // restore graphic settings
3042  	 	 	 $this->setGraphicVars($gvars);
3043  	 	 }
3044  	 	 // close page
3045  	 	 $this->endPage();
3046  	 	 // close document
3047  	 	 $this->_enddoc();
3048  	 	 // unset all class variables (except critical ones)
3049  	 	 $this->_destroy(false);
3050  	 }
3051  
3052  	 /**
3053  	  * Move pointer at the specified document page and update page dimensions.
3054  	  * @param $pnum (int) page number (1 ... numpages)
3055  	  * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3056  	  * @public
3057  	  * @since 2.1.000 (2008-01-07)
3058  	  * @see getPage(), lastpage(), getNumPages()
3059  	  */
3060  	public function setPage($pnum, $resetmargins=false) {
3061  	 	 if (($pnum == $this->page) AND ($this->state == 2)) {
3062  	 	 	 return;
3063  	 	 }
3064  	 	 if (($pnum > 0) AND ($pnum <= $this->numpages)) {
3065  	 	 	 $this->state = 2;
3066  	 	 	 // save current graphic settings
3067  	 	 	 //$gvars = $this->getGraphicVars();
3068  	 	 	 $oldpage = $this->page;
3069  	 	 	 $this->page = $pnum;
3070  	 	 	 $this->wPt = $this->pagedim[$this->page]['w'];
3071  	 	 	 $this->hPt = $this->pagedim[$this->page]['h'];
3072  	 	 	 $this->w = $this->pagedim[$this->page]['wk'];
3073  	 	 	 $this->h = $this->pagedim[$this->page]['hk'];
3074  	 	 	 $this->tMargin = $this->pagedim[$this->page]['tm'];
3075  	 	 	 $this->bMargin = $this->pagedim[$this->page]['bm'];
3076  	 	 	 $this->original_lMargin = $this->pagedim[$this->page]['olm'];
3077  	 	 	 $this->original_rMargin = $this->pagedim[$this->page]['orm'];
3078  	 	 	 $this->AutoPageBreak = $this->pagedim[$this->page]['pb'];
3079  	 	 	 $this->CurOrientation = $this->pagedim[$this->page]['or'];
3080  	 	 	 $this->SetAutoPageBreak($this->AutoPageBreak, $this->bMargin);
3081  	 	 	 // restore graphic settings
3082  	 	 	 //$this->setGraphicVars($gvars);
3083  	 	 	 if ($resetmargins) {
3084  	 	 	 	 $this->lMargin = $this->pagedim[$this->page]['olm'];
3085  	 	 	 	 $this->rMargin = $this->pagedim[$this->page]['orm'];
3086  	 	 	 	 $this->SetY($this->tMargin);
3087  	 	 	 } else {
3088  	 	 	 	 // account for booklet mode
3089  	 	 	 	 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
3090  	 	 	 	 	 $deltam = $this->pagedim[$this->page]['olm'] - $this->pagedim[$this->page]['orm'];
3091  	 	 	 	 	 $this->lMargin += $deltam;
3092  	 	 	 	 	 $this->rMargin -= $deltam;
3093  	 	 	 	 }
3094  	 	 	 }
3095  	 	 } else {
3096  	 	 	 $this->Error('Wrong page number on setPage() function: '.$pnum);
3097  	 	 }
3098  	 }
3099  
3100  	 /**
3101  	  * Reset pointer to the last document page.
3102  	  * @param $resetmargins (boolean) if true reset left, right, top margins and Y position.
3103  	  * @public
3104  	  * @since 2.0.000 (2008-01-04)
3105  	  * @see setPage(), getPage(), getNumPages()
3106  	  */
3107  	public function lastPage($resetmargins=false) {
3108  	 	 $this->setPage($this->getNumPages(), $resetmargins);
3109  	 }
3110  
3111  	 /**
3112  	  * Get current document page number.
3113  	  * @return int page number
3114  	  * @public
3115  	  * @since 2.1.000 (2008-01-07)
3116  	  * @see setPage(), lastpage(), getNumPages()
3117  	  */
3118  	public function getPage() {
3119  	 	 return $this->page;
3120  	 }
3121  
3122  	 /**
3123  	  * Get the total number of insered pages.
3124  	  * @return int number of pages
3125  	  * @public
3126  	  * @since 2.1.000 (2008-01-07)
3127  	  * @see setPage(), getPage(), lastpage()
3128  	  */
3129  	public function getNumPages() {
3130  	 	 return $this->numpages;
3131  	 }
3132  
3133  	 /**
3134  	  * Adds a new TOC (Table Of Content) page to the document.
3135  	  * @param $orientation (string) page orientation.
3136  	  * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3137  	  * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3138  	  * @public
3139  	  * @since 5.0.001 (2010-05-06)
3140  	  * @see AddPage(), startPage(), endPage(), endTOCPage()
3141  	  */
3142  	public function addTOCPage($orientation='', $format='', $keepmargins=false) {
3143  	 	 $this->AddPage($orientation, $format, $keepmargins, true);
3144  	 }
3145  
3146  	 /**
3147  	  * Terminate the current TOC (Table Of Content) page
3148  	  * @public
3149  	  * @since 5.0.001 (2010-05-06)
3150  	  * @see AddPage(), startPage(), endPage(), addTOCPage()
3151  	  */
3152  	public function endTOCPage() {
3153  	 	 $this->endPage(true);
3154  	 }
3155  
3156  	 /**
3157  	  * 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).
3158  	  * The origin of the coordinate system is at the top-left corner (or top-right for RTL) and increasing ordinates go downwards.
3159  	  * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3160  	  * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3161  	  * @param $keepmargins (boolean) if true overwrites the default page margins with the current margins
3162  	  * @param $tocpage (boolean) if true set the tocpage state to true (the added page will be used to display Table Of Content).
3163  	  * @public
3164  	  * @since 1.0
3165  	  * @see startPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3166  	  */
3167  	public function AddPage($orientation='', $format='', $keepmargins=false, $tocpage=false) {
3168  	 	 if ($this->inxobj) {
3169  	 	 	 // we are inside an XObject template
3170  	 	 	 return;
3171  	 	 }
3172  	 	 if (!isset($this->original_lMargin) OR $keepmargins) {
3173  	 	 	 $this->original_lMargin = $this->lMargin;
3174  	 	 }
3175  	 	 if (!isset($this->original_rMargin) OR $keepmargins) {
3176  	 	 	 $this->original_rMargin = $this->rMargin;
3177  	 	 }
3178  	 	 // terminate previous page
3179  	 	 $this->endPage();
3180  	 	 // start new page
3181  	 	 $this->startPage($orientation, $format, $tocpage);
3182  	 }
3183  
3184  	 /**
3185  	  * Terminate the current page
3186  	  * @param $tocpage (boolean) if true set the tocpage state to false (end the page used to display Table Of Content).
3187  	  * @public
3188  	  * @since 4.2.010 (2008-11-14)
3189  	  * @see AddPage(), startPage(), addTOCPage(), endTOCPage()
3190  	  */
3191  	public function endPage($tocpage=false) {
3192  	 	 // check if page is already closed
3193  	 	 if (($this->page == 0) OR ($this->numpages > $this->page) OR (!$this->pageopen[$this->page])) {
3194  	 	 	 return;
3195  	 	 }
3196  	 	 // print page footer
3197  	 	 $this->setFooter();
3198  	 	 // close page
3199  	 	 $this->_endpage();
3200  	 	 // mark page as closed
3201  	 	 $this->pageopen[$this->page] = false;
3202  	 	 if ($tocpage) {
3203  	 	 	 $this->tocpage = false;
3204  	 	 }
3205  	 }
3206  
3207  	 /**
3208  	  * Starts a new page to the document. The page must be closed using the endPage() function.
3209  	  * The origin of the coordinate system is at the top-left corner and increasing ordinates go downwards.
3210  	  * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
3211  	  * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
3212  	  * @param $tocpage (boolean) if true the page is designated to contain the Table-Of-Content.
3213  	  * @since 4.2.010 (2008-11-14)
3214  	  * @see AddPage(), endPage(), addTOCPage(), endTOCPage(), getPageSizeFromFormat(), setPageFormat()
3215  	  * @public
3216  	  */
3217  	public function startPage($orientation='', $format='', $tocpage=false) {
3218  	 	 if ($tocpage) {
3219  	 	 	 $this->tocpage = true;
3220  	 	 }
3221  	 	 // move page numbers of documents to be attached
3222  	 	 if ($this->tocpage) {
3223  	 	 	 // move reference to unexistent pages (used for page attachments)
3224  	 	 	 // adjust outlines
3225  	 	 	 $tmpoutlines = $this->outlines;
3226  	 	 	 foreach ($tmpoutlines as $key => $outline) {
3227  	 	 	 	 if (!$outline['f'] AND ($outline['p'] > $this->numpages)) {
3228  	 	 	 	 	 $this->outlines[$key]['p'] = ($outline['p'] + 1);
3229  	 	 	 	 }
3230  	 	 	 }
3231  	 	 	 // adjust dests
3232  	 	 	 $tmpdests = $this->dests;
3233  	 	 	 foreach ($tmpdests as $key => $dest) {
3234  	 	 	 	 if (!$dest['f'] AND ($dest['p'] > $this->numpages)) {
3235  	 	 	 	 	 $this->dests[$key]['p'] = ($dest['p'] + 1);
3236  	 	 	 	 }
3237  	 	 	 }
3238  	 	 	 // adjust links
3239  	 	 	 $tmplinks = $this->links;
3240  	 	 	 foreach ($tmplinks as $key => $link) {
3241  	 	 	 	 if (!$link['f'] AND ($link['p'] > $this->numpages)) {
3242  	 	 	 	 	 $this->links[$key]['p'] = ($link['p'] + 1);
3243  	 	 	 	 }
3244  	 	 	 }
3245  	 	 }
3246  	 	 if ($this->numpages > $this->page) {
3247  	 	 	 // this page has been already added
3248  	 	 	 $this->setPage($this->page + 1);
3249  	 	 	 $this->SetY($this->tMargin);
3250  	 	 	 return;
3251  	 	 }
3252  	 	 // start a new page
3253  	 	 if ($this->state == 0) {
3254  	 	 	 $this->Open();
3255  	 	 }
3256  	 	 ++$this->numpages;
3257  	 	 $this->swapMargins($this->booklet);
3258  	 	 // save current graphic settings
3259  	 	 $gvars = $this->getGraphicVars();
3260  	 	 // start new page
3261  	 	 $this->_beginpage($orientation, $format);
3262  	 	 // mark page as open
3263  	 	 $this->pageopen[$this->page] = true;
3264  	 	 // restore graphic settings
3265  	 	 $this->setGraphicVars($gvars);
3266  	 	 // mark this point
3267  	 	 $this->setPageMark();
3268  	 	 // print page header
3269  	 	 $this->setHeader();
3270  	 	 // restore graphic settings
3271  	 	 $this->setGraphicVars($gvars);
3272  	 	 // mark this point
3273  	 	 $this->setPageMark();
3274  	 	 // print table header (if any)
3275  	 	 $this->setTableHeader();
3276  	 	 // set mark for empty page check
3277  	 	 $this->emptypagemrk[$this->page]= $this->pagelen[$this->page];
3278  	 }
3279  
3280  	 /**
3281  	  * Set start-writing mark on current page stream used to put borders and fills.
3282  	  * Borders and fills are always created after content and inserted on the position marked by this method.
3283  	  * This function must be called after calling Image() function for a background image.
3284  	  * Background images must be always inserted before calling Multicell() or WriteHTMLCell() or WriteHTML() functions.
3285  	  * @public
3286  	  * @since 4.0.016 (2008-07-30)
3287  	  */
3288  	public function setPageMark() {
3289  	 	 $this->intmrk[$this->page] = $this->pagelen[$this->page];
3290  	 	 $this->bordermrk[$this->page] = $this->intmrk[$this->page];
3291  	 	 $this->setContentMark();
3292  	 }
3293  
3294  	 /**
3295  	  * Set start-writing mark on selected page.
3296  	  * Borders and fills are always created after content and inserted on the position marked by this method.
3297  	  * @param $page (int) page number (default is the current page)
3298  	  * @protected
3299  	  * @since 4.6.021 (2009-07-20)
3300  	  */
3301  	protected function setContentMark($page=0) {
3302  	 	 if ($page <= 0) {
3303  	 	 	 $page = $this->page;
3304  	 	 }
3305  	 	 if (isset($this->footerlen[$page])) {
3306  	 	 	 $this->cntmrk[$page] = $this->pagelen[$page] - $this->footerlen[$page];
3307  	 	 } else {
3308  	 	 	 $this->cntmrk[$page] = $this->pagelen[$page];
3309  	 	 }
3310  	 }
3311  
3312  	 /**
3313  	  * Set header data.
3314  	  * @param $ln (string) header image logo
3315  	  * @param $lw (string) header image logo width in mm
3316  	  * @param $ht (string) string to print as title on document header
3317  	  * @param $hs (string) string to print on document header
3318  	  * @param $tc (array) RGB array color for text.
3319  	  * @param $lc (array) RGB array color for line.
3320  	  * @public
3321  	  */
3322  	public function setHeaderData($ln='', $lw=0, $ht='', $hs='', $tc=array(0,0,0), $lc=array(0,0,0)) {
3323  	 	 $this->header_logo = $ln;
3324  	 	 $this->header_logo_width = $lw;
3325  	 	 $this->header_title = $ht;
3326  	 	 $this->header_string = $hs;
3327  	 	 $this->header_text_color = $tc;
3328  	 	 $this->header_line_color = $lc;
3329  	 }
3330  
3331  	 /**
3332  	  * Set footer data.
3333  	  * @param $tc (array) RGB array color for text.
3334  	  * @param $lc (array) RGB array color for line.
3335  	  * @public
3336  	  */
3337  	public function setFooterData($tc=array(0,0,0), $lc=array(0,0,0)) {
3338  	 	 $this->footer_text_color = $tc;
3339  	 	 $this->footer_line_color = $lc;
3340  	 }
3341  
3342  	 /**
3343  	  * Returns header data:
3344  	  * <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>
3345  	  * @return array()
3346  	  * @public
3347  	  * @since 4.0.012 (2008-07-24)
3348  	  */
3349  	public function getHeaderData() {
3350  	 	 $ret = array();
3351  	 	 $ret['logo'] = $this->header_logo;
3352  	 	 $ret['logo_width'] = $this->header_logo_width;
3353  	 	 $ret['title'] = $this->header_title;
3354  	 	 $ret['string'] = $this->header_string;
3355  	 	 $ret['text_color'] = $this->header_text_color;
3356  	 	 $ret['line_color'] = $this->header_line_color;
3357  	 	 return $ret;
3358  	 }
3359  
3360  	 /**
3361  	  * Set header margin.
3362  	  * (minimum distance between header and top page margin)
3363  	  * @param $hm (int) distance in user units
3364  	  * @public
3365  	  */
3366  	public function setHeaderMargin($hm=10) {
3367  	 	 $this->header_margin = $hm;
3368  	 }
3369  
3370  	 /**
3371  	  * Returns header margin in user units.
3372  	  * @return float
3373  	  * @since 4.0.012 (2008-07-24)
3374  	  * @public
3375  	  */
3376  	public function getHeaderMargin() {
3377  	 	 return $this->header_margin;
3378  	 }
3379  
3380  	 /**
3381  	  * Set footer margin.
3382  	  * (minimum distance between footer and bottom page margin)
3383  	  * @param $fm (int) distance in user units
3384  	  * @public
3385  	  */
3386  	public function setFooterMargin($fm=10) {
3387  	 	 $this->footer_margin = $fm;
3388  	 }
3389  
3390  	 /**
3391  	  * Returns footer margin in user units.
3392  	  * @return float
3393  	  * @since 4.0.012 (2008-07-24)
3394  	  * @public
3395  	  */
3396  	public function getFooterMargin() {
3397  	 	 return $this->footer_margin;
3398  	 }
3399  	 /**
3400  	  * Set a flag to print page header.
3401  	  * @param $val (boolean) set to true to print the page header (default), false otherwise.
3402  	  * @public
3403  	  */
3404  	public function setPrintHeader($val=true) {
3405  	 	 $this->print_header = $val ? true : false;
3406  	 }
3407  
3408  	 /**
3409  	  * Set a flag to print page footer.
3410  	  * @param $val (boolean) set to true to print the page footer (default), false otherwise.
3411  	  * @public
3412  	  */
3413  	public function setPrintFooter($val=true) {
3414  	 	 $this->print_footer = $val ? true : false;
3415  	 }
3416  
3417  	 /**
3418  	  * Return the right-bottom (or left-bottom for RTL) corner X coordinate of last inserted image
3419  	  * @return float
3420  	  * @public
3421  	  */
3422  	public function getImageRBX() {
3423  	 	 return $this->img_rb_x;
3424  	 }
3425  
3426  	 /**
3427  	  * Return the right-bottom (or left-bottom for RTL) corner Y coordinate of last inserted image
3428  	  * @return float
3429  	  * @public
3430  	  */
3431  	public function getImageRBY() {
3432  	 	 return $this->img_rb_y;
3433  	 }
3434  
3435  	 /**
3436  	  * Reset the xobject template used by Header() method.
3437  	  * @public
3438  	  */
3439  	public function resetHeaderTemplate() {
3440  	 	 $this->header_xobjid = false;
3441  	 }
3442  
3443  	 /**
3444  	  * Set a flag to automatically reset the xobject template used by Header() method at each page.
3445  	  * @param $val (boolean) set to true to reset Header xobject template at each page, false otherwise.
3446  	  * @public
3447  	  */
3448  	public function setHeaderTemplateAutoreset($val=true) {
3449  	 	 $this->header_xobj_autoreset = $val ? true : false;
3450  	 }
3451  
3452  	 /**
3453  	  * This method is used to render the page header.
3454  	  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3455  	  * @public
3456  	  */
3457  	public function Header() {
3458  	 	 if ($this->header_xobjid === false) {
3459  	 	 	 // start a new XObject Template
3460  	 	 	 $this->header_xobjid = $this->startTemplate($this->w, $this->tMargin);
3461  	 	 	 $headerfont = $this->getHeaderFont();
3462  	 	 	 $headerdata = $this->getHeaderData();
3463  	 	 	 $this->y = $this->header_margin;
3464  	 	 	 if ($this->rtl) {
3465  	 	 	 	 $this->x = $this->w - $this->original_rMargin;
3466  	 	 	 } else {
3467  	 	 	 	 $this->x = $this->original_lMargin;
3468  	 	 	 }
3469  	 	 	 if (($headerdata['logo']) AND ($headerdata['logo'] != K_BLANK_IMAGE)) {
3470  	 	 	 	 $imgtype = TCPDF_IMAGES::getImageFileType(K_PATH_IMAGES.$headerdata['logo']);
3471  	 	 	 	 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
3472  	 	 	 	 	 $this->ImageEps(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3473  	 	 	 	 } elseif ($imgtype == 'svg') {
3474  	 	 	 	 	 $this->ImageSVG(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3475  	 	 	 	 } else {
3476  	 	 	 	 	 $this->Image(K_PATH_IMAGES.$headerdata['logo'], '', '', $headerdata['logo_width']);
3477  	 	 	 	 }
3478  	 	 	 	 $imgy = $this->getImageRBY();
3479  	 	 	 } else {
3480  	 	 	 	 $imgy = $this->y;
3481  	 	 	 }
3482  	 	 	 $cell_height = $this->getCellHeight($headerfont[2] / $this->k);
3483  	 	 	 // set starting margin for text data cell
3484  	 	 	 if ($this->getRTL()) {
3485  	 	 	 	 $header_x = $this->original_rMargin + ($headerdata['logo_width'] * 1.1);
3486  	 	 	 } else {
3487  	 	 	 	 $header_x = $this->original_lMargin + ($headerdata['logo_width'] * 1.1);
3488  	 	 	 }
3489  	 	 	 $cw = $this->w - $this->original_lMargin - $this->original_rMargin - ($headerdata['logo_width'] * 1.1);
3490  	 	 	 $this->SetTextColorArray($this->header_text_color);
3491  	 	 	 // header title
3492  	 	 	 $this->SetFont($headerfont[0], 'B', $headerfont[2] + 1);
3493  	 	 	 $this->SetX($header_x);
3494  	 	 	 $this->Cell($cw, $cell_height, $headerdata['title'], 0, 1, '', 0, '', 0);
3495  	 	 	 // header string
3496  	 	 	 $this->SetFont($headerfont[0], $headerfont[1], $headerfont[2]);
3497  	 	 	 $this->SetX($header_x);
3498  	 	 	 $this->MultiCell($cw, $cell_height, $headerdata['string'], 0, '', 0, 1, '', '', true, 0, false, true, 0, 'T', false);
3499  	 	 	 // print an ending header line
3500  	 	 	 $this->SetLineStyle(array('width' => 0.85 / $this->k, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $headerdata['line_color']));
3501  	 	 	 $this->SetY((2.835 / $this->k) + max($imgy, $this->y));
3502  	 	 	 if ($this->rtl) {
3503  	 	 	 	 $this->SetX($this->original_rMargin);
3504  	 	 	 } else {
3505  	 	 	 	 $this->SetX($this->original_lMargin);
3506  	 	 	 }
3507  	 	 	 $this->Cell(($this->w - $this->original_lMargin - $this->original_rMargin), 0, '', 'T', 0, 'C');
3508  	 	 	 $this->endTemplate();
3509  	 	 }
3510  	 	 // print header template
3511  	 	 $x = 0;
3512  	 	 $dx = 0;
3513  	 	 if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
3514  	 	 	 // adjust margins for booklet mode
3515  	 	 	 $dx = ($this->original_lMargin - $this->original_rMargin);
3516  	 	 }
3517  	 	 if ($this->rtl) {
3518  	 	 	 $x = $this->w + $dx;
3519  	 	 } else {
3520  	 	 	 $x = 0 + $dx;
3521  	 	 }
3522  	 	 $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
3523  	 	 if ($this->header_xobj_autoreset) {
3524  	 	 	 // reset header xobject template at each page
3525  	 	 	 $this->header_xobjid = false;
3526  	 	 }
3527  	 }
3528  
3529  	 /**
3530  	  * This method is used to render the page footer.
3531  	  * It is automatically called by AddPage() and could be overwritten in your own inherited class.
3532  	  * @public
3533  	  */
3534  	public function Footer() {
3535  	 	 $cur_y = $this->y;
3536  	 	 $this->SetTextColorArray($this->footer_text_color);
3537  	 	 //set style for cell border
3538  	 	 $line_width = (0.85 / $this->k);
3539  	 	 $this->SetLineStyle(array('width' => $line_width, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $this->footer_line_color));
3540  	 	 //print document barcode
3541  	 	 $barcode = $this->getBarcode();
3542  	 	 if (!empty($barcode)) {
3543  	 	 	 $this->Ln($line_width);
3544  	 	 	 $barcode_width = round(($this->w - $this->original_lMargin - $this->original_rMargin) / 3);
3545  	 	 	 $style = array(
3546  	 	 	 	 'position' => $this->rtl?'R':'L',
3547  	 	 	 	 'align' => $this->rtl?'R':'L',
3548  	 	 	 	 'stretch' => false,
3549  	 	 	 	 'fitwidth' => true,
3550  	 	 	 	 'cellfitalign' => '',
3551  	 	 	 	 'border' => false,
3552  	 	 	 	 'padding' => 0,
3553  	 	 	 	 'fgcolor' => array(0,0,0),
3554  	 	 	 	 'bgcolor' => false,
3555  	 	 	 	 'text' => false
3556  	 	 	 );
3557  	 	 	 $this->write1DBarcode($barcode, 'C128', '', $cur_y + $line_width, '', (($this->footer_margin / 3) - $line_width), 0.3, $style, '');
3558  	 	 }
3559  	 	 $w_page = isset($this->l['w_page']) ? $this->l['w_page'].' ' : '';
3560  	 	 if (empty($this->pagegroups)) {
3561  	 	 	 $pagenumtxt = $w_page.$this->getAliasNumPage().' / '.$this->getAliasNbPages();
3562  	 	 } else {
3563  	 	 	 $pagenumtxt = $w_page.$this->getPageNumGroupAlias().' / '.$this->getPageGroupAlias();
3564  	 	 }
3565  	 	 $this->SetY($cur_y);
3566  	 	 //Print page number
3567  	 	 if ($this->getRTL()) {
3568  	 	 	 $this->SetX($this->original_rMargin);
3569  	 	 	 $this->Cell(0, 0, $pagenumtxt, 'T', 0, 'L');
3570  	 	 } else {
3571  	 	 	 $this->SetX($this->original_lMargin);
3572  	 	 	 $this->Cell(0, 0, $this->getAliasRightShift().$pagenumtxt, 'T', 0, 'R');
3573  	 	 }
3574  	 }
3575  
3576  	 /**
3577  	  * This method is used to render the page header.
3578  	  * @protected
3579  	  * @since 4.0.012 (2008-07-24)
3580  	  */
3581  	protected function setHeader() {
3582  	 	 if (!$this->print_header OR ($this->state != 2)) {
3583  	 	 	 return;
3584  	 	 }
3585  	 	 $this->InHeader = true;
3586  	 	 $this->setGraphicVars($this->default_graphic_vars);
3587  	 	 $temp_thead = $this->thead;
3588  	 	 $temp_theadMargins = $this->theadMargins;
3589  	 	 $lasth = $this->lasth;
3590  	 	 $newline = $this->newline;
3591  	 	 $this->_outSaveGraphicsState();
3592  	 	 $this->rMargin = $this->original_rMargin;
3593  	 	 $this->lMargin = $this->original_lMargin;
3594  	 	 $this->SetCellPadding(0);
3595  	 	 //set current position
3596  	 	 if ($this->rtl) {
3597  	 	 	 $this->SetXY($this->original_rMargin, $this->header_margin);
3598  	 	 } else {
3599  	 	 	 $this->SetXY($this->original_lMargin, $this->header_margin);
3600  	 	 }
3601  	 	 $this->SetFont($this->header_font[0], $this->header_font[1], $this->header_font[2]);
3602  	 	 $this->Header();
3603  	 	 //restore position
3604  	 	 if ($this->rtl) {
3605  	 	 	 $this->SetXY($this->original_rMargin, $this->tMargin);
3606  	 	 } else {
3607  	 	 	 $this->SetXY($this->original_lMargin, $this->tMargin);
3608  	 	 }
3609  	 	 $this->_outRestoreGraphicsState();
3610  	 	 $this->lasth = $lasth;
3611  	 	 $this->thead = $temp_thead;
3612  	 	 $this->theadMargins = $temp_theadMargins;
3613  	 	 $this->newline = $newline;
3614  	 	 $this->InHeader = false;
3615  	 }
3616  
3617  	 /**
3618  	  * This method is used to render the page footer.
3619  	  * @protected
3620  	  * @since 4.0.012 (2008-07-24)
3621  	  */
3622  	protected function setFooter() {
3623  	 	 if ($this->state != 2) {
3624  	 	 	 return;
3625  	 	 }
3626  	 	 $this->InFooter = true;
3627  	 	 // save current graphic settings
3628  	 	 $gvars = $this->getGraphicVars();
3629  	 	 // mark this point
3630  	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page];
3631  	 	 $this->_out("\n");
3632  	 	 if ($this->print_footer) {
3633  	 	 	 $this->setGraphicVars($this->default_graphic_vars);
3634  	 	 	 $this->current_column = 0;
3635  	 	 	 $this->num_columns = 1;
3636  	 	 	 $temp_thead = $this->thead;
3637  	 	 	 $temp_theadMargins = $this->theadMargins;
3638  	 	 	 $lasth = $this->lasth;
3639  	 	 	 $this->_outSaveGraphicsState();
3640  	 	 	 $this->rMargin = $this->original_rMargin;
3641  	 	 	 $this->lMargin = $this->original_lMargin;
3642  	 	 	 $this->SetCellPadding(0);
3643  	 	 	 //set current position
3644  	 	 	 $footer_y = $this->h - $this->footer_margin;
3645  	 	 	 if ($this->rtl) {
3646  	 	 	 	 $this->SetXY($this->original_rMargin, $footer_y);
3647  	 	 	 } else {
3648  	 	 	 	 $this->SetXY($this->original_lMargin, $footer_y);
3649  	 	 	 }
3650  	 	 	 $this->SetFont($this->footer_font[0], $this->footer_font[1], $this->footer_font[2]);
3651  	 	 	 $this->Footer();
3652  	 	 	 //restore position
3653  	 	 	 if ($this->rtl) {
3654  	 	 	 	 $this->SetXY($this->original_rMargin, $this->tMargin);
3655  	 	 	 } else {
3656  	 	 	 	 $this->SetXY($this->original_lMargin, $this->tMargin);
3657  	 	 	 }
3658  	 	 	 $this->_outRestoreGraphicsState();
3659  	 	 	 $this->lasth = $lasth;
3660  	 	 	 $this->thead = $temp_thead;
3661  	 	 	 $this->theadMargins = $temp_theadMargins;
3662  	 	 }
3663  	 	 // restore graphic settings
3664  	 	 $this->setGraphicVars($gvars);
3665  	 	 $this->current_column = $gvars['current_column'];
3666  	 	 $this->num_columns = $gvars['num_columns'];
3667  	 	 // calculate footer length
3668  	 	 $this->footerlen[$this->page] = $this->pagelen[$this->page] - $this->footerpos[$this->page] + 1;
3669  	 	 $this->InFooter = false;
3670  	 }
3671  
3672  	 /**
3673  	  * Check if we are on the page body (excluding page header and footer).
3674  	  * @return true if we are not in page header nor in page footer, false otherwise.
3675  	  * @protected
3676  	  * @since 5.9.091 (2011-06-15)
3677  	  */
3678  	protected function inPageBody() {
3679  	 	 return (($this->InHeader === false) AND ($this->InFooter === false));
3680  	 }
3681  
3682  	 /**
3683  	  * This method is used to render the table header on new page (if any).
3684  	  * @protected
3685  	  * @since 4.5.030 (2009-03-25)
3686  	  */
3687  	protected function setTableHeader() {
3688  	 	 if ($this->num_columns > 1) {
3689  	 	 	 // multi column mode
3690  	 	 	 return;
3691  	 	 }
3692  	 	 if (isset($this->theadMargins['top'])) {
3693  	 	 	 // restore the original top-margin
3694  	 	 	 $this->tMargin = $this->theadMargins['top'];
3695  	 	 	 $this->pagedim[$this->page]['tm'] = $this->tMargin;
3696  	 	 	 $this->y = $this->tMargin;
3697  	 	 }
3698  	 	 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
3699  	 	 	 // set margins
3700  	 	 	 $prev_lMargin = $this->lMargin;
3701  	 	 	 $prev_rMargin = $this->rMargin;
3702  	 	 	 $prev_cell_padding = $this->cell_padding;
3703  	 	 	 $this->lMargin = $this->theadMargins['lmargin'] + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$this->theadMargins['page']]['olm']);
3704  	 	 	 $this->rMargin = $this->theadMargins['rmargin'] + ($this->pagedim[$this->page]['orm'] - $this->pagedim[$this->theadMargins['page']]['orm']);
3705  	 	 	 $this->cell_padding = $this->theadMargins['cell_padding'];
3706  	 	 	 if ($this->rtl) {
3707  	 	 	 	 $this->x = $this->w - $this->rMargin;
3708  	 	 	 } else {
3709  	 	 	 	 $this->x = $this->lMargin;
3710  	 	 	 }
3711  	 	 	 // account for special "cell" mode
3712  	 	 	 if ($this->theadMargins['cell']) {
3713  	 	 	 	 if ($this->rtl) {
3714  	 	 	 	 	 $this->x -= $this->cell_padding['R'];
3715  	 	 	 	 } else {
3716  	 	 	 	 	 $this->x += $this->cell_padding['L'];
3717  	 	 	 	 }
3718  	 	 	 }
3719  	 	 	 $gvars = $this->getGraphicVars();
3720  	 	 	 if (!empty($this->theadMargins['gvars'])) {
3721  	 	 	 	 // set the correct graphic style
3722  	 	 	 	 $this->setGraphicVars($this->theadMargins['gvars']);
3723  	 	 	 	 $this->rMargin = $gvars['rMargin'];
3724  	 	 	 	 $this->lMargin = $gvars['lMargin'];
3725  	 	 	 }
3726  	 	 	 // print table header
3727  	 	 	 $this->writeHTML($this->thead, false, false, false, false, '');
3728  	 	 	 $this->setGraphicVars($gvars);
3729  	 	 	 // set new top margin to skip the table headers
3730  	 	 	 if (!isset($this->theadMargins['top'])) {
3731  	 	 	 	 $this->theadMargins['top'] = $this->tMargin;
3732  	 	 	 }
3733  	 	 	 // store end of header position
3734  	 	 	 if (!isset($this->columns[0]['th'])) {
3735  	 	 	 	 $this->columns[0]['th'] = array();
3736  	 	 	 }
3737  	 	 	 $this->columns[0]['th']['\''.$this->page.'\''] = $this->y;
3738  	 	 	 $this->tMargin = $this->y;
3739  	 	 	 $this->pagedim[$this->page]['tm'] = $this->tMargin;
3740  	 	 	 $this->lasth = 0;
3741  	 	 	 $this->lMargin = $prev_lMargin;
3742  	 	 	 $this->rMargin = $prev_rMargin;
3743  	 	 	 $this->cell_padding = $prev_cell_padding;
3744  	 	 }
3745  	 }
3746  
3747  	 /**
3748  	  * Returns the current page number.
3749  	  * @return int page number
3750  	  * @public
3751  	  * @since 1.0
3752  	  * @see getAliasNbPages()
3753  	  */
3754  	public function PageNo() {
3755  	 	 return $this->page;
3756  	 }
3757  
3758  	 /**
3759  	  * Returns the array of spot colors.
3760  	  * @return (array) Spot colors array.
3761  	  * @public
3762  	  * @since 6.0.038 (2013-09-30)
3763  	  */
3764  	public function getAllSpotColors() {
3765  	 	 return $this->spot_colors;
3766  	 }
3767  
3768  	 /**
3769  	  * Defines a new spot color.
3770  	  * It can be expressed in RGB components or gray scale.
3771  	  * The method can be called before the first page is created and the value is retained from page to page.
3772  	  * @param $name (string) Full name of the spot color.
3773  	  * @param $c (float) Cyan color for CMYK. Value between 0 and 100.
3774  	  * @param $m (float) Magenta color for CMYK. Value between 0 and 100.
3775  	  * @param $y (float) Yellow color for CMYK. Value between 0 and 100.
3776  	  * @param $k (float) Key (Black) color for CMYK. Value between 0 and 100.
3777  	  * @public
3778  	  * @since 4.0.024 (2008-09-12)
3779  	  * @see SetDrawSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3780  	  */
3781  	public function AddSpotColor($name, $c, $m, $y, $k) {
3782  	 	 if (!isset($this->spot_colors[$name])) {
3783  	 	 	 $i = (1 + count($this->spot_colors));
3784  	 	 	 $this->spot_colors[$name] = array('C' => $c, 'M' => $m, 'Y' => $y, 'K' => $k, 'name' => $name, 'i' => $i);
3785  	 	 }
3786  	 }
3787  
3788  	 /**
3789  	  * Set the spot color for the specified type ('draw', 'fill', 'text').
3790  	  * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3791  	  * @param $name (string) Name of the spot color.
3792  	  * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3793  	  * @return (string) PDF color command.
3794  	  * @public
3795  	  * @since 5.9.125 (2011-10-03)
3796  	  */
3797  	public function setSpotColor($type, $name, $tint=100) {
3798  	 	 $spotcolor = TCPDF_COLORS::getSpotColor($name, $this->spot_colors);
3799  	 	 if ($spotcolor === false) {
3800  	 	 	 $this->Error('Undefined spot color: '.$name.', you must add it using the AddSpotColor() method.');
3801  	 	 }
3802  	 	 $tint = (max(0, min(100, $tint)) / 100);
3803  	 	 $pdfcolor = sprintf('/CS%d ', $this->spot_colors[$name]['i']);
3804  	 	 switch ($type) {
3805  	 	 	 case 'draw': {
3806  	 	 	 	 $pdfcolor .= sprintf('CS %F SCN', $tint);
3807  	 	 	 	 $this->DrawColor = $pdfcolor;
3808  	 	 	 	 $this->strokecolor = $spotcolor;
3809  	 	 	 	 break;
3810  	 	 	 }
3811  	 	 	 case 'fill': {
3812  	 	 	 	 $pdfcolor .= sprintf('cs %F scn', $tint);
3813  	 	 	 	 $this->FillColor = $pdfcolor;
3814  	 	 	 	 $this->bgcolor = $spotcolor;
3815  	 	 	 	 break;
3816  	 	 	 }
3817  	 	 	 case 'text': {
3818  	 	 	 	 $pdfcolor .= sprintf('cs %F scn', $tint);
3819  	 	 	 	 $this->TextColor = $pdfcolor;
3820  	 	 	 	 $this->fgcolor = $spotcolor;
3821  	 	 	 	 break;
3822  	 	 	 }
3823  	 	 }
3824  	 	 $this->ColorFlag = ($this->FillColor != $this->TextColor);
3825  	 	 if ($this->state == 2) {
3826  	 	 	 $this->_out($pdfcolor);
3827  	 	 }
3828  	 	 if ($this->inxobj) {
3829  	 	 	 // we are inside an XObject template
3830  	 	 	 $this->xobjects[$this->xobjid]['spot_colors'][$name] = $this->spot_colors[$name];
3831  	 	 }
3832  	 	 return $pdfcolor;
3833  	 }
3834  
3835  	 /**
3836  	  * Defines the spot color used for all drawing operations (lines, rectangles and cell borders).
3837  	  * @param $name (string) Name of the spot color.
3838  	  * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3839  	  * @public
3840  	  * @since 4.0.024 (2008-09-12)
3841  	  * @see AddSpotColor(), SetFillSpotColor(), SetTextSpotColor()
3842  	  */
3843  	public function SetDrawSpotColor($name, $tint=100) {
3844  	 	 $this->setSpotColor('draw', $name, $tint);
3845  	 }
3846  
3847  	 /**
3848  	  * Defines the spot color used for all filling operations (filled rectangles and cell backgrounds).
3849  	  * @param $name (string) Name of the spot color.
3850  	  * @param $tint (float) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3851  	  * @public
3852  	  * @since 4.0.024 (2008-09-12)
3853  	  * @see AddSpotColor(), SetDrawSpotColor(), SetTextSpotColor()
3854  	  */
3855  	public function SetFillSpotColor($name, $tint=100) {
3856  	 	 $this->setSpotColor('fill', $name, $tint);
3857  	 }
3858  
3859  	 /**
3860  	  * Defines the spot color used for text.
3861  	  * @param $name (string) Name of the spot color.
3862  	  * @param $tint (int) Intensity of the color (from 0 to 100 ; 100 = full intensity by default).
3863  	  * @public
3864  	  * @since 4.0.024 (2008-09-12)
3865  	  * @see AddSpotColor(), SetDrawSpotColor(), SetFillSpotColor()
3866  	  */
3867  	public function SetTextSpotColor($name, $tint=100) {
3868  	 	 $this->setSpotColor('text', $name, $tint);
3869  	 }
3870  
3871  	 /**
3872  	  * Set the color array for the specified type ('draw', 'fill', 'text').
3873  	  * It can be expressed in RGB, CMYK or GRAY SCALE components.
3874  	  * The method can be called before the first page is created and the value is retained from page to page.
3875  	  * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3876  	  * @param $color (array) Array of colors (1=gray, 3=RGB, 4=CMYK or 5=spotcolor=CMYK+name values).
3877  	  * @param $ret (boolean) If true do not send the PDF command.
3878  	  * @return (string) The PDF command or empty string.
3879  	  * @public
3880  	  * @since 3.1.000 (2008-06-11)
3881  	  */
3882  	public function setColorArray($type, $color, $ret=false) {
3883  	 	 if (is_array($color)) {
3884  	 	 	 $color = array_values($color);
3885  	 	 	 // component: grey, RGB red or CMYK cyan
3886  	 	 	 $c = isset($color[0]) ? $color[0] : -1;
3887  	 	 	 // component: RGB green or CMYK magenta
3888  	 	 	 $m = isset($color[1]) ? $color[1] : -1;
3889  	 	 	 // component: RGB blue or CMYK yellow
3890  	 	 	 $y = isset($color[2]) ? $color[2] : -1;
3891  	 	 	 // component: CMYK black
3892  	 	 	 $k = isset($color[3]) ? $color[3] : -1;
3893  	 	 	 // color name
3894  	 	 	 $name = isset($color[4]) ? $color[4] : '';
3895  	 	 	 if ($c >= 0) {
3896  	 	 	 	 return $this->setColor($type, $c, $m, $y, $k, $ret, $name);
3897  	 	 	 }
3898  	 	 }
3899  	 	 return '';
3900  	 }
3901  
3902  	 /**
3903  	  * Defines the color used for all drawing operations (lines, rectangles and cell borders).
3904  	  * It can be expressed in RGB, CMYK or GRAY SCALE components.
3905  	  * The method can be called before the first page is created and the value is retained from page to page.
3906  	  * @param $color (array) Array of colors (1, 3 or 4 values).
3907  	  * @param $ret (boolean) If true do not send the PDF command.
3908  	  * @return string the PDF command
3909  	  * @public
3910  	  * @since 3.1.000 (2008-06-11)
3911  	  * @see SetDrawColor()
3912  	  */
3913  	public function SetDrawColorArray($color, $ret=false) {
3914  	 	 return $this->setColorArray('draw', $color, $ret);
3915  	 }
3916  
3917  	 /**
3918  	  * Defines the color used for all filling operations (filled rectangles and cell backgrounds).
3919  	  * It can be expressed in RGB, CMYK or GRAY SCALE components.
3920  	  * The method can be called before the first page is created and the value is retained from page to page.
3921  	  * @param $color (array) Array of colors (1, 3 or 4 values).
3922  	  * @param $ret (boolean) If true do not send the PDF command.
3923  	  * @public
3924  	  * @since 3.1.000 (2008-6-11)
3925  	  * @see SetFillColor()
3926  	  */
3927  	public function SetFillColorArray($color, $ret=false) {
3928  	 	 return $this->setColorArray('fill', $color, $ret);
3929  	 }
3930  
3931  	 /**
3932  	  * Defines the color used for text. It can be expressed in RGB components or gray scale.
3933  	  * The method can be called before the first page is created and the value is retained from page to page.
3934  	  * @param $color (array) Array of colors (1, 3 or 4 values).
3935  	  * @param $ret (boolean) If true do not send the PDF command.
3936  	  * @public
3937  	  * @since 3.1.000 (2008-6-11)
3938  	  * @see SetFillColor()
3939  	  */
3940  	public function SetTextColorArray($color, $ret=false) {
3941  	 	 return $this->setColorArray('text', $color, $ret);
3942  	 }
3943  
3944  	 /**
3945  	  * Defines the color used by the specified type ('draw', 'fill', 'text').
3946  	  * @param $type (string) Type of object affected by this color: ('draw', 'fill', 'text').
3947  	  * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
3948  	  * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
3949  	  * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
3950  	  * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
3951  	  * @param $ret (boolean) If true do not send the command.
3952  	  * @param $name (string) spot color name (if any)
3953  	  * @return (string) The PDF command or empty string.
3954  	  * @public
3955  	  * @since 5.9.125 (2011-10-03)
3956  	  */
3957  	public function setColor($type, $col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
3958  	 	 // set default values
3959  	 	 if (!is_numeric($col1)) {
3960  	 	 	 $col1 = 0;
3961  	 	 }
3962  	 	 if (!is_numeric($col2)) {
3963  	 	 	 $col2 = -1;
3964  	 	 }
3965  	 	 if (!is_numeric($col3)) {
3966  	 	 	 $col3 = -1;
3967  	 	 }
3968  	 	 if (!is_numeric($col4)) {
3969  	 	 	 $col4 = -1;
3970  	 	 }
3971  	 	 // set color by case
3972  	 	 $suffix = '';
3973  	 	 if (($col2 == -1) AND ($col3 == -1) AND ($col4 == -1)) {
3974  	 	 	 // Grey scale
3975  	 	 	 $col1 = max(0, min(255, $col1));
3976  	 	 	 $intcolor = array('G' => $col1);
3977  	 	 	 $pdfcolor = sprintf('%F ', ($col1 / 255));
3978  	 	 	 $suffix = 'g';
3979  	 	 } elseif ($col4 == -1) {
3980  	 	 	 // RGB
3981  	 	 	 $col1 = max(0, min(255, $col1));
3982  	 	 	 $col2 = max(0, min(255, $col2));
3983  	 	 	 $col3 = max(0, min(255, $col3));
3984  	 	 	 $intcolor = array('R' => $col1, 'G' => $col2, 'B' => $col3);
3985  	 	 	 $pdfcolor = sprintf('%F %F %F ', ($col1 / 255), ($col2 / 255), ($col3 / 255));
3986  	 	 	 $suffix = 'rg';
3987  	 	 } else {
3988  	 	 	 $col1 = max(0, min(100, $col1));
3989  	 	 	 $col2 = max(0, min(100, $col2));
3990  	 	 	 $col3 = max(0, min(100, $col3));
3991  	 	 	 $col4 = max(0, min(100, $col4));
3992  	 	 	 if (empty($name)) {
3993  	 	 	 	 // CMYK
3994  	 	 	 	 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4);
3995  	 	 	 	 $pdfcolor = sprintf('%F %F %F %F ', ($col1 / 100), ($col2 / 100), ($col3 / 100), ($col4 / 100));
3996  	 	 	 	 $suffix = 'k';
3997  	 	 	 } else {
3998  	 	 	 	 // SPOT COLOR
3999  	 	 	 	 $intcolor = array('C' => $col1, 'M' => $col2, 'Y' => $col3, 'K' => $col4, 'name' => $name);
4000  	 	 	 	 $this->AddSpotColor($name, $col1, $col2, $col3, $col4);
4001  	 	 	 	 $pdfcolor = $this->setSpotColor($type, $name, 100);
4002  	 	 	 }
4003  	 	 }
4004  	 	 switch ($type) {
4005  	 	 	 case 'draw': {
4006  	 	 	 	 $pdfcolor .= strtoupper($suffix);
4007  	 	 	 	 $this->DrawColor = $pdfcolor;
4008  	 	 	 	 $this->strokecolor = $intcolor;
4009  	 	 	 	 break;
4010  	 	 	 }
4011  	 	 	 case 'fill': {
4012  	 	 	 	 $pdfcolor .= $suffix;
4013  	 	 	 	 $this->FillColor = $pdfcolor;
4014  	 	 	 	 $this->bgcolor = $intcolor;
4015  	 	 	 	 break;
4016  	 	 	 }
4017  	 	 	 case 'text': {
4018  	 	 	 	 $pdfcolor .= $suffix;
4019  	 	 	 	 $this->TextColor = $pdfcolor;
4020  	 	 	 	 $this->fgcolor = $intcolor;
4021  	 	 	 	 break;
4022  	 	 	 }
4023  	 	 }
4024  	 	 $this->ColorFlag = ($this->FillColor != $this->TextColor);
4025  	 	 if (($type != 'text') AND ($this->state == 2)) {
4026  	 	 	 if (!$ret) {
4027  	 	 	 	 $this->_out($pdfcolor);
4028  	 	 	 }
4029  	 	 	 return $pdfcolor;
4030  	 	 }
4031  	 	 return '';
4032  	 }
4033  
4034  	 /**
4035  	  * 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.
4036  	  * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4037  	  * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4038  	  * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4039  	  * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4040  	  * @param $ret (boolean) If true do not send the command.
4041  	  * @param $name (string) spot color name (if any)
4042  	  * @return string the PDF command
4043  	  * @public
4044  	  * @since 1.3
4045  	  * @see SetDrawColorArray(), SetFillColor(), SetTextColor(), Line(), Rect(), Cell(), MultiCell()
4046  	  */
4047  	public function SetDrawColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4048  	 	 return $this->setColor('draw', $col1, $col2, $col3, $col4, $ret, $name);
4049  	 }
4050  
4051  	 /**
4052  	  * 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.
4053  	  * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4054  	  * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4055  	  * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4056  	  * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4057  	  * @param $ret (boolean) If true do not send the command.
4058  	  * @param $name (string) Spot color name (if any).
4059  	  * @return (string) The PDF command.
4060  	  * @public
4061  	  * @since 1.3
4062  	  * @see SetFillColorArray(), SetDrawColor(), SetTextColor(), Rect(), Cell(), MultiCell()
4063  	  */
4064  	public function SetFillColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4065  	 	 return $this->setColor('fill', $col1, $col2, $col3, $col4, $ret, $name);
4066  	 }
4067  
4068  	 /**
4069  	  * 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.
4070  	  * @param $col1 (float) GRAY level for single color, or Red color for RGB (0-255), or CYAN color for CMYK (0-100).
4071  	  * @param $col2 (float) GREEN color for RGB (0-255), or MAGENTA color for CMYK (0-100).
4072  	  * @param $col3 (float) BLUE color for RGB (0-255), or YELLOW color for CMYK (0-100).
4073  	  * @param $col4 (float) KEY (BLACK) color for CMYK (0-100).
4074  	  * @param $ret (boolean) If true do not send the command.
4075  	  * @param $name (string) Spot color name (if any).
4076  	  * @return (string) Empty string.
4077  	  * @public
4078  	  * @since 1.3
4079  	  * @see SetTextColorArray(), SetDrawColor(), SetFillColor(), Text(), Cell(), MultiCell()
4080  	  */
4081  	public function SetTextColor($col1=0, $col2=-1, $col3=-1, $col4=-1, $ret=false, $name='') {
4082  	 	 return $this->setColor('text', $col1, $col2, $col3, $col4, $ret, $name);
4083  	 }
4084  
4085  	 /**
4086  	  * Returns the length of a string in user unit. A font must be selected.<br>
4087  	  * @param $s (string) The string whose length is to be computed
4088  	  * @param $fontname (string) 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.
4089  	  * @param $fontstyle (string) 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-through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4090  	  * @param $fontsize (float) Font size in points. The default value is the current size.
4091  	  * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4092  	  * @return mixed int total string length or array of characted widths
4093  	  * @author Nicola Asuni
4094  	  * @public
4095  	  * @since 1.2
4096  	  */
4097  	public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4098  	 	 return $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont), $s, $this->tmprtl, $this->isunicode, $this->CurrentFont), $fontname, $fontstyle, $fontsize, $getarray);
4099  	 }
4100  
4101  	 /**
4102  	  * Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
4103  	  * @param $sa (string) The array of chars whose total length is to be computed
4104  	  * @param $fontname (string) 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.
4105  	  * @param $fontstyle (string) 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 through</li><li>O: overline</li></ul> or any combination. The default value is regular.
4106  	  * @param $fontsize (float) Font size in points. The default value is the current size.
4107  	  * @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
4108  	  * @return mixed int total string length or array of characted widths
4109  	  * @author Nicola Asuni
4110  	  * @public
4111  	  * @since 2.4.000 (2008-03-06)
4112  	  */
4113  	public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
4114  	 	 // store current values
4115  	 	 if (!TCPDF_STATIC::empty_string($fontname)) {
4116  	 	 	 $prev_FontFamily = $this->FontFamily;
4117  	 	 	 $prev_FontStyle = $this->FontStyle;
4118  	 	 	 $prev_FontSizePt = $this->FontSizePt;
4119  	 	 	 $this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
4120  	 	 }
4121  	 	 // convert UTF-8 array to Latin1 if required
4122  	 	 if ($this->isunicode AND (!$this->isUnicodeFont())) {
4123  	 	 	 $sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa);
4124  	 	 }
4125  	 	 $w = 0; // total width
4126  	 	 $wa = array(); // array of characters widths
4127  	 	 foreach ($sa as $ck => $char) {
4128  	 	 	 // character width
4129  	 	 	 $cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
4130  	 	 	 $wa[] = $cw;
4131  	 	 	 $w += $cw;
4132  	 	 }
4133  	 	 // restore previous values
4134  	 	 if (!TCPDF_STATIC::empty_string($fontname)) {
4135  	 	 	 $this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
4136  	 	 }
4137  	 	 if ($getarray) {
4138  	 	 	 return $wa;
4139  	 	 }
4140  	 	 return $w;
4141  	 }
4142  
4143  	 /**
4144  	  * Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
4145  	  * @param $char (int) The char code whose length is to be returned
4146  	  * @param $notlast (boolean) If false ignore the font-spacing.
4147  	  * @return float char width
4148  	  * @author Nicola Asuni
4149  	  * @public
4150  	  * @since 2.4.000 (2008-03-06)
4151  	  */
4152  	public function GetCharWidth($char, $notlast=true) {
4153  	 	 // get raw width
4154  	 	 $chw = $this->getRawCharWidth($char);
4155  	 	 if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
4156  	 	 	 // increase/decrease font spacing
4157  	 	 	 $chw += $this->font_spacing;
4158  	 	 }
4159  	 	 if ($this->font_stretching != 100) {
4160  	 	 	 // fixed stretching mode
4161  	 	 	 $chw *= ($this->font_stretching / 100);
4162  	 	 }
4163  	 	 return $chw;
4164  	 }
4165  
4166  	 /**
4167  	  * Returns the length of the char in user unit for the current font.
4168  	  * @param $char (int) The char code whose length is to be returned
4169  	  * @return float char width
4170  	  * @author Nicola Asuni
4171  	  * @public
4172  	  * @since 5.9.000 (2010-09-28)
4173  	  */
4174  	public function getRawCharWidth($char) {
4175  	 	 if ($char == 173) {
4176  	 	 	 // SHY character will not be printed
4177  	 	 	 return (0);
4178  	 	 }
4179  	 	 if (isset($this->CurrentFont['cw'][$char])) {
4180  	 	 	 $w = $this->CurrentFont['cw'][$char];
4181  	 	 } elseif (isset($this->CurrentFont['dw'])) {
4182  	 	 	 // default width
4183  	 	 	 $w = $this->CurrentFont['dw'];
4184  	 	 } elseif (isset($this->CurrentFont['cw'][32])) {
4185  	 	 	 // default width
4186  	 	 	 $w = $this->CurrentFont['cw'][32];
4187  	 	 } else {
4188  	 	 	 $w = 600;
4189  	 	 }
4190  	 	 return $this->getAbsFontMeasure($w);
4191  	 }
4192  
4193  	 /**
4194  	  * Returns the numbero of characters in a string.
4195  	  * @param $s (string) The input string.
4196  	  * @return int number of characters
4197  	  * @public
4198  	  * @since 2.0.0001 (2008-01-07)
4199  	  */
4200  	public function GetNumChars($s) {
4201  	 	 if ($this->isUnicodeFont()) {
4202  	 	 	 return count(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont));
4203  	 	 }
4204  	 	 return strlen($s);
4205  	 }
4206  
4207  	 /**
4208  	  * Fill the list of available fonts ($this->fontlist).
4209  	  * @protected
4210  	  * @since 4.0.013 (2008-07-28)
4211  	  */
4212  	protected function getFontsList() {
4213  	 	 if (($fontsdir = opendir(TCPDF_FONTS::_getfontpath())) !== false) {
4214  	 	 	 while (($file = readdir($fontsdir)) !== false) {
4215  	 	 	 	 if (substr($file, -4) == '.php') {
4216  	 	 	 	 	 array_push($this->fontlist, strtolower(basename($file, '.php')));
4217  	 	 	 	 }
4218  	 	 	 }
4219  	 	 	 closedir($fontsdir);
4220  	 	 }
4221  	 }
4222  
4223  	 /**
4224  	  * Imports a TrueType, Type1, core, or CID0 font and makes it available.
4225  	  * It is necessary to generate a font definition file first (read /fonts/utils/README.TXT).
4226  	  * 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.
4227  	  * @param $family (string) Font family. The name can be chosen arbitrarily. If it is a standard family name, it will override the corresponding font.
4228  	  * @param $style (string) 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>
4229  	  * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4230  	  * @return array containing the font data, or false in case of error.
4231  	  * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4232  	  * @public
4233  	  * @since 1.5
4234  	  * @see SetFont(), setFontSubsetting()
4235  	  */
4236  	public function AddFont($family, $style='', $fontfile='', $subset='default') {
4237  	 	 if ($subset === 'default') {
4238  	 	 	 $subset = $this->font_subsetting;
4239  	 	 }
4240  	 	 if ($this->pdfa_mode) {
4241  	 	 	 $subset = false;
4242  	 	 }
4243  	 	 if (TCPDF_STATIC::empty_string($family)) {
4244  	 	 	 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
4245  	 	 	 	 $family = $this->FontFamily;
4246  	 	 	 } else {
4247  	 	 	 	 $this->Error('Empty font family');
4248  	 	 	 }
4249  	 	 }
4250  	 	 // move embedded styles on $style
4251  	 	 if (substr($family, -1) == 'I') {
4252  	 	 	 $style .= 'I';
4253  	 	 	 $family = substr($family, 0, -1);
4254  	 	 }
4255  	 	 if (substr($family, -1) == 'B') {
4256  	 	 	 $style .= 'B';
4257  	 	 	 $family = substr($family, 0, -1);
4258  	 	 }
4259  	 	 // normalize family name
4260  	 	 $family = strtolower($family);
4261  	 	 if ((!$this->isunicode) AND ($family == 'arial')) {
4262  	 	 	 $family = 'helvetica';
4263  	 	 }
4264  	 	 if (($family == 'symbol') OR ($family == 'zapfdingbats')) {
4265  	 	 	 $style = '';
4266  	 	 }
4267  	 	 if ($this->pdfa_mode AND (isset($this->CoreFonts[$family]))) {
4268  	 	 	 // all fonts must be embedded
4269  	 	 	 $family = 'pdfa'.$family;
4270  	 	 }
4271  	 	 $tempstyle = strtoupper($style);
4272  	 	 $style = '';
4273  	 	 // underline
4274  	 	 if (strpos($tempstyle, 'U') !== false) {
4275  	 	 	 $this->underline = true;
4276  	 	 } else {
4277  	 	 	 $this->underline = false;
4278  	 	 }
4279  	 	 // line-through (deleted)
4280  	 	 if (strpos($tempstyle, 'D') !== false) {
4281  	 	 	 $this->linethrough = true;
4282  	 	 } else {
4283  	 	 	 $this->linethrough = false;
4284  	 	 }
4285  	 	 // overline
4286  	 	 if (strpos($tempstyle, 'O') !== false) {
4287  	 	 	 $this->overline = true;
4288  	 	 } else {
4289  	 	 	 $this->overline = false;
4290  	 	 }
4291  	 	 // bold
4292  	 	 if (strpos($tempstyle, 'B') !== false) {
4293  	 	 	 $style .= 'B';
4294  	 	 }
4295  	 	 // oblique
4296  	 	 if (strpos($tempstyle, 'I') !== false) {
4297  	 	 	 $style .= 'I';
4298  	 	 }
4299  	 	 $bistyle = $style;
4300  	 	 $fontkey = $family.$style;
4301  	 	 $font_style = $style.($this->underline ? 'U' : '').($this->linethrough ? 'D' : '').($this->overline ? 'O' : '');
4302  	 	 $fontdata = array('fontkey' => $fontkey, 'family' => $family, 'style' => $font_style);
4303  	 	 // check if the font has been already added
4304  	 	 $fb = $this->getFontBuffer($fontkey);
4305  	 	 if ($fb !== false) {
4306  	 	 	 if ($this->inxobj) {
4307  	 	 	 	 // we are inside an XObject template
4308  	 	 	 	 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $fb['i'];
4309  	 	 	 }
4310  	 	 	 return $fontdata;
4311  	 	 }
4312  	 	 // get specified font directory (if any)
4313  	 	 $fontdir = false;
4314  	 	 if (!TCPDF_STATIC::empty_string($fontfile)) {
4315  	 	 	 $fontdir = dirname($fontfile);
4316  	 	 	 if (TCPDF_STATIC::empty_string($fontdir) OR ($fontdir == '.')) {
4317  	 	 	 	 $fontdir = '';
4318  	 	 	 } else {
4319  	 	 	 	 $fontdir .= '/';
4320  	 	 	 }
4321  	 	 }
4322  	 	 // true when the font style variation is missing
4323  	 	 $missing_style = false;
4324  	 	 // search and include font file
4325  	 	 if (TCPDF_STATIC::empty_string($fontfile) OR (!@TCPDF_STATIC::file_exists($fontfile))) {
4326  	 	 	 // build a standard filenames for specified font
4327  	 	 	 $tmp_fontfile = str_replace(' ', '', $family).strtolower($style).'.php';
4328  	 	 	 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4329  	 	 	 if (TCPDF_STATIC::empty_string($fontfile)) {
4330  	 	 	 	 $missing_style = true;
4331  	 	 	 	 // try to remove the style part
4332  	 	 	 	 $tmp_fontfile = str_replace(' ', '', $family).'.php';
4333  	 	 	 	 $fontfile = TCPDF_FONTS::getFontFullPath($tmp_fontfile, $fontdir);
4334  	 	 	 }
4335  	 	 }
4336  	 	 // include font file
4337  	 	 if (!TCPDF_STATIC::empty_string($fontfile) AND (@TCPDF_STATIC::file_exists($fontfile))) {
4338  	 	 	 include($fontfile);
4339  	 	 } else {
4340  	 	 	 $this->Error('Could not include font definition file: '.$family.'');
4341  	 	 }
4342  	 	 // check font parameters
4343  	 	 if ((!isset($type)) OR (!isset($cw))) {
4344  	 	 	 $this->Error('The font definition file has a bad format: '.$fontfile.'');
4345  	 	 }
4346  	 	 // SET default parameters
4347  	 	 if (!isset($file) OR TCPDF_STATIC::empty_string($file)) {
4348  	 	 	 $file = '';
4349  	 	 }
4350  	 	 if (!isset($enc) OR TCPDF_STATIC::empty_string($enc)) {
4351  	 	 	 $enc = '';
4352  	 	 }
4353  	 	 if (!isset($cidinfo) OR TCPDF_STATIC::empty_string($cidinfo)) {
4354  	 	 	 $cidinfo = array('Registry'=>'Adobe', 'Ordering'=>'Identity', 'Supplement'=>0);
4355  	 	 	 $cidinfo['uni2cid'] = array();
4356  	 	 }
4357  	 	 if (!isset($ctg) OR TCPDF_STATIC::empty_string($ctg)) {
4358  	 	 	 $ctg = '';
4359  	 	 }
4360  	 	 if (!isset($desc) OR TCPDF_STATIC::empty_string($desc)) {
4361  	 	 	 $desc = array();
4362  	 	 }
4363  	 	 if (!isset($up) OR TCPDF_STATIC::empty_string($up)) {
4364  	 	 	 $up = -100;
4365  	 	 }
4366  	 	 if (!isset($ut) OR TCPDF_STATIC::empty_string($ut)) {
4367  	 	 	 $ut = 50;
4368  	 	 }
4369  	 	 if (!isset($cw) OR TCPDF_STATIC::empty_string($cw)) {
4370  	 	 	 $cw = array();
4371  	 	 }
4372  	 	 if (!isset($dw) OR TCPDF_STATIC::empty_string($dw)) {
4373  	 	 	 // set default width
4374  	 	 	 if (isset($desc['MissingWidth']) AND ($desc['MissingWidth'] > 0)) {
4375  	 	 	 	 $dw = $desc['MissingWidth'];
4376  	 	 	 } elseif (isset($cw[32])) {
4377  	 	 	 	 $dw = $cw[32];
4378  	 	 	 } else {
4379  	 	 	 	 $dw = 600;
4380  	 	 	 }
4381  	 	 }
4382  	 	 ++$this->numfonts;
4383  	 	 if ($type == 'core') {
4384  	 	 	 $name = $this->CoreFonts[$fontkey];
4385  	 	 	 $subset = false;
4386  	 	 } elseif (($type == 'TrueType') OR ($type == 'Type1')) {
4387  	 	 	 $subset = false;
4388  	 	 } elseif ($type == 'TrueTypeUnicode') {
4389  	 	 	 $enc = 'Identity-H';
4390  	 	 } elseif ($type == 'cidfont0') {
4391  	 	 	 if ($this->pdfa_mode) {
4392  	 	 	 	 $this->Error('All fonts must be embedded in PDF/A mode!');
4393  	 	 	 }
4394  	 	 } else {
4395  	 	 	 $this->Error('Unknow font type: '.$type.'');
4396  	 	 }
4397  	 	 // set name if unset
4398  	 	 if (!isset($name) OR empty($name)) {
4399  	 	 	 $name = $fontkey;
4400  	 	 }
4401  	 	 // create artificial font style variations if missing (only works with non-embedded fonts)
4402  	 	 if (($type != 'core') AND $missing_style) {
4403  	 	 	 // style variations
4404  	 	 	 $styles = array('' => '', 'B' => ',Bold', 'I' => ',Italic', 'BI' => ',BoldItalic');
4405  	 	 	 $name .= $styles[$bistyle];
4406  	 	 	 // artificial bold
4407  	 	 	 if (strpos($bistyle, 'B') !== false) {
4408  	 	 	 	 if (isset($desc['StemV'])) {
4409  	 	 	 	 	 // from normal to bold
4410  	 	 	 	 	 $desc['StemV'] = round($desc['StemV'] * 1.75);
4411  	 	 	 	 } else {
4412  	 	 	 	 	 // bold
4413  	 	 	 	 	 $desc['StemV'] = 123;
4414  	 	 	 	 }
4415  	 	 	 }
4416  	 	 	 // artificial italic
4417  	 	 	 if (strpos($bistyle, 'I') !== false) {
4418  	 	 	 	 if (isset($desc['ItalicAngle'])) {
4419  	 	 	 	 	 $desc['ItalicAngle'] -= 11;
4420  	 	 	 	 } else {
4421  	 	 	 	 	 $desc['ItalicAngle'] = -11;
4422  	 	 	 	 }
4423  	 	 	 	 if (isset($desc['Flags'])) {
4424  	 	 	 	 	 $desc['Flags'] |= 64; //bit 7
4425  	 	 	 	 } else {
4426  	 	 	 	 	 $desc['Flags'] = 64;
4427  	 	 	 	 }
4428  	 	 	 }
4429  	 	 }
4430  	 	 // check if the array of characters bounding boxes is defined
4431  	 	 if (!isset($cbbox)) {
4432  	 	 	 $cbbox = array();
4433  	 	 }
4434  	 	 // initialize subsetchars
4435  	 	 $subsetchars = array_fill(0, 255, true);
4436  	 	 $this->setFontBuffer($fontkey, array('fontkey' => $fontkey, 'i' => $this->numfonts, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'cbbox' => $cbbox, 'dw' => $dw, 'enc' => $enc, 'cidinfo' => $cidinfo, 'file' => $file, 'ctg' => $ctg, 'subset' => $subset, 'subsetchars' => $subsetchars));
4437  	 	 if ($this->inxobj) {
4438  	 	 	 // we are inside an XObject template
4439  	 	 	 $this->xobjects[$this->xobjid]['fonts'][$fontkey] = $this->numfonts;
4440  	 	 }
4441  	 	 if (isset($diff) AND (!empty($diff))) {
4442  	 	 	 //Search existing encodings
4443  	 	 	 $d = 0;
4444  	 	 	 $nb = count($this->diffs);
4445  	 	 	 for ($i=1; $i <= $nb; ++$i) {
4446  	 	 	 	 if ($this->diffs[$i] == $diff) {
4447  	 	 	 	 	 $d = $i;
4448  	 	 	 	 	 break;
4449  	 	 	 	 }
4450  	 	 	 }
4451  	 	 	 if ($d == 0) {
4452  	 	 	 	 $d = $nb + 1;
4453  	 	 	 	 $this->diffs[$d] = $diff;
4454  	 	 	 }
4455  	 	 	 $this->setFontSubBuffer($fontkey, 'diff', $d);
4456  	 	 }
4457  	 	 if (!TCPDF_STATIC::empty_string($file)) {
4458  	 	 	 if (!isset($this->FontFiles[$file])) {
4459  	 	 	 	 if ((strcasecmp($type,'TrueType') == 0) OR (strcasecmp($type, 'TrueTypeUnicode') == 0)) {
4460  	 	 	 	 	 $this->FontFiles[$file] = array('length1' => $originalsize, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4461  	 	 	 	 } elseif ($type != 'core') {
4462  	 	 	 	 	 $this->FontFiles[$file] = array('length1' => $size1, 'length2' => $size2, 'fontdir' => $fontdir, 'subset' => $subset, 'fontkeys' => array($fontkey));
4463  	 	 	 	 }
4464  	 	 	 } else {
4465  	 	 	 	 // update fontkeys that are sharing this font file
4466  	 	 	 	 $this->FontFiles[$file]['subset'] = ($this->FontFiles[$file]['subset'] AND $subset);
4467  	 	 	 	 if (!in_array($fontkey, $this->FontFiles[$file]['fontkeys'])) {
4468  	 	 	 	 	 $this->FontFiles[$file]['fontkeys'][] = $fontkey;
4469  	 	 	 	 }
4470  	 	 	 }
4471  	 	 }
4472  	 	 return $fontdata;
4473  	 }
4474  
4475  	 /**
4476  	  * Sets the font used to print character strings.
4477  	  * The font can be either a standard one or a font added via the AddFont() method. Standard fonts use Windows encoding cp1252 (Western Europe).
4478  	  * The method can be called before the first page is created and the font is retained from page to page.
4479  	  * If you just wish to change the current font size, it is simpler to call SetFontSize().
4480  	  * 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 />
4481  	  * @param $family (string) 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.
4482  	  * @param $style (string) 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 through</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.
4483  	  * @param $size (float) 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
4484  	  * @param $fontfile (string) The font definition file. By default, the name is built from the family and style, in lower case with no spaces.
4485  	  * @param $subset (mixed) if true embedd only a subset of the font (stores only the information related to the used characters); if false embedd full font; if 'default' uses the default value set using setFontSubsetting(). This option is valid only for TrueTypeUnicode fonts. If you want to enable users to change the document, set this parameter to false. If you subset the font, the person who receives your PDF would need to have your same font in order to make changes to your PDF. The file size of the PDF would also be smaller because you are embedding only part of a font.
4486  	  * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4487  	  * @author Nicola Asuni
4488  	  * @public
4489  	  * @since 1.0
4490  	  * @see AddFont(), SetFontSize()
4491  	  */
4492  	public function SetFont($family, $style='', $size=null, $fontfile='', $subset='default', $out=true) {
4493  	 	 //Select a font; size given in points
4494  	 	 if ($size === null) {
4495  	 	 	 $size = $this->FontSizePt;
4496  	 	 }
4497  	 	 if ($size < 0) {
4498  	 	 	 $size = 0;
4499  	 	 }
4500  	 	 // try to add font (if not already added)
4501  	 	 $fontdata = $this->AddFont($family, $style, $fontfile, $subset);
4502  	 	 $this->FontFamily = $fontdata['family'];
4503  	 	 $this->FontStyle = $fontdata['style'];
4504  	 	 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
4505  	 	 	 // save subset chars of the previous font
4506  	 	 	 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
4507  	 	 }
4508  	 	 $this->CurrentFont = $this->getFontBuffer($fontdata['fontkey']);
4509  	 	 $this->SetFontSize($size, $out);
4510  	 }
4511  
4512  	 /**
4513  	  * Defines the size of the current font.
4514  	  * @param $size (float) The font size in points.
4515  	  * @param $out (boolean) if true output the font size command, otherwise only set the font properties.
4516  	  * @public
4517  	  * @since 1.0
4518  	  * @see SetFont()
4519  	  */
4520  	public function SetFontSize($size, $out=true) {
4521  	 	 $size = (float)$size;
4522  	 	 // font size in points
4523  	 	 $this->FontSizePt = $size;
4524  	 	 // font size in user units
4525  	 	 $this->FontSize = $size / $this->k;
4526  	 	 // calculate some font metrics
4527  	 	 if (isset($this->CurrentFont['desc']['FontBBox'])) {
4528  	 	 	 $bbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4529  	 	 	 $font_height = ((intval($bbox[3]) - intval($bbox[1])) * $size / 1000);
4530  	 	 } else {
4531  	 	 	 $font_height = $size * 1.219;
4532  	 	 }
4533  	 	 if (isset($this->CurrentFont['desc']['Ascent']) AND ($this->CurrentFont['desc']['Ascent'] > 0)) {
4534  	 	 	 $font_ascent = ($this->CurrentFont['desc']['Ascent'] * $size / 1000);
4535  	 	 }
4536  	 	 if (isset($this->CurrentFont['desc']['Descent']) AND ($this->CurrentFont['desc']['Descent'] <= 0)) {
4537  	 	 	 $font_descent = (- $this->CurrentFont['desc']['Descent'] * $size / 1000);
4538  	 	 }
4539  	 	 if (!isset($font_ascent) AND !isset($font_descent)) {
4540  	 	 	 // core font
4541  	 	 	 $font_ascent = 0.76 * $font_height;
4542  	 	 	 $font_descent = $font_height - $font_ascent;
4543  	 	 } elseif (!isset($font_descent)) {
4544  	 	 	 $font_descent = $font_height - $font_ascent;
4545  	 	 } elseif (!isset($font_ascent)) {
4546  	 	 	 $font_ascent = $font_height - $font_descent;
4547  	 	 }
4548  	 	 $this->FontAscent = ($font_ascent / $this->k);
4549  	 	 $this->FontDescent = ($font_descent / $this->k);
4550  	 	 if ($out AND ($this->page > 0) AND (isset($this->CurrentFont['i'])) AND ($this->state == 2)) {
4551  	 	 	 $this->_out(sprintf('BT /F%d %F Tf ET', $this->CurrentFont['i'], $this->FontSizePt));
4552  	 	 }
4553  	 }
4554  
4555  	 /**
4556  	  * Returns the bounding box of the current font in user units.
4557  	  * @return array
4558  	  * @public
4559  	  * @since 5.9.152 (2012-03-23)
4560  	  */
4561  	public function getFontBBox() {
4562  	 	 $fbbox = array();
4563  	 	 if (isset($this->CurrentFont['desc']['FontBBox'])) {
4564  	 	 	 $tmpbbox = explode(' ', substr($this->CurrentFont['desc']['FontBBox'], 1, -1));
4565  	 	 	 $fbbox = array_map(array($this,'getAbsFontMeasure'), $tmpbbox);
4566  	 	 } else {
4567  	 	 	 // Find max width
4568  	 	 	 if (isset($this->CurrentFont['desc']['MaxWidth'])) {
4569  	 	 	 	 $maxw = $this->getAbsFontMeasure(intval($this->CurrentFont['desc']['MaxWidth']));
4570  	 	 	 } else {
4571  	 	 	 	 $maxw = 0;
4572  	 	 	 	 if (isset($this->CurrentFont['desc']['MissingWidth'])) {
4573  	 	 	 	 	 $maxw = max($maxw, $this->CurrentFont['desc']['MissingWidth']);
4574  	 	 	 	 }
4575  	 	 	 	 if (isset($this->CurrentFont['desc']['AvgWidth'])) {
4576  	 	 	 	 	 $maxw = max($maxw, $this->CurrentFont['desc']['AvgWidth']);
4577  	 	 	 	 }
4578  	 	 	 	 if (isset($this->CurrentFont['dw'])) {
4579  	 	 	 	 	 $maxw = max($maxw, $this->CurrentFont['dw']);
4580  	 	 	 	 }
4581  	 	 	 	 foreach ($this->CurrentFont['cw'] as $char => $w) {
4582  	 	 	 	 	 $maxw = max($maxw, $w);
4583  	 	 	 	 }
4584  	 	 	 	 if ($maxw == 0) {
4585  	 	 	 	 	 $maxw = 600;
4586  	 	 	 	 }
4587  	 	 	 	 $maxw = $this->getAbsFontMeasure($maxw);
4588  	 	 	 }
4589  	 	 	 $fbbox = array(0, (0 - $this->FontDescent), $maxw, $this->FontAscent);
4590  	 	 }
4591  	 	 return $fbbox;
4592  	 }
4593  
4594  	 /**
4595  	  * Convert a relative font measure into absolute value.
4596  	  * @param $s (int) Font measure.
4597  	  * @return float Absolute measure.
4598  	  * @since 5.9.186 (2012-09-13)
4599  	  */
4600  	public function getAbsFontMeasure($s) {
4601  	 	 return ($s * $this->FontSize / 1000);
4602  	 }
4603  
4604  	 /**
4605  	  * Returns the glyph bounding box of the specified character in the current font in user units.
4606  	  * @param $char (int) Input character code.
4607  	  * @return mixed array(xMin, yMin, xMax, yMax) or FALSE if not defined.
4608  	  * @since 5.9.186 (2012-09-13)
4609  	  */
4610  	public function getCharBBox($char) {
4611  	 	 $c = intval($char);
4612  	 	 if (isset($this->CurrentFont['cw'][$c])) {
4613  	 	 	 // glyph is defined ... use zero width & height for glyphs without outlines
4614  	 	 	 $result = array(0,0,0,0);
4615  	 	 	 if (isset($this->CurrentFont['cbbox'][$c])) {
4616  	 	 	 	 $result = $this->CurrentFont['cbbox'][$c];
4617  	 	 	 }
4618  	 	 	 return array_map(array($this,'getAbsFontMeasure'), $result);
4619  	 	 }
4620  	 	 return false;
4621  	 }
4622  
4623  	 /**
4624  	  * Return the font descent value
4625  	  * @param $font (string) font name
4626  	  * @param $style (string) font style
4627  	  * @param $size (float) The size (in points)
4628  	  * @return int font descent
4629  	  * @public
4630  	  * @author Nicola Asuni
4631  	  * @since 4.9.003 (2010-03-30)
4632  	  */
4633  	public function getFontDescent($font, $style='', $size=0) {
4634  	 	 $fontdata = $this->AddFont($font, $style);
4635  	 	 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4636  	 	 if (isset($fontinfo['desc']['Descent']) AND ($fontinfo['desc']['Descent'] <= 0)) {
4637  	 	 	 $descent = (- $fontinfo['desc']['Descent'] * $size / 1000);
4638  	 	 } else {
4639  	 	 	 $descent = (1.219 * 0.24 * $size);
4640  	 	 }
4641  	 	 return ($descent / $this->k);
4642  	 }
4643  
4644  	 /**
4645  	  * Return the font ascent value.
4646  	  * @param $font (string) font name
4647  	  * @param $style (string) font style
4648  	  * @param $size (float) The size (in points)
4649  	  * @return int font ascent
4650  	  * @public
4651  	  * @author Nicola Asuni
4652  	  * @since 4.9.003 (2010-03-30)
4653  	  */
4654  	public function getFontAscent($font, $style='', $size=0) {
4655  	 	 $fontdata = $this->AddFont($font, $style);
4656  	 	 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4657  	 	 if (isset($fontinfo['desc']['Ascent']) AND ($fontinfo['desc']['Ascent'] > 0)) {
4658  	 	 	 $ascent = ($fontinfo['desc']['Ascent'] * $size / 1000);
4659  	 	 } else {
4660  	 	 	 $ascent = 1.219 * 0.76 * $size;
4661  	 	 }
4662  	 	 return ($ascent / $this->k);
4663  	 }
4664  
4665  	 /**
4666  	  * Return true in the character is present in the specified font.
4667  	  * @param $char (mixed) Character to check (integer value or string)
4668  	  * @param $font (string) Font name (family name).
4669  	  * @param $style (string) Font style.
4670  	  * @return (boolean) true if the char is defined, false otherwise.
4671  	  * @public
4672  	  * @since 5.9.153 (2012-03-28)
4673  	  */
4674  	public function isCharDefined($char, $font='', $style='') {
4675  	 	 if (is_string($char)) {
4676  	 	 	 // get character code
4677  	 	 	 $char = TCPDF_FONTS::UTF8StringToArray($char, $this->isunicode, $this->CurrentFont);
4678  	 	 	 $char = $char[0];
4679  	 	 }
4680  	 	 if (TCPDF_STATIC::empty_string($font)) {
4681  	 	 	 if (TCPDF_STATIC::empty_string($style)) {
4682  	 	 	 	 return (isset($this->CurrentFont['cw'][intval($char)]));
4683  	 	 	 }
4684  	 	 	 $font = $this->FontFamily;
4685  	 	 }
4686  	 	 $fontdata = $this->AddFont($font, $style);
4687  	 	 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4688  	 	 return (isset($fontinfo['cw'][intval($char)]));
4689  	 }
4690  
4691  	 /**
4692  	  * Replace missing font characters on selected font with specified substitutions.
4693  	  * @param $text (string) Text to process.
4694  	  * @param $font (string) Font name (family name).
4695  	  * @param $style (string) Font style.
4696  	  * @param $subs (array) Array of possible character substitutions. The key is the character to check (integer value) and the value is a single intege value or an array of possible substitutes.
4697  	  * @return (string) Processed text.
4698  	  * @public
4699  	  * @since 5.9.153 (2012-03-28)
4700  	  */
4701  	public function replaceMissingChars($text, $font='', $style='', $subs=array()) {
4702  	 	 if (empty($subs)) {
4703  	 	 	 return $text;
4704  	 	 }
4705  	 	 if (TCPDF_STATIC::empty_string($font)) {
4706  	 	 	 $font = $this->FontFamily;
4707  	 	 }
4708  	 	 $fontdata = $this->AddFont($font, $style);
4709  	 	 $fontinfo = $this->getFontBuffer($fontdata['fontkey']);
4710  	 	 $uniarr = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
4711  	 	 foreach ($uniarr as $k => $chr) {
4712  	 	 	 if (!isset($fontinfo['cw'][$chr])) {
4713  	 	 	 	 // this character is missing on the selected font
4714  	 	 	 	 if (isset($subs[$chr])) {
4715  	 	 	 	 	 // we have available substitutions
4716  	 	 	 	 	 if (is_array($subs[$chr])) {
4717  	 	 	 	 	 	 foreach($subs[$chr] as $s) {
4718  	 	 	 	 	 	 	 if (isset($fontinfo['cw'][$s])) {
4719  	 	 	 	 	 	 	 	 $uniarr[$k] = $s;
4720  	 	 	 	 	 	 	 	 break;
4721  	 	 	 	 	 	 	 }
4722  	 	 	 	 	 	 }
4723  	 	 	 	 	 } elseif (isset($fontinfo['cw'][$subs[$chr]])) {
4724  	 	 	 	 	 	 $uniarr[$k] = $subs[$chr];
4725  	 	 	 	 	 }
4726  	 	 	 	 }
4727  	 	 	 }
4728  	 	 }
4729  	 	 return TCPDF_FONTS::UniArrSubString(TCPDF_FONTS::UTF8ArrayToUniArray($uniarr, $this->isunicode));
4730  	 }
4731  
4732  	 /**
4733  	  * Defines the default monospaced font.
4734  	  * @param $font (string) Font name.
4735  	  * @public
4736  	  * @since 4.5.025
4737  	  */
4738  	public function SetDefaultMonospacedFont($font) {
4739  	 	 $this->default_monospaced_font = $font;
4740  	 }
4741  
4742  	 /**
4743  	  * 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 />
4744  	  * The identifier can then be passed to Cell(), Write(), Image() or Link(). The destination is defined with SetLink().
4745  	  * @public
4746  	  * @since 1.5
4747  	  * @see Cell(), Write(), Image(), Link(), SetLink()
4748  	  */
4749  	public function AddLink() {
4750  	 	 // create a new internal link
4751  	 	 $n = count($this->links) + 1;
4752  	 	 $this->links[$n] = array('p' => 0, 'y' => 0, 'f' => false);
4753  	 	 return $n;
4754  	 }
4755  
4756  	 /**
4757  	  * Defines the page and position a link points to.
4758  	  * @param $link (int) The link identifier returned by AddLink()
4759  	  * @param $y (float) Ordinate of target position; -1 indicates the current position. The default value is 0 (top of page)
4760  	  * @param $page (int|string) Number of target page; -1 indicates the current page (default value). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
4761  	  * @public
4762  	  * @since 1.5
4763  	  * @see AddLink()
4764  	  */
4765  	public function SetLink($link, $y=0, $page=-1) {
4766  	 	 $fixed = false;
4767  	 	 if (!empty($page) AND (substr($page, 0, 1) == '*')) {
4768  	 	 	 $page = intval(substr($page, 1));
4769  	 	 	 // this page number will not be changed when moving/add/deleting pages
4770  	 	 	 $fixed = true;
4771  	 	 }
4772  	 	 if ($page < 0) {
4773  	 	 	 $page = $this->page;
4774  	 	 }
4775  	 	 if ($y == -1) {
4776  	 	 	 $y = $this->y;
4777  	 	 }
4778  	 	 $this->links[$link] = array('p' => $page, 'y' => $y, 'f' => $fixed);
4779  	 }
4780  
4781  	 /**
4782  	  * Puts a link on a rectangular area of the page.
4783  	  * 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.
4784  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
4785  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
4786  	  * @param $w (float) Width of the rectangle
4787  	  * @param $h (float) Height of the rectangle
4788  	  * @param $link (mixed) URL or identifier returned by AddLink()
4789  	  * @param $spaces (int) number of spaces on the text to link
4790  	  * @public
4791  	  * @since 1.5
4792  	  * @see AddLink(), Annotation(), Cell(), Write(), Image()
4793  	  */
4794  	public function Link($x, $y, $w, $h, $link, $spaces=0) {
4795  	 	 $this->Annotation($x, $y, $w, $h, $link, array('Subtype'=>'Link'), $spaces);
4796  	 }
4797  
4798  	 /**
4799  	  * Puts a markup annotation on a rectangular area of the page.
4800  	  * !!!!THE ANNOTATION SUPPORT IS NOT YET FULLY IMPLEMENTED !!!!
4801  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
4802  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
4803  	  * @param $w (float) Width of the rectangle
4804  	  * @param $h (float) Height of the rectangle
4805  	  * @param $text (string) annotation text or alternate content
4806  	  * @param $opt (array) array of options (see section 8.4 of PDF reference 1.7).
4807  	  * @param $spaces (int) number of spaces on the text to link
4808  	  * @public
4809  	  * @since 4.0.018 (2008-08-06)
4810  	  */
4811  	public function Annotation($x, $y, $w, $h, $text, $opt=array('Subtype'=>'Text'), $spaces=0) {
4812  	 	 if ($this->inxobj) {
4813  	 	 	 // store parameters for later use on template
4814  	 	 	 $this->xobjects[$this->xobjid]['annotations'][] = array('x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'text' => $text, 'opt' => $opt, 'spaces' => $spaces);
4815  	 	 	 return;
4816  	 	 }
4817  	 	 if ($x === '') {
4818  	 	 	 $x = $this->x;
4819  	 	 }
4820  	 	 if ($y === '') {
4821  	 	 	 $y = $this->y;
4822  	 	 }
4823  	 	 // check page for no-write regions and adapt page margins if necessary
4824  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
4825  	 	 // recalculate coordinates to account for graphic transformations
4826  	 	 if (isset($this->transfmatrix) AND !empty($this->transfmatrix)) {
4827  	 	 	 for ($i=$this->transfmatrix_key; $i > 0; --$i) {
4828  	 	 	 	 $maxid = count($this->transfmatrix[$i]) - 1;
4829  	 	 	 	 for ($j=$maxid; $j >= 0; --$j) {
4830  	 	 	 	 	 $ctm = $this->transfmatrix[$i][$j];
4831  	 	 	 	 	 if (isset($ctm['a'])) {
4832  	 	 	 	 	 	 $x = $x * $this->k;
4833  	 	 	 	 	 	 $y = ($this->h - $y) * $this->k;
4834  	 	 	 	 	 	 $w = $w * $this->k;
4835  	 	 	 	 	 	 $h = $h * $this->k;
4836  	 	 	 	 	 	 // top left
4837  	 	 	 	 	 	 $xt = $x;
4838  	 	 	 	 	 	 $yt = $y;
4839  	 	 	 	 	 	 $x1 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4840  	 	 	 	 	 	 $y1 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4841  	 	 	 	 	 	 // top right
4842  	 	 	 	 	 	 $xt = $x + $w;
4843  	 	 	 	 	 	 $yt = $y;
4844  	 	 	 	 	 	 $x2 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4845  	 	 	 	 	 	 $y2 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4846  	 	 	 	 	 	 // bottom left
4847  	 	 	 	 	 	 $xt = $x;
4848  	 	 	 	 	 	 $yt = $y - $h;
4849  	 	 	 	 	 	 $x3 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4850  	 	 	 	 	 	 $y3 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4851  	 	 	 	 	 	 // bottom right
4852  	 	 	 	 	 	 $xt = $x + $w;
4853  	 	 	 	 	 	 $yt = $y - $h;
4854  	 	 	 	 	 	 $x4 = ($ctm['a'] * $xt) + ($ctm['c'] * $yt) + $ctm['e'];
4855  	 	 	 	 	 	 $y4 = ($ctm['b'] * $xt) + ($ctm['d'] * $yt) + $ctm['f'];
4856  	 	 	 	 	 	 // new coordinates (rectangle area)
4857  	 	 	 	 	 	 $x = min($x1, $x2, $x3, $x4);
4858  	 	 	 	 	 	 $y = max($y1, $y2, $y3, $y4);
4859  	 	 	 	 	 	 $w = (max($x1, $x2, $x3, $x4) - $x) / $this->k;
4860  	 	 	 	 	 	 $h = ($y - min($y1, $y2, $y3, $y4)) / $this->k;
4861  	 	 	 	 	 	 $x = $x / $this->k;
4862  	 	 	 	 	 	 $y = $this->h - ($y / $this->k);
4863  	 	 	 	 	 }
4864  	 	 	 	 }
4865  	 	 	 }
4866  	 	 }
4867  	 	 if ($this->page <= 0) {
4868  	 	 	 $page = 1;
4869  	 	 } else {
4870  	 	 	 $page = $this->page;
4871  	 	 }
4872  	 	 if (!isset($this->PageAnnots[$page])) {
4873  	 	 	 $this->PageAnnots[$page] = array();
4874  	 	 }
4875  	 	 $this->PageAnnots[$page][] = array('n' => ++$this->n, 'x' => $x, 'y' => $y, 'w' => $w, 'h' => $h, 'txt' => $text, 'opt' => $opt, 'numspaces' => $spaces);
4876  	 	 if (!$this->pdfa_mode || ($this->pdfa_mode && $this->pdfa_version == 3)) {
4877  	 	 	 if ((($opt['Subtype'] == 'FileAttachment') OR ($opt['Subtype'] == 'Sound')) AND (!TCPDF_STATIC::empty_string($opt['FS']))
4878  	 	 	 	 AND (@TCPDF_STATIC::file_exists($opt['FS']) OR TCPDF_STATIC::isValidURL($opt['FS']))
4879  	 	 	 	 AND (!isset($this->embeddedfiles[basename($opt['FS'])]))) {
4880  	 	 	 	 $this->embeddedfiles[basename($opt['FS'])] = array('f' => ++$this->n, 'n' => ++$this->n, 'file' => $opt['FS']);
4881  	 	 	 }
4882  	 	 }
4883  	 	 // Add widgets annotation's icons
4884  	 	 if (isset($opt['mk']['i']) AND @TCPDF_STATIC::file_exists($opt['mk']['i'])) {
4885  	 	 	 $this->Image($opt['mk']['i'], '', '', 10, 10, '', '', '', false, 300, '', false, false, 0, false, true);
4886  	 	 }
4887  	 	 if (isset($opt['mk']['ri']) AND @TCPDF_STATIC::file_exists($opt['mk']['ri'])) {
4888  	 	 	 $this->Image($opt['mk']['ri'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4889  	 	 }
4890  	 	 if (isset($opt['mk']['ix']) AND @TCPDF_STATIC::file_exists($opt['mk']['ix'])) {
4891  	 	 	 $this->Image($opt['mk']['ix'], '', '', 0, 0, '', '', '', false, 300, '', false, false, 0, false, true);
4892  	 	 }
4893  	 }
4894  
4895  	 /**
4896  	  * Embedd the attached files.
4897  	  * @since 4.4.000 (2008-12-07)
4898  	  * @protected
4899  	  * @see Annotation()
4900  	  */
4901  	protected function _putEmbeddedFiles() {
4902  	 	 if ($this->pdfa_mode && $this->pdfa_version != 3)  {
4903  	 	 	 // embedded files are not allowed in PDF/A mode version 1 and 2
4904  	 	 	 return;
4905  	 	 }
4906  	 	 reset($this->embeddedfiles);
4907  	 	 foreach ($this->embeddedfiles as $filename => $filedata) {
4908  	 	     $data = $this->getCachedFileContents($filedata['file']);
4909  	 	 	 if ($data !== FALSE) {
4910  	 	 	 	 $rawsize = strlen($data);
4911  	 	 	 	 if ($rawsize > 0) {
4912  	 	 	 	 	 // update name tree
4913  	 	 	 	 	 $this->efnames[$filename] = $filedata['f'].' 0 R';
4914  	 	 	 	 	 // embedded file specification object
4915  	 	 	 	 	 $out = $this->_getobj($filedata['f'])."\n";
4916  	 	 	 	 	 $out .= '<</Type /Filespec /F '.$this->_datastring($filename, $filedata['f']);
4917  	 	 	 	 	 $out .= ' /UF '.$this->_datastring($filename, $filedata['f']);
4918  	 	 	 	 	 $out .= ' /AFRelationship /Source';
4919  	 	 	 	 	 $out .= ' /EF <</F '.$filedata['n'].' 0 R>> >>';
4920  	 	 	 	 	 $out .= "\n".'endobj';
4921  	 	 	 	 	 $this->_out($out);
4922  	 	 	 	 	 // embedded file object
4923  	 	 	 	 	 $filter = '';
4924  	 	 	 	 	 if ($this->compress) {
4925  	 	 	 	 	 	 $data = gzcompress($data);
4926  	 	 	 	 	 	 $filter = ' /Filter /FlateDecode';
4927  	 	 	 	 	 }
4928  
4929  	 	 	 	 	 if ($this->pdfa_version == 3) {
4930  	 	 	 	 	 	 $filter = ' /Subtype /text#2Fxml';
4931  	 	 	 	 	 }
4932  
4933  	 	 	 	 	 $stream = $this->_getrawstream($data, $filedata['n']);
4934  	 	 	 	 	 $out = $this->_getobj($filedata['n'])."\n";
4935  	 	 	 	 	 $out .= '<< /Type /EmbeddedFile'.$filter.' /Length '.strlen($stream).' /Params <</Size '.$rawsize.'>> >>';
4936  	 	 	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
4937  	 	 	 	 	 $out .= "\n".'endobj';
4938  	 	 	 	 	 $this->_out($out);
4939  	 	 	 	 }
4940  	 	 	 }
4941  	 	 }
4942  	 }
4943  
4944  	 /**
4945  	  * Prints a text cell at the specified position.
4946  	  * This method allows to place a string precisely on the page.
4947  	  * @param $x (float) Abscissa of the cell origin
4948  	  * @param $y (float) Ordinate of the cell origin
4949  	  * @param $txt (string) String to print
4950  	  * @param $fstroke (int) outline size in user units (false = disable)
4951  	  * @param $fclip (boolean) if true activate clipping mode (you must call StartTransform() before this function and StopTransform() to stop the clipping tranformation).
4952  	  * @param $ffill (boolean) if true fills the text
4953  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
4954  	  * @param $ln (int) 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.
4955  	  * @param $align (string) 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>
4956  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
4957  	  * @param $link (mixed) URL or identifier returned by AddLink().
4958  	  * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
4959  	  * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
4960  	  * @param $calign (string) 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>
4961  	  * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
4962  	  * @param $rtloff (boolean) if true uses the page top-left corner as origin of axis for $x and $y initial position.
4963  	  * @public
4964  	  * @since 1.0
4965  	  * @see Cell(), Write(), MultiCell(), WriteHTML(), WriteHTMLCell()
4966  	  */
4967  	public function Text($x, $y, $txt, $fstroke=false, $fclip=false, $ffill=true, $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M', $rtloff=false) {
4968  	 	 $textrendermode = $this->textrendermode;
4969  	 	 $textstrokewidth = $this->textstrokewidth;
4970  	 	 $this->setTextRenderingMode($fstroke, $ffill, $fclip);
4971  	 	 $this->SetXY($x, $y, $rtloff);
4972  	 	 $this->Cell(0, 0, $txt, $border, $ln, $align, $fill, $link, $stretch, $ignore_min_height, $calign, $valign);
4973  	 	 // restore previous rendering mode
4974  	 	 $this->textrendermode = $textrendermode;
4975  	 	 $this->textstrokewidth = $textstrokewidth;
4976  	 }
4977  
4978  	 /**
4979  	  * Whenever a page break condition is met, the method is called, and the break is issued or not depending on the returned value.
4980  	  * The default implementation returns a value according to the mode selected by SetAutoPageBreak().<br />
4981  	  * This method is called automatically and should not be called directly by the application.
4982  	  * @return boolean
4983  	  * @public
4984  	  * @since 1.4
4985  	  * @see SetAutoPageBreak()
4986  	  */
4987  	public function AcceptPageBreak() {
4988  	 	 if ($this->num_columns > 1) {
4989  	 	 	 // multi column mode
4990  	 	 	 if ($this->current_column < ($this->num_columns - 1)) {
4991  	 	 	 	 // go to next column
4992  	 	 	 	 $this->selectColumn($this->current_column + 1);
4993  	 	 	 } elseif ($this->AutoPageBreak) {
4994  	 	 	 	 // add a new page
4995  	 	 	 	 $this->AddPage();
4996  	 	 	 	 // set first column
4997  	 	 	 	 $this->selectColumn(0);
4998  	 	 	 }
4999  	 	 	 // avoid page breaking from checkPageBreak()
5000  	 	 	 return false;
5001  	 	 }
5002  	 	 return $this->AutoPageBreak;
5003  	 }
5004  
5005  	 /**
5006  	  * Add page if needed.
5007  	  * @param $h (float) Cell height. Default value: 0.
5008  	  * @param $y (mixed) starting y position, leave empty for current position.
5009  	  * @param $addpage (boolean) if true add a page, otherwise only return the true/false state
5010  	  * @return boolean true in case of page break, false otherwise.
5011  	  * @since 3.2.000 (2008-07-01)
5012  	  * @protected
5013  	  */
5014  	protected function checkPageBreak($h=0, $y='', $addpage=true) {
5015  	 	 if (TCPDF_STATIC::empty_string($y)) {
5016  	 	 	 $y = $this->y;
5017  	 	 }
5018  	 	 $current_page = $this->page;
5019  	 	 if ((($y + $h) > $this->PageBreakTrigger) AND ($this->inPageBody()) AND ($this->AcceptPageBreak())) {
5020  	 	 	 if ($addpage) {
5021  	 	 	 	 //Automatic page break
5022  	 	 	 	 $x = $this->x;
5023  	 	 	 	 $this->AddPage($this->CurOrientation);
5024  	 	 	 	 $this->y = $this->tMargin;
5025  	 	 	 	 $oldpage = $this->page - 1;
5026  	 	 	 	 if ($this->rtl) {
5027  	 	 	 	 	 if ($this->pagedim[$this->page]['orm'] != $this->pagedim[$oldpage]['orm']) {
5028  	 	 	 	 	 	 $this->x = $x - ($this->pagedim[$this->page]['orm'] - $this->pagedim[$oldpage]['orm']);
5029  	 	 	 	 	 } else {
5030  	 	 	 	 	 	 $this->x = $x;
5031  	 	 	 	 	 }
5032  	 	 	 	 } else {
5033  	 	 	 	 	 if ($this->pagedim[$this->page]['olm'] != $this->pagedim[$oldpage]['olm']) {
5034  	 	 	 	 	 	 $this->x = $x + ($this->pagedim[$this->page]['olm'] - $this->pagedim[$oldpage]['olm']);
5035  	 	 	 	 	 } else {
5036  	 	 	 	 	 	 $this->x = $x;
5037  	 	 	 	 	 }
5038  	 	 	 	 }
5039  	 	 	 }
5040  	 	 	 return true;
5041  	 	 }
5042  	 	 if ($current_page != $this->page) {
5043  	 	 	 // account for columns mode
5044  	 	 	 return true;
5045  	 	 }
5046  	 	 return false;
5047  	 }
5048  
5049  	 /**
5050  	  * 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 />
5051  	  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5052  	  * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5053  	  * @param $h (float) Cell height. Default value: 0.
5054  	  * @param $txt (string) String to print. Default value: empty string.
5055  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5056  	  * @param $ln (int) 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.
5057  	  * @param $align (string) 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>
5058  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5059  	  * @param $link (mixed) URL or identifier returned by AddLink().
5060  	  * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5061  	  * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5062  	  * @param $calign (string) 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>
5063  	  * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>C : center</li><li>B : bottom</li></ul>
5064  	  * @public
5065  	  * @since 1.0
5066  	  * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), AddLink(), Ln(), MultiCell(), Write(), SetAutoPageBreak()
5067  	  */
5068  	public function Cell($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5069  	 	 $prev_cell_margin = $this->cell_margin;
5070  	 	 $prev_cell_padding = $this->cell_padding;
5071  	 	 $this->adjustCellPadding($border);
5072  	 	 if (!$ignore_min_height) {
5073  	 	 	 $min_cell_height = $this->getCellHeight($this->FontSize);
5074  	 	 	 if ($h < $min_cell_height) {
5075  	 	 	 	 $h = $min_cell_height;
5076  	 	 	 }
5077  	 	 }
5078  	 	 $this->checkPageBreak($h + $this->cell_margin['T'] + $this->cell_margin['B']);
5079  	 	 // apply text shadow if enabled
5080  	 	 if ($this->txtshadow['enabled']) {
5081  	 	 	 // save data
5082  	 	 	 $x = $this->x;
5083  	 	 	 $y = $this->y;
5084  	 	 	 $bc = $this->bgcolor;
5085  	 	 	 $fc = $this->fgcolor;
5086  	 	 	 $sc = $this->strokecolor;
5087  	 	 	 $alpha = $this->alpha;
5088  	 	 	 // print shadow
5089  	 	 	 $this->x += $this->txtshadow['depth_w'];
5090  	 	 	 $this->y += $this->txtshadow['depth_h'];
5091  	 	 	 $this->SetFillColorArray($this->txtshadow['color']);
5092  	 	 	 $this->SetTextColorArray($this->txtshadow['color']);
5093  	 	 	 $this->SetDrawColorArray($this->txtshadow['color']);
5094  	 	 	 if ($this->txtshadow['opacity'] != $alpha['CA']) {
5095  	 	 	 	 $this->setAlpha($this->txtshadow['opacity'], $this->txtshadow['blend_mode']);
5096  	 	 	 }
5097  	 	 	 if ($this->state == 2) {
5098  	 	 	 	 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5099  	 	 	 }
5100  	 	 	 //restore data
5101  	 	 	 $this->x = $x;
5102  	 	 	 $this->y = $y;
5103  	 	 	 $this->SetFillColorArray($bc);
5104  	 	 	 $this->SetTextColorArray($fc);
5105  	 	 	 $this->SetDrawColorArray($sc);
5106  	 	 	 if ($this->txtshadow['opacity'] != $alpha['CA']) {
5107  	 	 	 	 $this->setAlpha($alpha['CA'], $alpha['BM'], $alpha['ca'], $alpha['AIS']);
5108  	 	 	 }
5109  	 	 }
5110  	 	 if ($this->state == 2) {
5111  	 	 	 $this->_out($this->getCellCode($w, $h, $txt, $border, $ln, $align, $fill, $link, $stretch, true, $calign, $valign));
5112  	 	 }
5113  	 	 $this->cell_padding = $prev_cell_padding;
5114  	 	 $this->cell_margin = $prev_cell_margin;
5115  	 }
5116  
5117  	 /**
5118  	  * 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 />
5119  	  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
5120  	  * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
5121  	  * @param $h (float) Cell height. Default value: 0.
5122  	  * @param $txt (string) String to print. Default value: empty string.
5123  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5124  	  * @param $ln (int) 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.
5125  	  * @param $align (string) 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>
5126  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5127  	  * @param $link (mixed) URL or identifier returned by AddLink().
5128  	  * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5129  	  * @param $ignore_min_height (boolean) if true ignore automatic minimum height value.
5130  	  * @param $calign (string) 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>
5131  	  * @param $valign (string) text vertical alignment inside the cell. Possible values are:<ul><li>T : top</li><li>M : middle</li><li>B : bottom</li></ul>
5132  	  * @return string containing cell code
5133  	  * @protected
5134  	  * @since 1.0
5135  	  * @see Cell()
5136  	  */
5137  	protected function getCellCode($w, $h=0, $txt='', $border=0, $ln=0, $align='', $fill=false, $link='', $stretch=0, $ignore_min_height=false, $calign='T', $valign='M') {
5138  	 	 // replace 'NO-BREAK SPACE' (U+00A0) character with a simple space
5139  	 	 $txt = str_replace(TCPDF_FONTS::unichr(160, $this->isunicode), ' ', $txt);
5140  	 	 $prev_cell_margin = $this->cell_margin;
5141  	 	 $prev_cell_padding = $this->cell_padding;
5142  	 	 $txt = TCPDF_STATIC::removeSHY($txt, $this->isunicode);
5143  	 	 $rs = ''; //string to be returned
5144  	 	 $this->adjustCellPadding($border);
5145  	 	 if (!$ignore_min_height) {
5146  	 	 	 $min_cell_height = $this->getCellHeight($this->FontSize);
5147  	 	 	 if ($h < $min_cell_height) {
5148  	 	 	 	 $h = $min_cell_height;
5149  	 	 	 }
5150  	 	 }
5151  	 	 $k = $this->k;
5152  	 	 // check page for no-write regions and adapt page margins if necessary
5153  	 	 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
5154  	 	 if ($this->rtl) {
5155  	 	 	 $x = $this->x - $this->cell_margin['R'];
5156  	 	 } else {
5157  	 	 	 $x = $this->x + $this->cell_margin['L'];
5158  	 	 }
5159  	 	 $y = $this->y + $this->cell_margin['T'];
5160  	 	 $prev_font_stretching = $this->font_stretching;
5161  	 	 $prev_font_spacing = $this->font_spacing;
5162  	 	 // cell vertical alignment
5163  	 	 switch ($calign) {
5164  	 	 	 case 'A': {
5165  	 	 	 	 // font top
5166  	 	 	 	 switch ($valign) {
5167  	 	 	 	 	 case 'T': {
5168  	 	 	 	 	 	 // top
5169  	 	 	 	 	 	 $y -= $this->cell_padding['T'];
5170  	 	 	 	 	 	 break;
5171  	 	 	 	 	 }
5172  	 	 	 	 	 case 'B': {
5173  	 	 	 	 	 	 // bottom
5174  	 	 	 	 	 	 $y -= ($h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent);
5175  	 	 	 	 	 	 break;
5176  	 	 	 	 	 }
5177  	 	 	 	 	 default:
5178  	 	 	 	 	 case 'C':
5179  	 	 	 	 	 case 'M': {
5180  	 	 	 	 	 	 // center
5181  	 	 	 	 	 	 $y -= (($h - $this->FontAscent - $this->FontDescent) / 2);
5182  	 	 	 	 	 	 break;
5183  	 	 	 	 	 }
5184  	 	 	 	 }
5185  	 	 	 	 break;
5186  	 	 	 }
5187  	 	 	 case 'L': {
5188  	 	 	 	 // font baseline
5189  	 	 	 	 switch ($valign) {
5190  	 	 	 	 	 case 'T': {
5191  	 	 	 	 	 	 // top
5192  	 	 	 	 	 	 $y -= ($this->cell_padding['T'] + $this->FontAscent);
5193  	 	 	 	 	 	 break;
5194  	 	 	 	 	 }
5195  	 	 	 	 	 case 'B': {
5196  	 	 	 	 	 	 // bottom
5197  	 	 	 	 	 	 $y -= ($h - $this->cell_padding['B'] - $this->FontDescent);
5198  	 	 	 	 	 	 break;
5199  	 	 	 	 	 }
5200  	 	 	 	 	 default:
5201  	 	 	 	 	 case 'C':
5202  	 	 	 	 	 case 'M': {
5203  	 	 	 	 	 	 // center
5204  	 	 	 	 	 	 $y -= (($h + $this->FontAscent - $this->FontDescent) / 2);
5205  	 	 	 	 	 	 break;
5206  	 	 	 	 	 }
5207  	 	 	 	 }
5208  	 	 	 	 break;
5209  	 	 	 }
5210  	 	 	 case 'D': {
5211  	 	 	 	 // font bottom
5212  	 	 	 	 switch ($valign) {
5213  	 	 	 	 	 case 'T': {
5214  	 	 	 	 	 	 // top
5215  	 	 	 	 	 	 $y -= ($this->cell_padding['T'] + $this->FontAscent + $this->FontDescent);
5216  	 	 	 	 	 	 break;
5217  	 	 	 	 	 }
5218  	 	 	 	 	 case 'B': {
5219  	 	 	 	 	 	 // bottom
5220  	 	 	 	 	 	 $y -= ($h - $this->cell_padding['B']);
5221  	 	 	 	 	 	 break;
5222  	 	 	 	 	 }
5223  	 	 	 	 	 default:
5224  	 	 	 	 	 case 'C':
5225  	 	 	 	 	 case 'M': {
5226  	 	 	 	 	 	 // center
5227  	 	 	 	 	 	 $y -= (($h + $this->FontAscent + $this->FontDescent) / 2);
5228  	 	 	 	 	 	 break;
5229  	 	 	 	 	 }
5230  	 	 	 	 }
5231  	 	 	 	 break;
5232  	 	 	 }
5233  	 	 	 case 'B': {
5234  	 	 	 	 // cell bottom
5235  	 	 	 	 $y -= $h;
5236  	 	 	 	 break;
5237  	 	 	 }
5238  	 	 	 case 'C':
5239  	 	 	 case 'M': {
5240  	 	 	 	 // cell center
5241  	 	 	 	 $y -= ($h / 2);
5242  	 	 	 	 break;
5243  	 	 	 }
5244  	 	 	 default:
5245  	 	 	 case 'T': {
5246  	 	 	 	 // cell top
5247  	 	 	 	 break;
5248  	 	 	 }
5249  	 	 }
5250  	 	 // text vertical alignment
5251  	 	 switch ($valign) {
5252  	 	 	 case 'T': {
5253  	 	 	 	 // top
5254  	 	 	 	 $yt = $y + $this->cell_padding['T'];
5255  	 	 	 	 break;
5256  	 	 	 }
5257  	 	 	 case 'B': {
5258  	 	 	 	 // bottom
5259  	 	 	 	 $yt = $y + $h - $this->cell_padding['B'] - $this->FontAscent - $this->FontDescent;
5260  	 	 	 	 break;
5261  	 	 	 }
5262  	 	 	 default:
5263  	 	 	 case 'C':
5264  	 	 	 case 'M': {
5265  	 	 	 	 // center
5266  	 	 	 	 $yt = $y + (($h - $this->FontAscent - $this->FontDescent) / 2);
5267  	 	 	 	 break;
5268  	 	 	 }
5269  	 	 }
5270  	 	 $basefonty = $yt + $this->FontAscent;
5271  	 	 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5272  	 	 	 if ($this->rtl) {
5273  	 	 	 	 $w = $x - $this->lMargin;
5274  	 	 	 } else {
5275  	 	 	 	 $w = $this->w - $this->rMargin - $x;
5276  	 	 	 }
5277  	 	 }
5278  	 	 $s = '';
5279  	 	 // fill and borders
5280  	 	 if (is_string($border) AND (strlen($border) == 4)) {
5281  	 	 	 // full border
5282  	 	 	 $border = 1;
5283  	 	 }
5284  	 	 if ($fill OR ($border == 1)) {
5285  	 	 	 if ($fill) {
5286  	 	 	 	 $op = ($border == 1) ? 'B' : 'f';
5287  	 	 	 } else {
5288  	 	 	 	 $op = 'S';
5289  	 	 	 }
5290  	 	 	 if ($this->rtl) {
5291  	 	 	 	 $xk = (($x - $w) * $k);
5292  	 	 	 } else {
5293  	 	 	 	 $xk = ($x * $k);
5294  	 	 	 }
5295  	 	 	 $s .= sprintf('%F %F %F %F re %s ', $xk, (($this->h - $y) * $k), ($w * $k), (-$h * $k), $op);
5296  	 	 }
5297  	 	 // draw borders
5298  	 	 $s .= $this->getCellBorder($x, $y, $w, $h, $border);
5299  	 	 if ($txt != '') {
5300  	 	 	 $txt2 = $txt;
5301  	 	 	 if ($this->isunicode) {
5302  	 	 	 	 if (($this->CurrentFont['type'] == 'core') OR ($this->CurrentFont['type'] == 'TrueType') OR ($this->CurrentFont['type'] == 'Type1')) {
5303  	 	 	 	 	 $txt2 = TCPDF_FONTS::UTF8ToLatin1($txt2, $this->isunicode, $this->CurrentFont);
5304  	 	 	 	 } else {
5305  	 	 	 	 	 $unicode = TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont); // array of UTF-8 unicode values
5306  	 	 	 	 	 $unicode = TCPDF_FONTS::utf8Bidi($unicode, '', $this->tmprtl, $this->isunicode, $this->CurrentFont);
5307  	 	 	 	 	 // replace thai chars (if any)
5308  	 	 	 	 	 if (defined('K_THAI_TOPCHARS') AND (K_THAI_TOPCHARS == true)) {
5309  	 	 	 	 	 	 // number of chars
5310  	 	 	 	 	 	 $numchars = count($unicode);
5311  	 	 	 	 	 	 // po pla, for far, for fan
5312  	 	 	 	 	 	 $longtail = array(0x0e1b, 0x0e1d, 0x0e1f);
5313  	 	 	 	 	 	 // do chada, to patak
5314  	 	 	 	 	 	 $lowtail = array(0x0e0e, 0x0e0f);
5315  	 	 	 	 	 	 // mai hun arkad, sara i, sara ii, sara ue, sara uee
5316  	 	 	 	 	 	 $upvowel = array(0x0e31, 0x0e34, 0x0e35, 0x0e36, 0x0e37);
5317  	 	 	 	 	 	 // mai ek, mai tho, mai tri, mai chattawa, karan
5318  	 	 	 	 	 	 $tonemark = array(0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c);
5319  	 	 	 	 	 	 // sara u, sara uu, pinthu
5320  	 	 	 	 	 	 $lowvowel = array(0x0e38, 0x0e39, 0x0e3a);
5321  	 	 	 	 	 	 $output = array();
5322  	 	 	 	 	 	 for ($i = 0; $i < $numchars; $i++) {
5323  	 	 	 	 	 	 	 if (($unicode[$i] >= 0x0e00) && ($unicode[$i] <= 0x0e5b)) {
5324  	 	 	 	 	 	 	 	 $ch0 = $unicode[$i];
5325  	 	 	 	 	 	 	 	 $ch1 = ($i > 0) ? $unicode[($i - 1)] : 0;
5326  	 	 	 	 	 	 	 	 $ch2 = ($i > 1) ? $unicode[($i - 2)] : 0;
5327  	 	 	 	 	 	 	 	 $chn = ($i < ($numchars - 1)) ? $unicode[($i + 1)] : 0;
5328  	 	 	 	 	 	 	 	 if (in_array($ch0, $tonemark)) {
5329  	 	 	 	 	 	 	 	 	 if ($chn == 0x0e33) {
5330  	 	 	 	 	 	 	 	 	 	 // sara um
5331  	 	 	 	 	 	 	 	 	 	 if (in_array($ch1, $longtail)) {
5332  	 	 	 	 	 	 	 	 	 	 	 // tonemark at upper left
5333  	 	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5334  	 	 	 	 	 	 	 	 	 	 } else {
5335  	 	 	 	 	 	 	 	 	 	 	 // tonemark at upper right (normal position)
5336  	 	 	 	 	 	 	 	 	 	 	 $output[] = $ch0;
5337  	 	 	 	 	 	 	 	 	 	 }
5338  	 	 	 	 	 	 	 	 	 } elseif (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $lowvowel))) {
5339  	 	 	 	 	 	 	 	 	 	 // tonemark at lower left
5340  	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, (0xf705 + $ch0 - 0x0e48));
5341  	 	 	 	 	 	 	 	 	 } elseif (in_array($ch1, $upvowel)) {
5342  	 	 	 	 	 	 	 	 	 	 if (in_array($ch2, $longtail)) {
5343  	 	 	 	 	 	 	 	 	 	 	 // tonemark at upper left
5344  	 	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, (0xf713 + $ch0 - 0x0e48));
5345  	 	 	 	 	 	 	 	 	 	 } else {
5346  	 	 	 	 	 	 	 	 	 	 	 // tonemark at upper right (normal position)
5347  	 	 	 	 	 	 	 	 	 	 	 $output[] = $ch0;
5348  	 	 	 	 	 	 	 	 	 	 }
5349  	 	 	 	 	 	 	 	 	 } else {
5350  	 	 	 	 	 	 	 	 	 	 // tonemark at lower right
5351  	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, (0xf70a + $ch0 - 0x0e48));
5352  	 	 	 	 	 	 	 	 	 }
5353  	 	 	 	 	 	 	 	 } elseif (($ch0 == 0x0e33) AND (in_array($ch1, $longtail) OR (in_array($ch2, $longtail) AND in_array($ch1, $tonemark)))) {
5354  	 	 	 	 	 	 	 	 	 // add lower left nikhahit and sara aa
5355  	 	 	 	 	 	 	 	 	 if ($this->isCharDefined(0xf711) AND $this->isCharDefined(0x0e32)) {
5356  	 	 	 	 	 	 	 	 	 	 $output[] = 0xf711;
5357  	 	 	 	 	 	 	 	 	 	 $this->CurrentFont['subsetchars'][0xf711] = true;
5358  	 	 	 	 	 	 	 	 	 	 $output[] = 0x0e32;
5359  	 	 	 	 	 	 	 	 	 	 $this->CurrentFont['subsetchars'][0x0e32] = true;
5360  	 	 	 	 	 	 	 	 	 } else {
5361  	 	 	 	 	 	 	 	 	 	 $output[] = $ch0;
5362  	 	 	 	 	 	 	 	 	 }
5363  	 	 	 	 	 	 	 	 } elseif (in_array($ch1, $longtail)) {
5364  	 	 	 	 	 	 	 	 	 if ($ch0 == 0x0e31) {
5365  	 	 	 	 	 	 	 	 	 	 // lower left mai hun arkad
5366  	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, 0xf710);
5367  	 	 	 	 	 	 	 	 	 } elseif (in_array($ch0, $upvowel)) {
5368  	 	 	 	 	 	 	 	 	 	 // lower left
5369  	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, (0xf701 + $ch0 - 0x0e34));
5370  	 	 	 	 	 	 	 	 	 } elseif ($ch0 == 0x0e47) {
5371  	 	 	 	 	 	 	 	 	 	 // lower left mai tai koo
5372  	 	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, 0xf712);
5373  	 	 	 	 	 	 	 	 	 } else {
5374  	 	 	 	 	 	 	 	 	 	 // normal character
5375  	 	 	 	 	 	 	 	 	 	 $output[] = $ch0;
5376  	 	 	 	 	 	 	 	 	 }
5377  	 	 	 	 	 	 	 	 } elseif (in_array($ch1, $lowtail) AND in_array($ch0, $lowvowel)) {
5378  	 	 	 	 	 	 	 	 	 // lower vowel
5379  	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, (0xf718 + $ch0 - 0x0e38));
5380  	 	 	 	 	 	 	 	 } elseif (($ch0 == 0x0e0d) AND in_array($chn, $lowvowel)) {
5381  	 	 	 	 	 	 	 	 	 // yo ying without lower part
5382  	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, 0xf70f);
5383  	 	 	 	 	 	 	 	 } elseif (($ch0 == 0x0e10) AND in_array($chn, $lowvowel)) {
5384  	 	 	 	 	 	 	 	 	 // tho santan without lower part
5385  	 	 	 	 	 	 	 	 	 $output[] = $this->replaceChar($ch0, 0xf700);
5386  	 	 	 	 	 	 	 	 } else {
5387  	 	 	 	 	 	 	 	 	 $output[] = $ch0;
5388  	 	 	 	 	 	 	 	 }
5389  	 	 	 	 	 	 	 } else {
5390  	 	 	 	 	 	 	 	 // non-thai character
5391  	 	 	 	 	 	 	 	 $output[] = $unicode[$i];
5392  	 	 	 	 	 	 	 }
5393  	 	 	 	 	 	 }
5394  	 	 	 	 	 	 $unicode = $output;
5395  	 	 	 	 	 	 // update font subsetchars
5396  	 	 	 	 	 	 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
5397  	 	 	 	 	 } // end of K_THAI_TOPCHARS
5398  	 	 	 	 	 $txt2 = TCPDF_FONTS::arrUTF8ToUTF16BE($unicode, false);
5399  	 	 	 	 }
5400  	 	 	 }
5401  	 	 	 $txt2 = TCPDF_STATIC::_escape($txt2);
5402  	 	 	 // get current text width (considering general font stretching and spacing)
5403  	 	 	 $txwidth = $this->GetStringWidth($txt);
5404  	 	 	 $width = $txwidth;
5405  	 	 	 // check for stretch mode
5406  	 	 	 if ($stretch > 0) {
5407  	 	 	 	 // calculate ratio between cell width and text width
5408  	 	 	 	 if ($width <= 0) {
5409  	 	 	 	 	 $ratio = 1;
5410  	 	 	 	 } else {
5411  	 	 	 	 	 $ratio = (($w - $this->cell_padding['L'] - $this->cell_padding['R']) / $width);
5412  	 	 	 	 }
5413  	 	 	 	 // check if stretching is required
5414  	 	 	 	 if (($ratio < 1) OR (($ratio > 1) AND (($stretch % 2) == 0))) {
5415  	 	 	 	 	 // the text will be stretched to fit cell width
5416  	 	 	 	 	 if ($stretch > 2) {
5417  	 	 	 	 	 	 // set new character spacing
5418  	 	 	 	 	 	 $this->font_spacing += ($w - $this->cell_padding['L'] - $this->cell_padding['R'] - $width) / (max(($this->GetNumChars($txt) - 1), 1) * ($this->font_stretching / 100));
5419  	 	 	 	 	 } else {
5420  	 	 	 	 	 	 // set new horizontal stretching
5421  	 	 	 	 	 	 $this->font_stretching *= $ratio;
5422  	 	 	 	 	 }
5423  	 	 	 	 	 // recalculate text width (the text fills the entire cell)
5424  	 	 	 	 	 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5425  	 	 	 	 	 // reset alignment
5426  	 	 	 	 	 $align = '';
5427  	 	 	 	 }
5428  	 	 	 }
5429  	 	 	 if ($this->font_stretching != 100) {
5430  	 	 	 	 // apply font stretching
5431  	 	 	 	 $rs .= sprintf('BT %F Tz ET ', $this->font_stretching);
5432  	 	 	 }
5433  	 	 	 if ($this->font_spacing != 0) {
5434  	 	 	 	 // increase/decrease font spacing
5435  	 	 	 	 $rs .= sprintf('BT %F Tc ET ', ($this->font_spacing * $this->k));
5436  	 	 	 }
5437  	 	 	 if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5438  	 	 	 	 $s .= 'q '.$this->TextColor.' ';
5439  	 	 	 }
5440  	 	 	 // rendering mode
5441  	 	 	 $s .= sprintf('BT %d Tr %F w ET ', $this->textrendermode, ($this->textstrokewidth * $this->k));
5442  	 	 	 // count number of spaces
5443  	 	 	 $ns = substr_count($txt, chr(32));
5444  	 	 	 // Justification
5445  	 	 	 $spacewidth = 0;
5446  	 	 	 if (($align == 'J') AND ($ns > 0)) {
5447  	 	 	 	 if ($this->isUnicodeFont()) {
5448  	 	 	 	 	 // get string width without spaces
5449  	 	 	 	 	 $width = $this->GetStringWidth(str_replace(' ', '', $txt));
5450  	 	 	 	 	 // calculate average space width
5451  	 	 	 	 	 $spacewidth = -1000 * ($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1) / ($this->FontSize?$this->FontSize:1);
5452  	 	 	 	 	 if ($this->font_stretching != 100) {
5453  	 	 	 	 	 	 // word spacing is affected by stretching
5454  	 	 	 	 	 	 $spacewidth /= ($this->font_stretching / 100);
5455  	 	 	 	 	 }
5456  	 	 	 	 	 // set word position to be used with TJ operator
5457  	 	 	 	 	 $txt2 = str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacewidth).' (', $txt2);
5458  	 	 	 	 	 $unicode_justification = true;
5459  	 	 	 	 } else {
5460  	 	 	 	 	 // get string width
5461  	 	 	 	 	 $width = $txwidth;
5462  	 	 	 	 	 // new space width
5463  	 	 	 	 	 $spacewidth = (($w - $width - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1)) * $this->k;
5464  	 	 	 	 	 if ($this->font_stretching != 100) {
5465  	 	 	 	 	 	 // word spacing (Tw) is affected by stretching
5466  	 	 	 	 	 	 $spacewidth /= ($this->font_stretching / 100);
5467  	 	 	 	 	 }
5468  	 	 	 	 	 // set word spacing
5469  	 	 	 	 	 $rs .= sprintf('BT %F Tw ET ', $spacewidth);
5470  	 	 	 	 }
5471  	 	 	 	 $width = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
5472  	 	 	 }
5473  	 	 	 // replace carriage return characters
5474  	 	 	 $txt2 = str_replace("\r", ' ', $txt2);
5475  	 	 	 switch ($align) {
5476  	 	 	 	 case 'C': {
5477  	 	 	 	 	 $dx = ($w - $width) / 2;
5478  	 	 	 	 	 break;
5479  	 	 	 	 }
5480  	 	 	 	 case 'R': {
5481  	 	 	 	 	 if ($this->rtl) {
5482  	 	 	 	 	 	 $dx = $this->cell_padding['R'];
5483  	 	 	 	 	 } else {
5484  	 	 	 	 	 	 $dx = $w - $width - $this->cell_padding['R'];
5485  	 	 	 	 	 }
5486  	 	 	 	 	 break;
5487  	 	 	 	 }
5488  	 	 	 	 case 'L': {
5489  	 	 	 	 	 if ($this->rtl) {
5490  	 	 	 	 	 	 $dx = $w - $width - $this->cell_padding['L'];
5491  	 	 	 	 	 } else {
5492  	 	 	 	 	 	 $dx = $this->cell_padding['L'];
5493  	 	 	 	 	 }
5494  	 	 	 	 	 break;
5495  	 	 	 	 }
5496  	 	 	 	 case 'J':
5497  	 	 	 	 default: {
5498  	 	 	 	 	 if ($this->rtl) {
5499  	 	 	 	 	 	 $dx = $this->cell_padding['R'];
5500  	 	 	 	 	 } else {
5501  	 	 	 	 	 	 $dx = $this->cell_padding['L'];
5502  	 	 	 	 	 }
5503  	 	 	 	 	 break;
5504  	 	 	 	 }
5505  	 	 	 }
5506  	 	 	 if ($this->rtl) {
5507  	 	 	 	 $xdx = $x - $dx - $width;
5508  	 	 	 } else {
5509  	 	 	 	 $xdx = $x + $dx;
5510  	 	 	 }
5511  	 	 	 $xdk = $xdx * $k;
5512  	 	 	 // print text
5513  	 	 	 $s .= sprintf('BT %F %F Td [(%s)] TJ ET', $xdk, (($this->h - $basefonty) * $k), $txt2);
5514  	 	 	 if (isset($uniblock)) {
5515  	 	 	 	 // print overlapping characters as separate string
5516  	 	 	 	 $xshift = 0; // horizontal shift
5517  	 	 	 	 $ty = (($this->h - $basefonty + (0.2 * $this->FontSize)) * $k);
5518  	 	 	 	 $spw = (($w - $txwidth - $this->cell_padding['L'] - $this->cell_padding['R']) / ($ns?$ns:1));
5519  	 	 	 	 foreach ($uniblock as $uk => $uniarr) {
5520  	 	 	 	 	 if (($uk % 2) == 0) {
5521  	 	 	 	 	 	 // x space to skip
5522  	 	 	 	 	 	 if ($spacewidth != 0) {
5523  	 	 	 	 	 	 	 // justification shift
5524  	 	 	 	 	 	 	 $xshift += (count(array_keys($uniarr, 32)) * $spw);
5525  	 	 	 	 	 	 }
5526  	 	 	 	 	 	 $xshift += $this->GetArrStringWidth($uniarr); // + shift justification
5527  	 	 	 	 	 } else {
5528  	 	 	 	 	 	 // character to print
5529  	 	 	 	 	 	 $topchr = TCPDF_FONTS::arrUTF8ToUTF16BE($uniarr, false);
5530  	 	 	 	 	 	 $topchr = TCPDF_STATIC::_escape($topchr);
5531  	 	 	 	 	 	 $s .= sprintf(' BT %F %F Td [(%s)] TJ ET', ($xdk + ($xshift * $k)), $ty, $topchr);
5532  	 	 	 	 	 }
5533  	 	 	 	 }
5534  	 	 	 }
5535  	 	 	 if ($this->underline) {
5536  	 	 	 	 $s .= ' '.$this->_dounderlinew($xdx, $basefonty, $width);
5537  	 	 	 }
5538  	 	 	 if ($this->linethrough) {
5539  	 	 	 	 $s .= ' '.$this->_dolinethroughw($xdx, $basefonty, $width);
5540  	 	 	 }
5541  	 	 	 if ($this->overline) {
5542  	 	 	 	 $s .= ' '.$this->_dooverlinew($xdx, $basefonty, $width);
5543  	 	 	 }
5544  	 	 	 if ($this->ColorFlag AND ($this->textrendermode < 4)) {
5545  	 	 	 	 $s .= ' Q';
5546  	 	 	 }
5547  	 	 	 if ($link) {
5548  	 	 	 	 $this->Link($xdx, $yt, $width, ($this->FontAscent + $this->FontDescent), $link, $ns);
5549  	 	 	 }
5550  	 	 }
5551  	 	 // output cell
5552  	 	 if ($s) {
5553  	 	 	 // output cell
5554  	 	 	 $rs .= $s;
5555  	 	 	 if ($this->font_spacing != 0) {
5556  	 	 	 	 // reset font spacing mode
5557  	 	 	 	 $rs .= ' BT 0 Tc ET';
5558  	 	 	 }
5559  	 	 	 if ($this->font_stretching != 100) {
5560  	 	 	 	 // reset font stretching mode
5561  	 	 	 	 $rs .= ' BT 100 Tz ET';
5562  	 	 	 }
5563  	 	 }
5564  	 	 // reset word spacing
5565  	 	 if (!$this->isUnicodeFont() AND ($align == 'J')) {
5566  	 	 	 $rs .= ' BT 0 Tw ET';
5567  	 	 }
5568  	 	 // reset stretching and spacing
5569  	 	 $this->font_stretching = $prev_font_stretching;
5570  	 	 $this->font_spacing = $prev_font_spacing;
5571  	 	 $this->lasth = $h;
5572  	 	 if ($ln > 0) {
5573  	 	 	 //Go to the beginning of the next line
5574  	 	 	 $this->y = $y + $h + $this->cell_margin['B'];
5575  	 	 	 if ($ln == 1) {
5576  	 	 	 	 if ($this->rtl) {
5577  	 	 	 	 	 $this->x = $this->w - $this->rMargin;
5578  	 	 	 	 } else {
5579  	 	 	 	 	 $this->x = $this->lMargin;
5580  	 	 	 	 }
5581  	 	 	 }
5582  	 	 } else {
5583  	 	 	 // go left or right by case
5584  	 	 	 if ($this->rtl) {
5585  	 	 	 	 $this->x = $x - $w - $this->cell_margin['L'];
5586  	 	 	 } else {
5587  	 	 	 	 $this->x = $x + $w + $this->cell_margin['R'];
5588  	 	 	 }
5589  	 	 }
5590  	 	 $gstyles = ''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor."\n";
5591  	 	 $rs = $gstyles.$rs;
5592  	 	 $this->cell_padding = $prev_cell_padding;
5593  	 	 $this->cell_margin = $prev_cell_margin;
5594  	 	 return $rs;
5595  	 }
5596  
5597  	 /**
5598  	  * Replace a char if is defined on the current font.
5599  	  * @param $oldchar (int) Integer code (unicode) of the character to replace.
5600  	  * @param $newchar (int) Integer code (unicode) of the new character.
5601  	  * @return int the replaced char or the old char in case the new char i not defined
5602  	  * @protected
5603  	  * @since 5.9.167 (2012-06-22)
5604  	  */
5605  	protected function replaceChar($oldchar, $newchar) {
5606  	 	 if ($this->isCharDefined($newchar)) {
5607  	 	 	 // add the new char on the subset list
5608  	 	 	 $this->CurrentFont['subsetchars'][$newchar] = true;
5609  	 	 	 // return the new character
5610  	 	 	 return $newchar;
5611  	 	 }
5612  	 	 // return the old char
5613  	 	 return $oldchar;
5614  	 }
5615  
5616  	 /**
5617  	  * Returns the code to draw the cell border
5618  	  * @param $x (float) X coordinate.
5619  	  * @param $y (float) Y coordinate.
5620  	  * @param $w (float) Cell width.
5621  	  * @param $h (float) Cell height.
5622  	  * @param $brd (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5623  	  * @return string containing cell border code
5624  	  * @protected
5625  	  * @see SetLineStyle()
5626  	  * @since 5.7.000 (2010-08-02)
5627  	  */
5628  	protected function getCellBorder($x, $y, $w, $h, $brd) {
5629  	 	 $s = ''; // string to be returned
5630  	 	 if (empty($brd)) {
5631  	 	 	 return $s;
5632  	 	 }
5633  	 	 if ($brd == 1) {
5634  	 	 	 $brd = array('LRTB' => true);
5635  	 	 }
5636  	 	 // calculate coordinates for border
5637  	 	 $k = $this->k;
5638  	 	 if ($this->rtl) {
5639  	 	 	 $xeL = ($x - $w) * $k;
5640  	 	 	 $xeR = $x * $k;
5641  	 	 } else {
5642  	 	 	 $xeL = $x * $k;
5643  	 	 	 $xeR = ($x + $w) * $k;
5644  	 	 }
5645  	 	 $yeL = (($this->h - ($y + $h)) * $k);
5646  	 	 $yeT = (($this->h - $y) * $k);
5647  	 	 $xeT = $xeL;
5648  	 	 $xeB = $xeR;
5649  	 	 $yeR = $yeT;
5650  	 	 $yeB = $yeL;
5651  	 	 if (is_string($brd)) {
5652  	 	 	 // convert string to array
5653  	 	 	 $slen = strlen($brd);
5654  	 	 	 $newbrd = array();
5655  	 	 	 for ($i = 0; $i < $slen; ++$i) {
5656  	 	 	 	 $newbrd[$brd[$i]] = array('cap' => 'square', 'join' => 'miter');
5657  	 	 	 }
5658  	 	 	 $brd = $newbrd;
5659  	 	 }
5660  	 	 if (isset($brd['mode'])) {
5661  	 	 	 $mode = $brd['mode'];
5662  	 	 	 unset($brd['mode']);
5663  	 	 } else {
5664  	 	 	 $mode = 'normal';
5665  	 	 }
5666  	 	 foreach ($brd as $border => $style) {
5667  	 	 	 if (is_array($style) AND !empty($style)) {
5668  	 	 	 	 // apply border style
5669  	 	 	 	 $prev_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' ';
5670  	 	 	 	 $s .= $this->SetLineStyle($style, true)."\n";
5671  	 	 	 }
5672  	 	 	 switch ($mode) {
5673  	 	 	 	 case 'ext': {
5674  	 	 	 	 	 $off = (($this->LineWidth / 2) * $k);
5675  	 	 	 	 	 $xL = $xeL - $off;
5676  	 	 	 	 	 $xR = $xeR + $off;
5677  	 	 	 	 	 $yT = $yeT + $off;
5678  	 	 	 	 	 $yL = $yeL - $off;
5679  	 	 	 	 	 $xT = $xL;
5680  	 	 	 	 	 $xB = $xR;
5681  	 	 	 	 	 $yR = $yT;
5682  	 	 	 	 	 $yB = $yL;
5683  	 	 	 	 	 $w += $this->LineWidth;
5684  	 	 	 	 	 $h += $this->LineWidth;
5685  	 	 	 	 	 break;
5686  	 	 	 	 }
5687  	 	 	 	 case 'int': {
5688  	 	 	 	 	 $off = ($this->LineWidth / 2) * $k;
5689  	 	 	 	 	 $xL = $xeL + $off;
5690  	 	 	 	 	 $xR = $xeR - $off;
5691  	 	 	 	 	 $yT = $yeT - $off;
5692  	 	 	 	 	 $yL = $yeL + $off;
5693  	 	 	 	 	 $xT = $xL;
5694  	 	 	 	 	 $xB = $xR;
5695  	 	 	 	 	 $yR = $yT;
5696  	 	 	 	 	 $yB = $yL;
5697  	 	 	 	 	 $w -= $this->LineWidth;
5698  	 	 	 	 	 $h -= $this->LineWidth;
5699  	 	 	 	 	 break;
5700  	 	 	 	 }
5701  	 	 	 	 case 'normal':
5702  	 	 	 	 default: {
5703  	 	 	 	 	 $xL = $xeL;
5704  	 	 	 	 	 $xT = $xeT;
5705  	 	 	 	 	 $xB = $xeB;
5706  	 	 	 	 	 $xR = $xeR;
5707  	 	 	 	 	 $yL = $yeL;
5708  	 	 	 	 	 $yT = $yeT;
5709  	 	 	 	 	 $yB = $yeB;
5710  	 	 	 	 	 $yR = $yeR;
5711  	 	 	 	 	 break;
5712  	 	 	 	 }
5713  	 	 	 }
5714  	 	 	 // draw borders by case
5715  	 	 	 if (strlen($border) == 4) {
5716  	 	 	 	 $s .= sprintf('%F %F %F %F re S ', $xT, $yT, ($w * $k), (-$h * $k));
5717  	 	 	 } elseif (strlen($border) == 3) {
5718  	 	 	 	 if (strpos($border,'B') === false) { // LTR
5719  	 	 	 	 	 $s .= sprintf('%F %F m ', $xL, $yL);
5720  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5721  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5722  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5723  	 	 	 	 	 $s .= 'S ';
5724  	 	 	 	 } elseif (strpos($border,'L') === false) { // TRB
5725  	 	 	 	 	 $s .= sprintf('%F %F m ', $xT, $yT);
5726  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5727  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5728  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5729  	 	 	 	 	 $s .= 'S ';
5730  	 	 	 	 } elseif (strpos($border,'T') === false) { // RBL
5731  	 	 	 	 	 $s .= sprintf('%F %F m ', $xR, $yR);
5732  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5733  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5734  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5735  	 	 	 	 	 $s .= 'S ';
5736  	 	 	 	 } elseif (strpos($border,'R') === false) { // BLT
5737  	 	 	 	 	 $s .= sprintf('%F %F m ', $xB, $yB);
5738  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5739  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5740  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5741  	 	 	 	 	 $s .= 'S ';
5742  	 	 	 	 }
5743  	 	 	 } elseif (strlen($border) == 2) {
5744  	 	 	 	 if ((strpos($border,'L') !== false) AND (strpos($border,'T') !== false)) { // LT
5745  	 	 	 	 	 $s .= sprintf('%F %F m ', $xL, $yL);
5746  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5747  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5748  	 	 	 	 	 $s .= 'S ';
5749  	 	 	 	 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'R') !== false)) { // TR
5750  	 	 	 	 	 $s .= sprintf('%F %F m ', $xT, $yT);
5751  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5752  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5753  	 	 	 	 	 $s .= 'S ';
5754  	 	 	 	 } elseif ((strpos($border,'R') !== false) AND (strpos($border,'B') !== false)) { // RB
5755  	 	 	 	 	 $s .= sprintf('%F %F m ', $xR, $yR);
5756  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5757  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5758  	 	 	 	 	 $s .= 'S ';
5759  	 	 	 	 } elseif ((strpos($border,'B') !== false) AND (strpos($border,'L') !== false)) { // BL
5760  	 	 	 	 	 $s .= sprintf('%F %F m ', $xB, $yB);
5761  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5762  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5763  	 	 	 	 	 $s .= 'S ';
5764  	 	 	 	 } elseif ((strpos($border,'L') !== false) AND (strpos($border,'R') !== false)) { // LR
5765  	 	 	 	 	 $s .= sprintf('%F %F m ', $xL, $yL);
5766  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5767  	 	 	 	 	 $s .= 'S ';
5768  	 	 	 	 	 $s .= sprintf('%F %F m ', $xR, $yR);
5769  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5770  	 	 	 	 	 $s .= 'S ';
5771  	 	 	 	 } elseif ((strpos($border,'T') !== false) AND (strpos($border,'B') !== false)) { // TB
5772  	 	 	 	 	 $s .= sprintf('%F %F m ', $xT, $yT);
5773  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5774  	 	 	 	 	 $s .= 'S ';
5775  	 	 	 	 	 $s .= sprintf('%F %F m ', $xB, $yB);
5776  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5777  	 	 	 	 	 $s .= 'S ';
5778  	 	 	 	 }
5779  	 	 	 } else { // strlen($border) == 1
5780  	 	 	 	 if (strpos($border,'L') !== false) { // L
5781  	 	 	 	 	 $s .= sprintf('%F %F m ', $xL, $yL);
5782  	 	 	 	 	 $s .= sprintf('%F %F l ', $xT, $yT);
5783  	 	 	 	 	 $s .= 'S ';
5784  	 	 	 	 } elseif (strpos($border,'T') !== false) { // T
5785  	 	 	 	 	 $s .= sprintf('%F %F m ', $xT, $yT);
5786  	 	 	 	 	 $s .= sprintf('%F %F l ', $xR, $yR);
5787  	 	 	 	 	 $s .= 'S ';
5788  	 	 	 	 } elseif (strpos($border,'R') !== false) { // R
5789  	 	 	 	 	 $s .= sprintf('%F %F m ', $xR, $yR);
5790  	 	 	 	 	 $s .= sprintf('%F %F l ', $xB, $yB);
5791  	 	 	 	 	 $s .= 'S ';
5792  	 	 	 	 } elseif (strpos($border,'B') !== false) { // B
5793  	 	 	 	 	 $s .= sprintf('%F %F m ', $xB, $yB);
5794  	 	 	 	 	 $s .= sprintf('%F %F l ', $xL, $yL);
5795  	 	 	 	 	 $s .= 'S ';
5796  	 	 	 	 }
5797  	 	 	 }
5798  	 	 	 if (is_array($style) AND !empty($style)) {
5799  	 	 	 	 // reset border style to previous value
5800  	 	 	 	 $s .= "\n".$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor."\n";
5801  	 	 	 }
5802  	 	 }
5803  	 	 return $s;
5804  	 }
5805  
5806  	 /**
5807  	  * This method allows printing text with line breaks.
5808  	  * 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 />
5809  	  * Text can be aligned, centered or justified. The cell block can be framed and the background painted.
5810  	  * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
5811  	  * @param $h (float) Cell minimum height. The cell extends automatically if needed.
5812  	  * @param $txt (string) String to print
5813  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
5814  	  * @param $align (string) 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>
5815  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
5816  	  * @param $ln (int) 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>
5817  	  * @param $x (float) x position in user units
5818  	  * @param $y (float) y position in user units
5819  	  * @param $reseth (boolean) if true reset the last cell height (default true).
5820  	  * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
5821  	  * @param $ishtml (boolean) INTERNAL USE ONLY -- set to true if $txt is HTML content (default = false). Never set this parameter to true, use instead writeHTMLCell() or writeHTML() methods.
5822  	  * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
5823  	  * @param $maxh (float) 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.
5824  	  * @param $valign (string) Vertical alignment of text (requires $maxh = $h > 0). Possible values are:<ul><li>T: TOP</li><li>M: middle</li><li>B: bottom</li></ul>. This feature works only when $ishtml=false and the cell must fit in a single page.
5825  	  * @param $fitcell (boolean) if true attempt to fit all the text within the cell by reducing the font size (do not work in HTML mode). $maxh must be greater than 0 and equal to $h.
5826  	  * @return int Return the number of cells or 1 for html mode.
5827  	  * @public
5828  	  * @since 1.3
5829  	  * @see SetFont(), SetDrawColor(), SetFillColor(), SetTextColor(), SetLineWidth(), Cell(), Write(), SetAutoPageBreak()
5830  	  */
5831  	public function MultiCell($w, $h, $txt, $border=0, $align='J', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0, $valign='T', $fitcell=false) {
5832  	 	 $prev_cell_margin = $this->cell_margin;
5833  	 	 $prev_cell_padding = $this->cell_padding;
5834  	 	 // adjust internal padding
5835  	 	 $this->adjustCellPadding($border);
5836  	 	 $mc_padding = $this->cell_padding;
5837  	 	 $mc_margin = $this->cell_margin;
5838  	 	 $this->cell_padding['T'] = 0;
5839  	 	 $this->cell_padding['B'] = 0;
5840  	 	 $this->setCellMargins(0, 0, 0, 0);
5841  	 	 if (TCPDF_STATIC::empty_string($this->lasth) OR $reseth) {
5842  	 	 	 // reset row height
5843  	 	 	 $this->resetLastH();
5844  	 	 }
5845  	 	 if (!TCPDF_STATIC::empty_string($y)) {
5846  	 	 	 $this->SetY($y); // set y in order to convert negative y values to positive ones
5847  	 	 }
5848  	 	 $y = $this->GetY();
5849  	 	 $resth = 0;
5850  	 	 if (($h > 0) AND $this->inPageBody() AND (($y + $h + $mc_margin['T'] + $mc_margin['B']) > $this->PageBreakTrigger)) {
5851  	 	 	 // spit cell in more pages/columns
5852  	 	 	 $newh = ($this->PageBreakTrigger - $y);
5853  	 	 	 $resth = ($h - $newh); // cell to be printed on the next page/column
5854  	 	 	 $h = $newh;
5855  	 	 }
5856  	 	 // get current page number
5857  	 	 $startpage = $this->page;
5858  	 	 // get current column
5859  	 	 $startcolumn = $this->current_column;
5860  	 	 if (!TCPDF_STATIC::empty_string($x)) {
5861  	 	 	 $this->SetX($x);
5862  	 	 } else {
5863  	 	 	 $x = $this->GetX();
5864  	 	 }
5865  	 	 // check page for no-write regions and adapt page margins if necessary
5866  	 	 list($x, $y) = $this->checkPageRegions(0, $x, $y);
5867  	 	 // apply margins
5868  	 	 $oy = $y + $mc_margin['T'];
5869  	 	 if ($this->rtl) {
5870  	 	 	 $ox = ($this->w - $x - $mc_margin['R']);
5871  	 	 } else {
5872  	 	 	 $ox = ($x + $mc_margin['L']);
5873  	 	 }
5874  	 	 $this->x = $ox;
5875  	 	 $this->y = $oy;
5876  	 	 // set width
5877  	 	 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
5878  	 	 	 if ($this->rtl) {
5879  	 	 	 	 $w = ($this->x - $this->lMargin - $mc_margin['L']);
5880  	 	 	 } else {
5881  	 	 	 	 $w = ($this->w - $this->x - $this->rMargin - $mc_margin['R']);
5882  	 	 	 }
5883  	 	 }
5884  	 	 // store original margin values
5885  	 	 $lMargin = $this->lMargin;
5886  	 	 $rMargin = $this->rMargin;
5887  	 	 if ($this->rtl) {
5888  	 	 	 $this->rMargin = ($this->w - $this->x);
5889  	 	 	 $this->lMargin = ($this->x - $w);
5890  	 	 } else {
5891  	 	 	 $this->lMargin = ($this->x);
5892  	 	 	 $this->rMargin = ($this->w - $this->x - $w);
5893  	 	 }
5894  	 	 $this->clMargin = $this->lMargin;
5895  	 	 $this->crMargin = $this->rMargin;
5896  	 	 if ($autopadding) {
5897  	 	 	 // add top padding
5898  	 	 	 $this->y += $mc_padding['T'];
5899  	 	 }
5900  	 	 if ($ishtml) { // ******* Write HTML text
5901  	 	 	 $this->writeHTML($txt, true, false, $reseth, true, $align);
5902  	 	 	 $nl = 1;
5903  	 	 } else { // ******* Write simple text
5904  	 	 	 $prev_FontSizePt = $this->FontSizePt;
5905  	 	 	 if ($fitcell) {
5906  	 	 	 	 // ajust height values
5907  	 	 	 	 $tobottom = ($this->h - $this->y - $this->bMargin - $this->cell_padding['T'] - $this->cell_padding['B']);
5908  	 	 	 	 $h = $maxh = max(min($h, $tobottom), min($maxh, $tobottom));
5909  	 	 	 }
5910  	 	 	 // vertical alignment
5911  	 	 	 if ($maxh > 0) {
5912  	 	 	 	 // get text height
5913  	 	 	 	 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5914  	 	 	 	 if ($fitcell AND ($text_height > $maxh) AND ($this->FontSizePt > 1)) {
5915  	 	 	 	 	 // try to reduce font size to fit text on cell (use a quick search algorithm)
5916  	 	 	 	 	 $fmin = 1;
5917  	 	 	 	 	 $fmax = $this->FontSizePt;
5918  	 	 	 	 	 $diff_epsilon = (1 / $this->k); // one point (min resolution)
5919  	 	 	 	 	 $maxit = (2 * min(100, max(10, intval($fmax)))); // max number of iterations
5920  	 	 	 	 	 while ($maxit >= 0) {
5921  	 	 	 	 	 	 $fmid = (($fmax + $fmin) / 2);
5922  	 	 	 	 	 	 $this->SetFontSize($fmid, false);
5923  	 	 	 	 	 	 $this->resetLastH();
5924  	 	 	 	 	 	 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5925  	 	 	 	 	 	 $diff = ($maxh - $text_height);
5926  	 	 	 	 	 	 if ($diff >= 0) {
5927  	 	 	 	 	 	 	 if ($diff <= $diff_epsilon) {
5928  	 	 	 	 	 	 	 	 break;
5929  	 	 	 	 	 	 	 }
5930  	 	 	 	 	 	 	 $fmin = $fmid;
5931  	 	 	 	 	 	 } else {
5932  	 	 	 	 	 	 	 $fmax = $fmid;
5933  	 	 	 	 	 	 }
5934  	 	 	 	 	 	 --$maxit;
5935  	 	 	 	 	 }
5936  	 	 	 	 	 if ($maxit < 0) {
5937  	 	 	 	 	 	 // premature exit, we get the minimum font value to fit the cell
5938  	 	 	 	 	 	 $this->SetFontSize($fmin);
5939  	 	 	 	 	 	 $this->resetLastH();
5940  	 	 	 	 	 	 $text_height = $this->getStringHeight($w, $txt, $reseth, $autopadding, $mc_padding, $border);
5941  	 	 	 	 	 } else {
5942  	 	 	 	 	 	 $this->SetFontSize($fmid);
5943  	 	 	 	 	 	 $this->resetLastH();
5944  	 	 	 	 	 }
5945  	 	 	 	 }
5946  	 	 	 	 if ($text_height < $maxh) {
5947  	 	 	 	 	 if ($valign == 'M') {
5948  	 	 	 	 	 	 // text vertically centered
5949  	 	 	 	 	 	 $this->y += (($maxh - $text_height) / 2);
5950  	 	 	 	 	 } elseif ($valign == 'B') {
5951  	 	 	 	 	 	 // text vertically aligned on bottom
5952  	 	 	 	 	 	 $this->y += ($maxh - $text_height);
5953  	 	 	 	 	 }
5954  	 	 	 	 }
5955  	 	 	 }
5956  	 	 	 $nl = $this->Write($this->lasth, $txt, '', 0, $align, true, $stretch, false, true, $maxh, 0, $mc_margin);
5957  	 	 	 if ($fitcell) {
5958  	 	 	 	 // restore font size
5959  	 	 	 	 $this->SetFontSize($prev_FontSizePt);
5960  	 	 	 }
5961  	 	 }
5962  	 	 if ($autopadding) {
5963  	 	 	 // add bottom padding
5964  	 	 	 $this->y += $mc_padding['B'];
5965  	 	 }
5966  	 	 // Get end-of-text Y position
5967  	 	 $currentY = $this->y;
5968  	 	 // get latest page number
5969  	 	 $endpage = $this->page;
5970  	 	 if ($resth > 0) {
5971  	 	 	 $skip = ($endpage - $startpage);
5972  	 	 	 $tmpresth = $resth;
5973  	 	 	 while ($tmpresth > 0) {
5974  	 	 	 	 if ($skip <= 0) {
5975  	 	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
5976  	 	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
5977  	 	 	 	 }
5978  	 	 	 	 if ($this->num_columns > 1) {
5979  	 	 	 	 	 $tmpresth -= ($this->h - $this->y - $this->bMargin);
5980  	 	 	 	 } else {
5981  	 	 	 	 	 $tmpresth -= ($this->h - $this->tMargin - $this->bMargin);
5982  	 	 	 	 }
5983  	 	 	 	 --$skip;
5984  	 	 	 }
5985  	 	 	 $currentY = $this->y;
5986  	 	 	 $endpage = $this->page;
5987  	 	 }
5988  	 	 // get latest column
5989  	 	 $endcolumn = $this->current_column;
5990  	 	 if ($this->num_columns == 0) {
5991  	 	 	 $this->num_columns = 1;
5992  	 	 }
5993  	 	 // disable page regions check
5994  	 	 $check_page_regions = $this->check_page_regions;
5995  	 	 $this->check_page_regions = false;
5996  	 	 // get border modes
5997  	 	 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
5998  	 	 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
5999  	 	 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
6000  	 	 // design borders around HTML cells.
6001  	 	 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
6002  	 	 	 $ccode = '';
6003  	 	 	 $this->setPage($page);
6004  	 	 	 if ($this->num_columns < 2) {
6005  	 	 	 	 // single-column mode
6006  	 	 	 	 $this->SetX($x);
6007  	 	 	 	 $this->y = $this->tMargin;
6008  	 	 	 }
6009  	 	 	 // account for margin changes
6010  	 	 	 if ($page > $startpage) {
6011  	 	 	 	 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
6012  	 	 	 	 	 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
6013  	 	 	 	 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
6014  	 	 	 	 	 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
6015  	 	 	 	 }
6016  	 	 	 }
6017  	 	 	 if ($startpage == $endpage) {
6018  	 	 	 	 // single page
6019  	 	 	 	 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
6020  	 	 	 	 	 if ($column != $this->current_column) {
6021  	 	 	 	 	 	 $this->selectColumn($column);
6022  	 	 	 	 	 }
6023  	 	 	 	 	 if ($this->rtl) {
6024  	 	 	 	 	 	 $this->x -= $mc_margin['R'];
6025  	 	 	 	 	 } else {
6026  	 	 	 	 	 	 $this->x += $mc_margin['L'];
6027  	 	 	 	 	 }
6028  	 	 	 	 	 if ($startcolumn == $endcolumn) { // single column
6029  	 	 	 	 	 	 $cborder = $border;
6030  	 	 	 	 	 	 $h = max($h, ($currentY - $oy));
6031  	 	 	 	 	 	 $this->y = $oy;
6032  	 	 	 	 	 } elseif ($column == $startcolumn) { // first column
6033  	 	 	 	 	 	 $cborder = $border_start;
6034  	 	 	 	 	 	 $this->y = $oy;
6035  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
6036  	 	 	 	 	 } elseif ($column == $endcolumn) { // end column
6037  	 	 	 	 	 	 $cborder = $border_end;
6038  	 	 	 	 	 	 $h = $currentY - $this->y;
6039  	 	 	 	 	 	 if ($resth > $h) {
6040  	 	 	 	 	 	 	 $h = $resth;
6041  	 	 	 	 	 	 }
6042  	 	 	 	 	 } else { // middle column
6043  	 	 	 	 	 	 $cborder = $border_middle;
6044  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
6045  	 	 	 	 	 	 $resth -= $h;
6046  	 	 	 	 	 }
6047  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6048  	 	 	 	 } // end for each column
6049  	 	 	 } elseif ($page == $startpage) { // first page
6050  	 	 	 	 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
6051  	 	 	 	 	 if ($column != $this->current_column) {
6052  	 	 	 	 	 	 $this->selectColumn($column);
6053  	 	 	 	 	 }
6054  	 	 	 	 	 if ($this->rtl) {
6055  	 	 	 	 	 	 $this->x -= $mc_margin['R'];
6056  	 	 	 	 	 } else {
6057  	 	 	 	 	 	 $this->x += $mc_margin['L'];
6058  	 	 	 	 	 }
6059  	 	 	 	 	 if ($column == $startcolumn) { // first column
6060  	 	 	 	 	 	 $cborder = $border_start;
6061  	 	 	 	 	 	 $this->y = $oy;
6062  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
6063  	 	 	 	 	 } else { // middle column
6064  	 	 	 	 	 	 $cborder = $border_middle;
6065  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
6066  	 	 	 	 	 	 $resth -= $h;
6067  	 	 	 	 	 }
6068  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6069  	 	 	 	 } // end for each column
6070  	 	 	 } elseif ($page == $endpage) { // last page
6071  	 	 	 	 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
6072  	 	 	 	 	 if ($column != $this->current_column) {
6073  	 	 	 	 	 	 $this->selectColumn($column);
6074  	 	 	 	 	 }
6075  	 	 	 	 	 if ($this->rtl) {
6076  	 	 	 	 	 	 $this->x -= $mc_margin['R'];
6077  	 	 	 	 	 } else {
6078  	 	 	 	 	 	 $this->x += $mc_margin['L'];
6079  	 	 	 	 	 }
6080  	 	 	 	 	 if ($column == $endcolumn) {
6081  	 	 	 	 	 	 // end column
6082  	 	 	 	 	 	 $cborder = $border_end;
6083  	 	 	 	 	 	 $h = $currentY - $this->y;
6084  	 	 	 	 	 	 if ($resth > $h) {
6085  	 	 	 	 	 	 	 $h = $resth;
6086  	 	 	 	 	 	 }
6087  	 	 	 	 	 } else {
6088  	 	 	 	 	 	 // middle column
6089  	 	 	 	 	 	 $cborder = $border_middle;
6090  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
6091  	 	 	 	 	 	 $resth -= $h;
6092  	 	 	 	 	 }
6093  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6094  	 	 	 	 } // end for each column
6095  	 	 	 } else { // middle page
6096  	 	 	 	 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
6097  	 	 	 	 	 $this->selectColumn($column);
6098  	 	 	 	 	 if ($this->rtl) {
6099  	 	 	 	 	 	 $this->x -= $mc_margin['R'];
6100  	 	 	 	 	 } else {
6101  	 	 	 	 	 	 $this->x += $mc_margin['L'];
6102  	 	 	 	 	 }
6103  	 	 	 	 	 $cborder = $border_middle;
6104  	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
6105  	 	 	 	 	 $resth -= $h;
6106  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
6107  	 	 	 	 } // end for each column
6108  	 	 	 }
6109  	 	 	 if ($cborder OR $fill) {
6110  	 	 	 	 $offsetlen = strlen($ccode);
6111  	 	 	 	 // draw border and fill
6112  	 	 	 	 if ($this->inxobj) {
6113  	 	 	 	 	 // we are inside an XObject template
6114  	 	 	 	 	 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
6115  	 	 	 	 	 	 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
6116  	 	 	 	 	 	 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
6117  	 	 	 	 	 	 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
6118  	 	 	 	 	 } else {
6119  	 	 	 	 	 	 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
6120  	 	 	 	 	 	 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
6121  	 	 	 	 	 }
6122  	 	 	 	 	 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
6123  	 	 	 	 	 $pstart = substr($pagebuff, 0, $pagemark);
6124  	 	 	 	 	 $pend = substr($pagebuff, $pagemark);
6125  	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
6126  	 	 	 	 } else {
6127  	 	 	 	 	 if (end($this->transfmrk[$this->page]) !== false) {
6128  	 	 	 	 	 	 $pagemarkkey = key($this->transfmrk[$this->page]);
6129  	 	 	 	 	 	 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
6130  	 	 	 	 	 	 $this->transfmrk[$this->page][$pagemarkkey] += $offsetlen;
6131  	 	 	 	 	 } elseif ($this->InFooter) {
6132  	 	 	 	 	 	 $pagemark = $this->footerpos[$this->page];
6133  	 	 	 	 	 	 $this->footerpos[$this->page] += $offsetlen;
6134  	 	 	 	 	 } else {
6135  	 	 	 	 	 	 $pagemark = $this->intmrk[$this->page];
6136  	 	 	 	 	 	 $this->intmrk[$this->page] += $offsetlen;
6137  	 	 	 	 	 }
6138  	 	 	 	 	 $pagebuff = $this->getPageBuffer($this->page);
6139  	 	 	 	 	 $pstart = substr($pagebuff, 0, $pagemark);
6140  	 	 	 	 	 $pend = substr($pagebuff, $pagemark);
6141  	 	 	 	 	 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
6142  	 	 	 	 }
6143  	 	 	 }
6144  	 	 } // end for each page
6145  	 	 // restore page regions check
6146  	 	 $this->check_page_regions = $check_page_regions;
6147  	 	 // Get end-of-cell Y position
6148  	 	 $currentY = $this->GetY();
6149  	 	 // restore previous values
6150  	 	 if ($this->num_columns > 1) {
6151  	 	 	 $this->selectColumn();
6152  	 	 } else {
6153  	 	 	 // restore original margins
6154  	 	 	 $this->lMargin = $lMargin;
6155  	 	 	 $this->rMargin = $rMargin;
6156  	 	 	 if ($this->page > $startpage) {
6157  	 	 	 	 // check for margin variations between pages (i.e. booklet mode)
6158  	 	 	 	 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$startpage]['olm']);
6159  	 	 	 	 $dr = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$startpage]['orm']);
6160  	 	 	 	 if (($dl != 0) OR ($dr != 0)) {
6161  	 	 	 	 	 $this->lMargin += $dl;
6162  	 	 	 	 	 $this->rMargin += $dr;
6163  	 	 	 	 }
6164  	 	 	 }
6165  	 	 }
6166  	 	 if ($ln > 0) {
6167  	 	 	 //Go to the beginning of the next line
6168  	 	 	 $this->SetY($currentY + $mc_margin['B']);
6169  	 	 	 if ($ln == 2) {
6170  	 	 	 	 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6171  	 	 	 }
6172  	 	 } else {
6173  	 	 	 // go left or right by case
6174  	 	 	 $this->setPage($startpage);
6175  	 	 	 $this->y = $y;
6176  	 	 	 $this->SetX($x + $w + $mc_margin['L'] + $mc_margin['R']);
6177  	 	 }
6178  	 	 $this->setContentMark();
6179  	 	 $this->cell_padding = $prev_cell_padding;
6180  	 	 $this->cell_margin = $prev_cell_margin;
6181  	 	 $this->clMargin = $this->lMargin;
6182  	 	 $this->crMargin = $this->rMargin;
6183  	 	 return $nl;
6184  	 }
6185  
6186  	 /**
6187  	  * This method return the estimated number of lines for print a simple text string using Multicell() method.
6188  	  * @param $txt (string) String for calculating his height
6189  	  * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6190  	  * @param $reseth (boolean) if true reset the last cell height (default false).
6191  	  * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6192  	  * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6193  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6194  	  * @return float Return the minimal height needed for multicell method for printing the $txt param.
6195  	  * @author Alexander Escalona Fern\E1ndez, Nicola Asuni
6196  	  * @public
6197  	  * @since 4.5.011
6198  	  */
6199  	public function getNumLines($txt, $w=0, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6200  	 	 if ($txt === NULL) {
6201  	 	 	 return 0;
6202  	 	 }
6203  	 	 if ($txt === '') {
6204  	 	 	 // empty string
6205  	 	 	 return 1;
6206  	 	 }
6207  	 	 // adjust internal padding
6208  	 	 $prev_cell_padding = $this->cell_padding;
6209  	 	 $prev_lasth = $this->lasth;
6210  	 	 if (is_array($cellpadding)) {
6211  	 	 	 $this->cell_padding = $cellpadding;
6212  	 	 }
6213  	 	 $this->adjustCellPadding($border);
6214  	 	 if (TCPDF_STATIC::empty_string($w) OR ($w <= 0)) {
6215  	 	 	 if ($this->rtl) {
6216  	 	 	 	 $w = $this->x - $this->lMargin;
6217  	 	 	 } else {
6218  	 	 	 	 $w = $this->w - $this->rMargin - $this->x;
6219  	 	 	 }
6220  	 	 }
6221  	 	 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6222  	 	 if ($reseth) {
6223  	 	 	 // reset row height
6224  	 	 	 $this->resetLastH();
6225  	 	 }
6226  	 	 $lines = 1;
6227  	 	 $sum = 0;
6228  	 	 $chars = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($txt, $this->isunicode, $this->CurrentFont), $txt, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6229  	 	 $charsWidth = $this->GetArrStringWidth($chars, '', '', 0, true);
6230  	 	 $length = count($chars);
6231  	 	 $lastSeparator = -1;
6232  	 	 for ($i = 0; $i < $length; ++$i) {
6233  	 	 	 $c = $chars[$i];
6234  	 	 	 $charWidth = $charsWidth[$i];
6235  	 	 	 if (($c != 160)
6236  	 	 	 	 	 AND (($c == 173)
6237  	 	 	 	 	 	 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6238  	 	 	 	 	 	 OR (($c == 45)
6239  	 	 	 	 	 	 	 AND ($i > 0) AND ($i < ($length - 1))
6240  	 	 	 	 	 	 	 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i - 1)], $this->isunicode))
6241  	 	 	 	 	 	 	 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6242  	 	 	 	 	 	 )
6243  	 	 	 	 	 )
6244  	 	 	 	 ) {
6245  	 	 	 	 $lastSeparator = $i;
6246  	 	 	 }
6247  	 	 	 if ((($sum + $charWidth) > $wmax) OR ($c == 10)) {
6248  	 	 	 	 ++$lines;
6249  	 	 	 	 if ($c == 10) {
6250  	 	 	 	 	 $lastSeparator = -1;
6251  	 	 	 	 	 $sum = 0;
6252  	 	 	 	 } elseif ($lastSeparator != -1) {
6253  	 	 	 	 	 $i = $lastSeparator;
6254  	 	 	 	 	 $lastSeparator = -1;
6255  	 	 	 	 	 $sum = 0;
6256  	 	 	 	 } else {
6257  	 	 	 	 	 $sum = $charWidth;
6258  	 	 	 	 }
6259  	 	 	 } else {
6260  	 	 	 	 $sum += $charWidth;
6261  	 	 	 }
6262  	 	 }
6263  	 	 if ($chars[($length - 1)] == 10) {
6264  	 	 	 --$lines;
6265  	 	 }
6266  	 	 $this->cell_padding = $prev_cell_padding;
6267  	 	 $this->lasth = $prev_lasth;
6268  	 	 return $lines;
6269  	 }
6270  
6271  	 /**
6272  	  * This method return the estimated height needed for printing a simple text string using the Multicell() method.
6273  	  * Generally, if you want to know the exact height for a block of content you can use the following alternative technique:
6274  	  * @pre
6275  	  *  // store current object
6276  	  *  $pdf->startTransaction();
6277  	  *  // store starting values
6278  	  *  $start_y = $pdf->GetY();
6279  	  *  $start_page = $pdf->getPage();
6280  	  *  // call your printing functions with your parameters
6281  	  *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6282  	  *  $pdf->MultiCell($w=0, $h=0, $txt, $border=1, $align='L', $fill=false, $ln=1, $x='', $y='', $reseth=true, $stretch=0, $ishtml=false, $autopadding=true, $maxh=0);
6283  	  *  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6284  	  *  // get the new Y
6285  	  *  $end_y = $pdf->GetY();
6286  	  *  $end_page = $pdf->getPage();
6287  	  *  // calculate height
6288  	  *  $height = 0;
6289  	  *  if ($end_page == $start_page) {
6290  	  *  	 $height = $end_y - $start_y;
6291  	  *  } else {
6292  	  *  	 for ($page=$start_page; $page <= $end_page; ++$page) {
6293  	  *  	 	 $this->setPage($page);
6294  	  *  	 	 if ($page == $start_page) {
6295  	  *  	 	 	 // first page
6296  	  *  	 	 	 $height += $this->h - $start_y - $this->bMargin;
6297  	  *  	 	 } elseif ($page == $end_page) {
6298  	  *  	 	 	 // last page
6299  	  *  	 	 	 $height += $end_y - $this->tMargin;
6300  	  *  	 	 } else {
6301  	  *  	 	 	 $height += $this->h - $this->tMargin - $this->bMargin;
6302  	  *  	 	 }
6303  	  *  	 }
6304  	  *  }
6305  	  *  // restore previous object
6306  	  *  $pdf = $pdf->rollbackTransaction();
6307  	  *
6308  	  * @param $w (float) Width of cells. If 0, they extend up to the right margin of the page.
6309  	  * @param $txt (string) String for calculating his height
6310  	  * @param $reseth (boolean) if true reset the last cell height (default false).
6311  	  * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width (default true).
6312  	  * @param $cellpadding (float) Internal cell padding, if empty uses default cell padding.
6313  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6314  	  * @return float Return the minimal height needed for multicell method for printing the $txt param.
6315  	  * @author Nicola Asuni, Alexander Escalona Fern\E1ndez
6316  	  * @public
6317  	  */
6318  	public function getStringHeight($w, $txt, $reseth=false, $autopadding=true, $cellpadding='', $border=0) {
6319  	 	 // adjust internal padding
6320  	 	 $prev_cell_padding = $this->cell_padding;
6321  	 	 $prev_lasth = $this->lasth;
6322  	 	 if (is_array($cellpadding)) {
6323  	 	 	 $this->cell_padding = $cellpadding;
6324  	 	 }
6325  	 	 $this->adjustCellPadding($border);
6326  	 	 $lines = $this->getNumLines($txt, $w, $reseth, $autopadding, $cellpadding, $border);
6327  	 	 $height = $this->getCellHeight(($lines * $this->FontSize), $autopadding);
6328  	 	 $this->cell_padding = $prev_cell_padding;
6329  	 	 $this->lasth = $prev_lasth;
6330  	 	 return $height;
6331  	 }
6332  
6333  	 /**
6334  	  * This method prints text from the current position.<br />
6335  	  * @param $h (float) Line height
6336  	  * @param $txt (string) String to print
6337  	  * @param $link (mixed) URL or identifier returned by AddLink()
6338  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
6339  	  * @param $align (string) 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>
6340  	  * @param $ln (boolean) if true set cursor at the bottom of the line, otherwise set cursor at the top of the line.
6341  	  * @param $stretch (int) font stretch mode: <ul><li>0 = disabled</li><li>1 = horizontal scaling only if text is larger than cell width</li><li>2 = forced horizontal scaling to fit cell width</li><li>3 = character spacing only if text is larger than cell width</li><li>4 = forced character spacing to fit cell width</li></ul> General font stretching and scaling values will be preserved when possible.
6342  	  * @param $firstline (boolean) if true prints only the first line and return the remaining string.
6343  	  * @param $firstblock (boolean) if true the string is the starting of a line.
6344  	  * @param $maxh (float) maximum height. It should be >= $h and less then remaining space to the bottom of the page, or 0 for disable this feature.
6345  	  * @param $wadj (float) first line width will be reduced by this amount (used in HTML mode).
6346  	  * @param $margin (array) margin array of the parent container
6347  	  * @return mixed Return the number of cells or the remaining string if $firstline = true.
6348  	  * @public
6349  	  * @since 1.5
6350  	  */
6351  	public function Write($h, $txt, $link='', $fill=false, $align='', $ln=false, $stretch=0, $firstline=false, $firstblock=false, $maxh=0, $wadj=0, $margin='') {
6352  	 	 // check page for no-write regions and adapt page margins if necessary
6353  	 	 list($this->x, $this->y) = $this->checkPageRegions($h, $this->x, $this->y);
6354  	 	 if (strlen($txt) == 0) {
6355  	 	 	 // fix empty text
6356  	 	 	 $txt = ' ';
6357  	 	 }
6358  	 	 if ($margin === '') {
6359  	 	 	 // set default margins
6360  	 	 	 $margin = $this->cell_margin;
6361  	 	 }
6362  	 	 // remove carriage returns
6363  	 	 $s = str_replace("\r", '', $txt);
6364  	 	 // check if string contains arabic text
6365  	 	 if (preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $s)) {
6366  	 	 	 $arabic = true;
6367  	 	 } else {
6368  	 	 	 $arabic = false;
6369  	 	 }
6370  	 	 // check if string contains RTL text
6371  	 	 if ($arabic OR ($this->tmprtl == 'R') OR preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $s)) {
6372  	 	 	 $rtlmode = true;
6373  	 	 } else {
6374  	 	 	 $rtlmode = false;
6375  	 	 }
6376  	 	 // get a char width
6377  	 	 $chrwidth = $this->GetCharWidth(46); // dot character
6378  	 	 // get array of unicode values
6379  	 	 $chars = TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont);
6380  	 	 // calculate maximum width for a single character on string
6381  	 	 $chrw = $this->GetArrStringWidth($chars, '', '', 0, true);
6382  	 	 array_walk($chrw, array($this, 'getRawCharWidth'));
6383  	 	 $maxchwidth = max($chrw);
6384  	 	 // get array of chars
6385  	 	 $uchars = TCPDF_FONTS::UTF8ArrayToUniArray($chars, $this->isunicode);
6386  	 	 // get the number of characters
6387  	 	 $nb = count($chars);
6388  	 	 // replacement for SHY character (minus symbol)
6389  	 	 $shy_replacement = 45;
6390  	 	 $shy_replacement_char = TCPDF_FONTS::unichr($shy_replacement, $this->isunicode);
6391  	 	 // widht for SHY replacement
6392  	 	 $shy_replacement_width = $this->GetCharWidth($shy_replacement);
6393  	 	 // page width
6394  	 	 $pw = $w = $this->w - $this->lMargin - $this->rMargin;
6395  	 	 // calculate remaining line width ($w)
6396  	 	 if ($this->rtl) {
6397  	 	 	 $w = $this->x - $this->lMargin;
6398  	 	 } else {
6399  	 	 	 $w = $this->w - $this->rMargin - $this->x;
6400  	 	 }
6401  	 	 // max column width
6402  	 	 $wmax = ($w - $wadj);
6403  	 	 if (!$firstline) {
6404  	 	 	 $wmax -= ($this->cell_padding['L'] + $this->cell_padding['R']);
6405  	 	 }
6406  	 	 if ((!$firstline) AND (($chrwidth > $wmax) OR ($maxchwidth > $wmax))) {
6407  	 	 	 // the maximum width character do not fit on column
6408  	 	 	 return '';
6409  	 	 }
6410  	 	 // minimum row height
6411  	 	 $row_height = max($h, $this->getCellHeight($this->FontSize));
6412  	 	 // max Y
6413  	 	 $maxy = $this->y + $maxh - max($row_height, $h);
6414  	 	 $start_page = $this->page;
6415  	 	 $i = 0; // character position
6416  	 	 $j = 0; // current starting position
6417  	 	 $sep = -1; // position of the last blank space
6418  	 	 $prevsep = $sep; // previous separator
6419  	 	 $shy = false; // true if the last blank is a soft hypen (SHY)
6420  	 	 $prevshy = $shy; // previous shy mode
6421  	 	 $l = 0; // current string length
6422  	 	 $nl = 0; //number of lines
6423  	 	 $linebreak = false;
6424  	 	 $pc = 0; // previous character
6425  	 	 // for each character
6426  	 	 while ($i < $nb) {
6427  	 	 	 if (($maxh > 0) AND ($this->y > $maxy) ) {
6428  	 	 	 	 break;
6429  	 	 	 }
6430  	 	 	 //Get the current character
6431  	 	 	 $c = $chars[$i];
6432  	 	 	 if ($c == 10) { // 10 = "\n" = new line
6433  	 	 	 	 //Explicit line break
6434  	 	 	 	 if ($align == 'J') {
6435  	 	 	 	 	 if ($this->rtl) {
6436  	 	 	 	 	 	 $talign = 'R';
6437  	 	 	 	 	 } else {
6438  	 	 	 	 	 	 $talign = 'L';
6439  	 	 	 	 	 }
6440  	 	 	 	 } else {
6441  	 	 	 	 	 $talign = $align;
6442  	 	 	 	 }
6443  	 	 	 	 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6444  	 	 	 	 if ($firstline) {
6445  	 	 	 	 	 $startx = $this->x;
6446  	 	 	 	 	 $tmparr = array_slice($chars, $j, ($i - $j));
6447  	 	 	 	 	 if ($rtlmode) {
6448  	 	 	 	 	 	 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6449  	 	 	 	 	 }
6450  	 	 	 	 	 $linew = $this->GetArrStringWidth($tmparr);
6451  	 	 	 	 	 unset($tmparr);
6452  	 	 	 	 	 if ($this->rtl) {
6453  	 	 	 	 	 	 $this->endlinex = $startx - $linew;
6454  	 	 	 	 	 } else {
6455  	 	 	 	 	 	 $this->endlinex = $startx + $linew;
6456  	 	 	 	 	 }
6457  	 	 	 	 	 $w = $linew;
6458  	 	 	 	 	 $tmpcellpadding = $this->cell_padding;
6459  	 	 	 	 	 if ($maxh == 0) {
6460  	 	 	 	 	 	 $this->SetCellPadding(0);
6461  	 	 	 	 	 }
6462  	 	 	 	 }
6463  	 	 	 	 if ($firstblock AND $this->isRTLTextDir()) {
6464  	 	 	 	 	 $tmpstr = $this->stringRightTrim($tmpstr);
6465  	 	 	 	 }
6466  	 	 	 	 // Skip newlines at the beginning of a page or column
6467  	 	 	 	 if (!empty($tmpstr) OR ($this->y < ($this->PageBreakTrigger - $row_height))) {
6468  	 	 	 	 	 $this->Cell($w, $h, $tmpstr, 0, 1, $talign, $fill, $link, $stretch);
6469  	 	 	 	 }
6470  	 	 	 	 unset($tmpstr);
6471  	 	 	 	 if ($firstline) {
6472  	 	 	 	 	 $this->cell_padding = $tmpcellpadding;
6473  	 	 	 	 	 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6474  	 	 	 	 }
6475  	 	 	 	 ++$nl;
6476  	 	 	 	 $j = $i + 1;
6477  	 	 	 	 $l = 0;
6478  	 	 	 	 $sep = -1;
6479  	 	 	 	 $prevsep = $sep;
6480  	 	 	 	 $shy = false;
6481  	 	 	 	 // account for margin changes
6482  	 	 	 	 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6483  	 	 	 	 	 $this->AcceptPageBreak();
6484  	 	 	 	 	 if ($this->rtl) {
6485  	 	 	 	 	 	 $this->x -= $margin['R'];
6486  	 	 	 	 	 } else {
6487  	 	 	 	 	 	 $this->x += $margin['L'];
6488  	 	 	 	 	 }
6489  	 	 	 	 	 $this->lMargin += $margin['L'];
6490  	 	 	 	 	 $this->rMargin += $margin['R'];
6491  	 	 	 	 }
6492  	 	 	 	 $w = $this->getRemainingWidth();
6493  	 	 	 	 $wmax = ($w - $this->cell_padding['L'] - $this->cell_padding['R']);
6494  	 	 	 } else {
6495  	 	 	 	 // 160 is the non-breaking space.
6496  	 	 	 	 // 173 is SHY (Soft Hypen).
6497  	 	 	 	 // \p{Z} or \p{Separator}: any kind of Unicode whitespace or invisible separator.
6498  	 	 	 	 // \p{Lo} or \p{Other_Letter}: a Unicode letter or ideograph that does not have lowercase and uppercase variants.
6499  	 	 	 	 // \p{Lo} is needed because Chinese characters are packed next to each other without spaces in between.
6500  	 	 	 	 if (($c != 160)
6501  	 	 	 	 	 AND (($c == 173)
6502  	 	 	 	 	 	 OR preg_match($this->re_spaces, TCPDF_FONTS::unichr($c, $this->isunicode))
6503  	 	 	 	 	 	 OR (($c == 45)
6504  	 	 	 	 	 	 	 AND ($i < ($nb - 1))
6505  	 	 	 	 	 	 	 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($pc, $this->isunicode))
6506  	 	 	 	 	 	 	 AND @preg_match('/[\p{L}]/'.$this->re_space['m'], TCPDF_FONTS::unichr($chars[($i + 1)], $this->isunicode))
6507  	 	 	 	 	 	 )
6508  	 	 	 	 	 )
6509  	 	 	 	 ) {
6510  	 	 	 	 	 // update last blank space position
6511  	 	 	 	 	 $prevsep = $sep;
6512  	 	 	 	 	 $sep = $i;
6513  	 	 	 	 	 // check if is a SHY
6514  	 	 	 	 	 if (($c == 173) OR ($c == 45)) {
6515  	 	 	 	 	 	 $prevshy = $shy;
6516  	 	 	 	 	 	 $shy = true;
6517  	 	 	 	 	 	 if ($pc == 45) {
6518  	 	 	 	 	 	 	 $tmp_shy_replacement_width = 0;
6519  	 	 	 	 	 	 	 $tmp_shy_replacement_char = '';
6520  	 	 	 	 	 	 } else {
6521  	 	 	 	 	 	 	 $tmp_shy_replacement_width = $shy_replacement_width;
6522  	 	 	 	 	 	 	 $tmp_shy_replacement_char = $shy_replacement_char;
6523  	 	 	 	 	 	 }
6524  	 	 	 	 	 } else {
6525  	 	 	 	 	 	 $shy = false;
6526  	 	 	 	 	 }
6527  	 	 	 	 }
6528  	 	 	 	 // update string length
6529  	 	 	 	 if ($this->isUnicodeFont() AND ($arabic)) {
6530  	 	 	 	 	 // with bidirectional algorithm some chars may be changed affecting the line length
6531  	 	 	 	 	 // *** very slow ***
6532  	 	 	 	 	 $l = $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(array_slice($chars, $j, ($i - $j)), '', $this->tmprtl, $this->isunicode, $this->CurrentFont));
6533  	 	 	 	 } else {
6534  	 	 	 	 	 $l += $this->GetCharWidth($c, ($i+1 < $nb));
6535  	 	 	 	 }
6536  	 	 	 	 if (($l > $wmax) OR (($c == 173) AND (($l + $tmp_shy_replacement_width) >= $wmax))) {
6537  	 	 	 	 	 if (($c == 173) AND (($l + $tmp_shy_replacement_width) > $wmax)) {
6538  	 	 	 	 	 	 $sep = $prevsep;
6539  	 	 	 	 	 	 $shy = $prevshy;
6540  	 	 	 	 	 }
6541  	 	 	 	 	 // we have reached the end of column
6542  	 	 	 	 	 if ($sep == -1) {
6543  	 	 	 	 	 	 // check if the line was already started
6544  	 	 	 	 	 	 if (($this->rtl AND ($this->x <= ($this->w - $this->rMargin - $this->cell_padding['R'] - $margin['R'] - $chrwidth)))
6545  	 	 	 	 	 	 	 OR ((!$this->rtl) AND ($this->x >= ($this->lMargin + $this->cell_padding['L'] + $margin['L'] + $chrwidth)))) {
6546  	 	 	 	 	 	 	 // print a void cell and go to next line
6547  	 	 	 	 	 	 	 $this->Cell($w, $h, '', 0, 1);
6548  	 	 	 	 	 	 	 $linebreak = true;
6549  	 	 	 	 	 	 	 if ($firstline) {
6550  	 	 	 	 	 	 	 	 return (TCPDF_FONTS::UniArrSubString($uchars, $j));
6551  	 	 	 	 	 	 	 }
6552  	 	 	 	 	 	 } else {
6553  	 	 	 	 	 	 	 // truncate the word because do not fit on column
6554  	 	 	 	 	 	 	 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6555  	 	 	 	 	 	 	 if ($firstline) {
6556  	 	 	 	 	 	 	 	 $startx = $this->x;
6557  	 	 	 	 	 	 	 	 $tmparr = array_slice($chars, $j, ($i - $j));
6558  	 	 	 	 	 	 	 	 if ($rtlmode) {
6559  	 	 	 	 	 	 	 	 	 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6560  	 	 	 	 	 	 	 	 }
6561  	 	 	 	 	 	 	 	 $linew = $this->GetArrStringWidth($tmparr);
6562  	 	 	 	 	 	 	 	 unset($tmparr);
6563  	 	 	 	 	 	 	 	 if ($this->rtl) {
6564  	 	 	 	 	 	 	 	 	 $this->endlinex = $startx - $linew;
6565  	 	 	 	 	 	 	 	 } else {
6566  	 	 	 	 	 	 	 	 	 $this->endlinex = $startx + $linew;
6567  	 	 	 	 	 	 	 	 }
6568  	 	 	 	 	 	 	 	 $w = $linew;
6569  	 	 	 	 	 	 	 	 $tmpcellpadding = $this->cell_padding;
6570  	 	 	 	 	 	 	 	 if ($maxh == 0) {
6571  	 	 	 	 	 	 	 	 	 $this->SetCellPadding(0);
6572  	 	 	 	 	 	 	 	 }
6573  	 	 	 	 	 	 	 }
6574  	 	 	 	 	 	 	 if ($firstblock AND $this->isRTLTextDir()) {
6575  	 	 	 	 	 	 	 	 $tmpstr = $this->stringRightTrim($tmpstr);
6576  	 	 	 	 	 	 	 }
6577  	 	 	 	 	 	 	 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6578  	 	 	 	 	 	 	 unset($tmpstr);
6579  	 	 	 	 	 	 	 if ($firstline) {
6580  	 	 	 	 	 	 	 	 $this->cell_padding = $tmpcellpadding;
6581  	 	 	 	 	 	 	 	 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6582  	 	 	 	 	 	 	 }
6583  	 	 	 	 	 	 	 $j = $i;
6584  	 	 	 	 	 	 	 --$i;
6585  	 	 	 	 	 	 }
6586  	 	 	 	 	 } else {
6587  	 	 	 	 	 	 // word wrapping
6588  	 	 	 	 	 	 if ($this->rtl AND (!$firstblock) AND ($sep < $i)) {
6589  	 	 	 	 	 	 	 $endspace = 1;
6590  	 	 	 	 	 	 } else {
6591  	 	 	 	 	 	 	 $endspace = 0;
6592  	 	 	 	 	 	 }
6593  	 	 	 	 	 	 // check the length of the next string
6594  	 	 	 	 	 	 $strrest = TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace));
6595  	 	 	 	 	 	 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $this->stringTrim($strrest));
6596  	 	 	 	 	 	 if (isset($nextstr[0]) AND ($this->GetStringWidth($nextstr[0]) > $pw)) {
6597  	 	 	 	 	 	 	 // truncate the word because do not fit on a full page width
6598  	 	 	 	 	 	 	 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $i);
6599  	 	 	 	 	 	 	 if ($firstline) {
6600  	 	 	 	 	 	 	 	 $startx = $this->x;
6601  	 	 	 	 	 	 	 	 $tmparr = array_slice($chars, $j, ($i - $j));
6602  	 	 	 	 	 	 	 	 if ($rtlmode) {
6603  	 	 	 	 	 	 	 	 	 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6604  	 	 	 	 	 	 	 	 }
6605  	 	 	 	 	 	 	 	 $linew = $this->GetArrStringWidth($tmparr);
6606  	 	 	 	 	 	 	 	 unset($tmparr);
6607  	 	 	 	 	 	 	 	 if ($this->rtl) {
6608  	 	 	 	 	 	 	 	 	 $this->endlinex = ($startx - $linew);
6609  	 	 	 	 	 	 	 	 } else {
6610  	 	 	 	 	 	 	 	 	 $this->endlinex = ($startx + $linew);
6611  	 	 	 	 	 	 	 	 }
6612  	 	 	 	 	 	 	 	 $w = $linew;
6613  	 	 	 	 	 	 	 	 $tmpcellpadding = $this->cell_padding;
6614  	 	 	 	 	 	 	 	 if ($maxh == 0) {
6615  	 	 	 	 	 	 	 	 	 $this->SetCellPadding(0);
6616  	 	 	 	 	 	 	 	 }
6617  	 	 	 	 	 	 	 }
6618  	 	 	 	 	 	 	 if ($firstblock AND $this->isRTLTextDir()) {
6619  	 	 	 	 	 	 	 	 $tmpstr = $this->stringRightTrim($tmpstr);
6620  	 	 	 	 	 	 	 }
6621  	 	 	 	 	 	 	 $this->Cell($w, $h, $tmpstr, 0, 1, $align, $fill, $link, $stretch);
6622  	 	 	 	 	 	 	 unset($tmpstr);
6623  	 	 	 	 	 	 	 if ($firstline) {
6624  	 	 	 	 	 	 	 	 $this->cell_padding = $tmpcellpadding;
6625  	 	 	 	 	 	 	 	 return (TCPDF_FONTS::UniArrSubString($uchars, $i));
6626  	 	 	 	 	 	 	 }
6627  	 	 	 	 	 	 	 $j = $i;
6628  	 	 	 	 	 	 	 --$i;
6629  	 	 	 	 	 	 } else {
6630  	 	 	 	 	 	 	 // word wrapping
6631  	 	 	 	 	 	 	 if ($shy) {
6632  	 	 	 	 	 	 	 	 // add hypen (minus symbol) at the end of the line
6633  	 	 	 	 	 	 	 	 $shy_width = $tmp_shy_replacement_width;
6634  	 	 	 	 	 	 	 	 if ($this->rtl) {
6635  	 	 	 	 	 	 	 	 	 $shy_char_left = $tmp_shy_replacement_char;
6636  	 	 	 	 	 	 	 	 	 $shy_char_right = '';
6637  	 	 	 	 	 	 	 	 } else {
6638  	 	 	 	 	 	 	 	 	 $shy_char_left = '';
6639  	 	 	 	 	 	 	 	 	 $shy_char_right = $tmp_shy_replacement_char;
6640  	 	 	 	 	 	 	 	 }
6641  	 	 	 	 	 	 	 } else {
6642  	 	 	 	 	 	 	 	 $shy_width = 0;
6643  	 	 	 	 	 	 	 	 $shy_char_left = '';
6644  	 	 	 	 	 	 	 	 $shy_char_right = '';
6645  	 	 	 	 	 	 	 }
6646  	 	 	 	 	 	 	 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, ($sep + $endspace));
6647  	 	 	 	 	 	 	 if ($firstline) {
6648  	 	 	 	 	 	 	 	 $startx = $this->x;
6649  	 	 	 	 	 	 	 	 $tmparr = array_slice($chars, $j, (($sep + $endspace) - $j));
6650  	 	 	 	 	 	 	 	 if ($rtlmode) {
6651  	 	 	 	 	 	 	 	 	 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6652  	 	 	 	 	 	 	 	 }
6653  	 	 	 	 	 	 	 	 $linew = $this->GetArrStringWidth($tmparr);
6654  	 	 	 	 	 	 	 	 unset($tmparr);
6655  	 	 	 	 	 	 	 	 if ($this->rtl) {
6656  	 	 	 	 	 	 	 	 	 $this->endlinex = $startx - $linew - $shy_width;
6657  	 	 	 	 	 	 	 	 } else {
6658  	 	 	 	 	 	 	 	 	 $this->endlinex = $startx + $linew + $shy_width;
6659  	 	 	 	 	 	 	 	 }
6660  	 	 	 	 	 	 	 	 $w = $linew;
6661  	 	 	 	 	 	 	 	 $tmpcellpadding = $this->cell_padding;
6662  	 	 	 	 	 	 	 	 if ($maxh == 0) {
6663  	 	 	 	 	 	 	 	 	 $this->SetCellPadding(0);
6664  	 	 	 	 	 	 	 	 }
6665  	 	 	 	 	 	 	 }
6666  	 	 	 	 	 	 	 // print the line
6667  	 	 	 	 	 	 	 if ($firstblock AND $this->isRTLTextDir()) {
6668  	 	 	 	 	 	 	 	 $tmpstr = $this->stringRightTrim($tmpstr);
6669  	 	 	 	 	 	 	 }
6670  	 	 	 	 	 	 	 $this->Cell($w, $h, $shy_char_left.$tmpstr.$shy_char_right, 0, 1, $align, $fill, $link, $stretch);
6671  	 	 	 	 	 	 	 unset($tmpstr);
6672  	 	 	 	 	 	 	 if ($firstline) {
6673  	 	 	 	 	 	 	 	 if ($chars[$sep] == 45) {
6674  	 	 	 	 	 	 	 	 	 $endspace += 1;
6675  	 	 	 	 	 	 	 	 }
6676  	 	 	 	 	 	 	 	 // return the remaining text
6677  	 	 	 	 	 	 	 	 $this->cell_padding = $tmpcellpadding;
6678  	 	 	 	 	 	 	 	 return (TCPDF_FONTS::UniArrSubString($uchars, ($sep + $endspace)));
6679  	 	 	 	 	 	 	 }
6680  	 	 	 	 	 	 	 $i = $sep;
6681  	 	 	 	 	 	 	 $sep = -1;
6682  	 	 	 	 	 	 	 $shy = false;
6683  	 	 	 	 	 	 	 $j = ($i + 1);
6684  	 	 	 	 	 	 }
6685  	 	 	 	 	 }
6686  	 	 	 	 	 // account for margin changes
6687  	 	 	 	 	 if ((($this->y + $this->lasth) > $this->PageBreakTrigger) AND ($this->inPageBody())) {
6688  	 	 	 	 	 	 $this->AcceptPageBreak();
6689  	 	 	 	 	 	 if ($this->rtl) {
6690  	 	 	 	 	 	 	 $this->x -= $margin['R'];
6691  	 	 	 	 	 	 } else {
6692  	 	 	 	 	 	 	 $this->x += $margin['L'];
6693  	 	 	 	 	 	 }
6694  	 	 	 	 	 	 $this->lMargin += $margin['L'];
6695  	 	 	 	 	 	 $this->rMargin += $margin['R'];
6696  	 	 	 	 	 }
6697  	 	 	 	 	 $w = $this->getRemainingWidth();
6698  	 	 	 	 	 $wmax = $w - $this->cell_padding['L'] - $this->cell_padding['R'];
6699  	 	 	 	 	 if ($linebreak) {
6700  	 	 	 	 	 	 $linebreak = false;
6701  	 	 	 	 	 } else {
6702  	 	 	 	 	 	 ++$nl;
6703  	 	 	 	 	 	 $l = 0;
6704  	 	 	 	 	 }
6705  	 	 	 	 }
6706  	 	 	 }
6707  	 	 	 // save last character
6708  	 	 	 $pc = $c;
6709  	 	 	 ++$i;
6710  	 	 } // end while i < nb
6711  	 	 // print last substring (if any)
6712  	 	 if ($l > 0) {
6713  	 	 	 switch ($align) {
6714  	 	 	 	 case 'J':
6715  	 	 	 	 case 'C': {
6716  	 	 	 	 	 $w = $w;
6717  	 	 	 	 	 break;
6718  	 	 	 	 }
6719  	 	 	 	 case 'L': {
6720  	 	 	 	 	 if ($this->rtl) {
6721  	 	 	 	 	 	 $w = $w;
6722  	 	 	 	 	 } else {
6723  	 	 	 	 	 	 $w = $l;
6724  	 	 	 	 	 }
6725  	 	 	 	 	 break;
6726  	 	 	 	 }
6727  	 	 	 	 case 'R': {
6728  	 	 	 	 	 if ($this->rtl) {
6729  	 	 	 	 	 	 $w = $l;
6730  	 	 	 	 	 } else {
6731  	 	 	 	 	 	 $w = $w;
6732  	 	 	 	 	 }
6733  	 	 	 	 	 break;
6734  	 	 	 	 }
6735  	 	 	 	 default: {
6736  	 	 	 	 	 $w = $l;
6737  	 	 	 	 	 break;
6738  	 	 	 	 }
6739  	 	 	 }
6740  	 	 	 $tmpstr = TCPDF_FONTS::UniArrSubString($uchars, $j, $nb);
6741  	 	 	 if ($firstline) {
6742  	 	 	 	 $startx = $this->x;
6743  	 	 	 	 $tmparr = array_slice($chars, $j, ($nb - $j));
6744  	 	 	 	 if ($rtlmode) {
6745  	 	 	 	 	 $tmparr = TCPDF_FONTS::utf8Bidi($tmparr, $tmpstr, $this->tmprtl, $this->isunicode, $this->CurrentFont);
6746  	 	 	 	 }
6747  	 	 	 	 $linew = $this->GetArrStringWidth($tmparr);
6748  	 	 	 	 unset($tmparr);
6749  	 	 	 	 if ($this->rtl) {
6750  	 	 	 	 	 $this->endlinex = $startx - $linew;
6751  	 	 	 	 } else {
6752  	 	 	 	 	 $this->endlinex = $startx + $linew;
6753  	 	 	 	 }
6754  	 	 	 	 $w = $linew;
6755  	 	 	 	 $tmpcellpadding = $this->cell_padding;
6756  	 	 	 	 if ($maxh == 0) {
6757  	 	 	 	 	 $this->SetCellPadding(0);
6758  	 	 	 	 }
6759  	 	 	 }
6760  	 	 	 if ($firstblock AND $this->isRTLTextDir()) {
6761  	 	 	 	 $tmpstr = $this->stringRightTrim($tmpstr);
6762  	 	 	 }
6763  	 	 	 $this->Cell($w, $h, $tmpstr, 0, $ln, $align, $fill, $link, $stretch);
6764  	 	 	 unset($tmpstr);
6765  	 	 	 if ($firstline) {
6766  	 	 	 	 $this->cell_padding = $tmpcellpadding;
6767  	 	 	 	 return (TCPDF_FONTS::UniArrSubString($uchars, $nb));
6768  	 	 	 }
6769  	 	 	 ++$nl;
6770  	 	 }
6771  	 	 if ($firstline) {
6772  	 	 	 return '';
6773  	 	 }
6774  	 	 return $nl;
6775  	 }
6776  
6777  	 /**
6778  	  * Returns the remaining width between the current position and margins.
6779  	  * @return int Return the remaining width
6780  	  * @protected
6781  	  */
6782  	protected function getRemainingWidth() {
6783  	 	 list($this->x, $this->y) = $this->checkPageRegions(0, $this->x, $this->y);
6784  	 	 if ($this->rtl) {
6785  	 	 	 return ($this->x - $this->lMargin);
6786  	 	 } else {
6787  	 	 	 return ($this->w - $this->rMargin - $this->x);
6788  	 	 }
6789  	 }
6790  
6791  	 /**
6792  	  * Set the block dimensions accounting for page breaks and page/column fitting
6793  	  * @param $w (float) width
6794  	  * @param $h (float) height
6795  	  * @param $x (float) X coordinate
6796  	  * @param $y (float) Y coodiante
6797  	  * @param $fitonpage (boolean) if true the block is resized to not exceed page dimensions.
6798  	  * @return array($w, $h, $x, $y)
6799  	  * @protected
6800  	  * @since 5.5.009 (2010-07-05)
6801  	  */
6802  	protected function fitBlock($w, $h, $x, $y, $fitonpage=false) {
6803  	 	 if ($w <= 0) {
6804  	 	 	 // set maximum width
6805  	 	 	 $w = ($this->w - $this->lMargin - $this->rMargin);
6806  	 	 	 if ($w <= 0) {
6807  	 	 	 	 $w = 1;
6808  	 	 	 }
6809  	 	 }
6810  	 	 if ($h <= 0) {
6811  	 	 	 // set maximum height
6812  	 	 	 $h = ($this->PageBreakTrigger - $this->tMargin);
6813  	 	 	 if ($h <= 0) {
6814  	 	 	 	 $h = 1;
6815  	 	 	 }
6816  	 	 }
6817  	 	 // resize the block to be vertically contained on a single page or single column
6818  	 	 if ($fitonpage OR $this->AutoPageBreak) {
6819  	 	 	 $ratio_wh = ($w / $h);
6820  	 	 	 if ($h > ($this->PageBreakTrigger - $this->tMargin)) {
6821  	 	 	 	 $h = $this->PageBreakTrigger - $this->tMargin;
6822  	 	 	 	 $w = ($h * $ratio_wh);
6823  	 	 	 }
6824  	 	 	 // resize the block to be horizontally contained on a single page or single column
6825  	 	 	 if ($fitonpage) {
6826  	 	 	 	 $maxw = ($this->w - $this->lMargin - $this->rMargin);
6827  	 	 	 	 if ($w > $maxw) {
6828  	 	 	 	 	 $w = $maxw;
6829  	 	 	 	 	 $h = ($w / $ratio_wh);
6830  	 	 	 	 }
6831  	 	 	 }
6832  	 	 }
6833  	 	 // Check whether we need a new page or new column first as this does not fit
6834  	 	 $prev_x = $this->x;
6835  	 	 $prev_y = $this->y;
6836  	 	 if ($this->checkPageBreak($h, $y) OR ($this->y < $prev_y)) {
6837  	 	 	 $y = $this->y;
6838  	 	 	 if ($this->rtl) {
6839  	 	 	 	 $x += ($prev_x - $this->x);
6840  	 	 	 } else {
6841  	 	 	 	 $x += ($this->x - $prev_x);
6842  	 	 	 }
6843  	 	 	 $this->newline = true;
6844  	 	 }
6845  	 	 // resize the block to be contained on the remaining available page or column space
6846  	 	 if ($fitonpage) {
6847  	 	 	 $ratio_wh = ($w / $h);
6848  	 	 	 if (($y + $h) > $this->PageBreakTrigger) {
6849  	 	 	 	 $h = $this->PageBreakTrigger - $y;
6850  	 	 	 	 $w = ($h * $ratio_wh);
6851  	 	 	 }
6852  	 	 	 if ((!$this->rtl) AND (($x + $w) > ($this->w - $this->rMargin))) {
6853  	 	 	 	 $w = $this->w - $this->rMargin - $x;
6854  	 	 	 	 $h = ($w / $ratio_wh);
6855  	 	 	 } elseif (($this->rtl) AND (($x - $w) < ($this->lMargin))) {
6856  	 	 	 	 $w = $x - $this->lMargin;
6857  	 	 	 	 $h = ($w / $ratio_wh);
6858  	 	 	 }
6859  	 	 }
6860  	 	 return array($w, $h, $x, $y);
6861  	 }
6862  
6863  	 /**
6864  	  * Puts an image in the page.
6865  	  * The upper-left corner must be given.
6866  	  * The dimensions can be specified in different ways:<ul>
6867  	  * <li>explicit width and height (expressed in user unit)</li>
6868  	  * <li>one explicit dimension, the other being calculated automatically in order to keep the original proportions</li>
6869  	  * <li>no explicit dimension, in which case the image is put at 72 dpi</li></ul>
6870  	  * 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;
6871  	  * The format can be specified explicitly or inferred from the file extension.<br />
6872  	  * It is possible to put a link on the image.<br />
6873  	  * Remark: if an image is used several times, only one copy will be embedded in the file.<br />
6874  	  * @param $file (string) Name of the file containing the image or a '@' character followed by the image data string. To link an image without embedding it on the document, set an asterisk character before the URL (i.e.: '*http://www.example.com/image.jpg').
6875  	  * @param $x (float) Abscissa of the upper-left corner (LTR) or upper-right corner (RTL).
6876  	  * @param $y (float) Ordinate of the upper-left corner (LTR) or upper-right corner (RTL).
6877  	  * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
6878  	  * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
6879  	  * @param $type (string) 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.
6880  	  * @param $link (mixed) URL or identifier returned by AddLink().
6881  	  * @param $align (string) 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>
6882  	  * @param $resize (mixed) 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).
6883  	  * @param $dpi (int) dot-per-inch resolution used on resize
6884  	  * @param $palign (string) 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>
6885  	  * @param $ismask (boolean) true if this image is a mask, false otherwise
6886  	  * @param $imgmask (mixed) image object returned by this function or false
6887  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
6888  	  * @param $fitbox (mixed) If not false scale image dimensions proportionally to fit within the ($w, $h) box. $fitbox can be true or a 2 characters string indicating the image alignment inside the box. The first character indicate the horizontal alignment (L = left, C = center, R = right) the second character indicate the vertical algnment (T = top, M = middle, B = bottom).
6889  	  * @param $hidden (boolean) If true do not display the image.
6890  	  * @param $fitonpage (boolean) If true the image is resized to not exceed page dimensions.
6891  	  * @param $alt (boolean) If true the image will be added as alternative and not directly printed (the ID of the image will be returned).
6892  	  * @param $altimgs (array) Array of alternate images IDs. Each alternative image must be an array with two values: an integer representing the image ID (the value returned by the Image method) and a boolean value to indicate if the image is the default for printing.
6893  	  * @return image information
6894  	  * @public
6895  	  * @since 1.1
6896  	  */
6897  	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, $alt=false, $altimgs=array()) {
6898  	 	 if ($this->state != 2) {
6899  	 	 	 return;
6900  	 	 }
6901  	 	 if (strcmp($x, '') === 0) {
6902  	 	 	 $x = $this->x;
6903  	 	 }
6904  	 	 if (strcmp($y, '') === 0) {
6905  	 	 	 $y = $this->y;
6906  	 	 }
6907  	 	 // check page for no-write regions and adapt page margins if necessary
6908  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
6909  	 	 $exurl = ''; // external streams
6910  	 	 $imsize = FALSE;
6911  
6912          // Make sure the file variable is not empty or null because accessing $file[0] later
6913          // results in error when running PHP 7.4
6914          if (empty($file)) {
6915              return false;
6916          }
6917  	 	 // check if we are passing an image as file or string
6918  	 	 if ($file[0] === '@') {
6919  	 	 	 // image from string
6920  	 	 	 $imgdata = substr($file, 1);
6921  	 	 } else { // image file
6922  	 	 	 if ($file[0] === '*') {
6923  	 	 	 	 // image as external stream
6924  	 	 	 	 $file = substr($file, 1);
6925  	 	 	 	 $exurl = $file;
6926  	 	 	 }
6927  	 	 	 // check if file exist and it is valid
6928  	 	 	 if (!@$this->fileExists($file)) {
6929  	 	 	 	 return false;
6930  	 	 	 }
6931              if (false !== $info = $this->getImageBuffer($file)) {
6932                  $imsize = array($info['w'], $info['h']);
6933              } elseif (($imsize = @getimagesize($file)) === FALSE && strpos($file, '__tcpdf_'.$this->file_id.'_img') === FALSE){
6934                  $imgdata = $this->getCachedFileContents($file);
6935              }
6936  	 	 }
6937  	 	 if (!empty($imgdata)) {
6938  	 	 	 // copy image to cache
6939  	 	 	 $original_file = $file;
6940  	 	 	 $file = TCPDF_STATIC::getObjFilename('img', $this->file_id);
6941  	 	 	 $fp = TCPDF_STATIC::fopenLocal($file, 'w');
6942  	 	 	 if (!$fp) {
6943  	 	 	 	 $this->Error('Unable to write file: '.$file);
6944  	 	 	 }
6945  	 	 	 fwrite($fp, $imgdata);
6946  	 	 	 fclose($fp);
6947  	 	 	 unset($imgdata);
6948  	 	 	 $imsize = @getimagesize($file);
6949  	 	 	 if ($imsize === FALSE) {
6950  	 	 	 	 unlink($file);
6951  	 	 	 	 $file = $original_file;
6952  	 	 	 }
6953  	 	 }
6954  	 	 if ($imsize === FALSE) {
6955  	 	 	 if (($w > 0) AND ($h > 0)) {
6956  	 	 	 	 // get measures from specified data
6957  	 	 	 	 $pw = $this->getHTMLUnitToUnits($w, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6958  	 	 	 	 $ph = $this->getHTMLUnitToUnits($h, 0, $this->pdfunit, true) * $this->imgscale * $this->k;
6959  	 	 	 	 $imsize = array($pw, $ph);
6960  	 	 	 } else {
6961  	 	 	 	 $this->Error('[Image] Unable to get the size of the image: '.$file);
6962  	 	 	 }
6963  	 	 }
6964  	 	 // file hash
6965  	 	 $filehash = md5($file);
6966  	 	 // get original image width and height in pixels
6967  	 	 list($pixw, $pixh) = $imsize;
6968  	 	 // calculate image width and height on document
6969  	 	 if (($w <= 0) AND ($h <= 0)) {
6970  	 	 	 // convert image size to document unit
6971  	 	 	 $w = $this->pixelsToUnits($pixw);
6972  	 	 	 $h = $this->pixelsToUnits($pixh);
6973  	 	 } elseif ($w <= 0) {
6974  	 	 	 $w = $h * $pixw / $pixh;
6975  	 	 } elseif ($h <= 0) {
6976  	 	 	 $h = $w * $pixh / $pixw;
6977  	 	 } elseif (($fitbox !== false) AND ($w > 0) AND ($h > 0)) {
6978  	 	 	 if (strlen($fitbox) !== 2) {
6979  	 	 	 	 // set default alignment
6980  	 	 	 	 $fitbox = '--';
6981  	 	 	 }
6982  	 	 	 // scale image dimensions proportionally to fit within the ($w, $h) box
6983  	 	 	 if ((($w * $pixh) / ($h * $pixw)) < 1) {
6984  	 	 	 	 // store current height
6985  	 	 	 	 $oldh = $h;
6986  	 	 	 	 // calculate new height
6987  	 	 	 	 $h = $w * $pixh / $pixw;
6988  	 	 	 	 // height difference
6989  	 	 	 	 $hdiff = ($oldh - $h);
6990  	 	 	 	 // vertical alignment
6991  	 	 	 	 switch (strtoupper($fitbox[1])) {
6992  	 	 	 	 	 case 'T': {
6993  	 	 	 	 	 	 break;
6994  	 	 	 	 	 }
6995  	 	 	 	 	 case 'M': {
6996  	 	 	 	 	 	 $y += ($hdiff / 2);
6997  	 	 	 	 	 	 break;
6998  	 	 	 	 	 }
6999  	 	 	 	 	 case 'B': {
7000  	 	 	 	 	 	 $y += $hdiff;
7001  	 	 	 	 	 	 break;
7002  	 	 	 	 	 }
7003  	 	 	 	 }
7004  	 	 	 } else {
7005  	 	 	 	 // store current width
7006  	 	 	 	 $oldw = $w;
7007  	 	 	 	 // calculate new width
7008  	 	 	 	 $w = $h * $pixw / $pixh;
7009  	 	 	 	 // width difference
7010  	 	 	 	 $wdiff = ($oldw - $w);
7011  	 	 	 	 // horizontal alignment
7012  	 	 	 	 switch (strtoupper($fitbox[0])) {
7013  	 	 	 	 	 case 'L': {
7014  	 	 	 	 	 	 if ($this->rtl) {
7015  	 	 	 	 	 	 	 $x -= $wdiff;
7016  	 	 	 	 	 	 }
7017  	 	 	 	 	 	 break;
7018  	 	 	 	 	 }
7019  	 	 	 	 	 case 'C': {
7020  	 	 	 	 	 	 if ($this->rtl) {
7021  	 	 	 	 	 	 	 $x -= ($wdiff / 2);
7022  	 	 	 	 	 	 } else {
7023  	 	 	 	 	 	 	 $x += ($wdiff / 2);
7024  	 	 	 	 	 	 }
7025  	 	 	 	 	 	 break;
7026  	 	 	 	 	 }
7027  	 	 	 	 	 case 'R': {
7028  	 	 	 	 	 	 if (!$this->rtl) {
7029  	 	 	 	 	 	 	 $x += $wdiff;
7030  	 	 	 	 	 	 }
7031  	 	 	 	 	 	 break;
7032  	 	 	 	 	 }
7033  	 	 	 	 }
7034  	 	 	 }
7035  	 	 }
7036  	 	 // fit the image on available space
7037  	 	 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
7038  	 	 // calculate new minimum dimensions in pixels
7039  	 	 $neww = round($w * $this->k * $dpi / $this->dpi);
7040  	 	 $newh = round($h * $this->k * $dpi / $this->dpi);
7041  	 	 // check if resize is necessary (resize is used only to reduce the image)
7042  	 	 $newsize = ($neww * $newh);
7043  	 	 $pixsize = ($pixw * $pixh);
7044  	 	 if (intval($resize) == 2) {
7045  	 	 	 $resize = true;
7046  	 	 } elseif ($newsize >= $pixsize) {
7047  	 	 	 $resize = false;
7048  	 	 }
7049  	 	 // check if image has been already added on document
7050  	 	 $newimage = true;
7051  	 	 if (in_array($file, $this->imagekeys)) {
7052  	 	 	 $newimage = false;
7053  	 	 	 // get existing image data
7054  	 	 	 $info = $this->getImageBuffer($file);
7055  	 	 	 if (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE) {
7056  	 	 	 	 // check if the newer image is larger
7057  	 	 	 	 $oldsize = ($info['w'] * $info['h']);
7058  	 	 	 	 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7059  	 	 	 	 	 $newimage = true;
7060  	 	 	 	 }
7061  	 	 	 }
7062  	 	 } elseif (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)) {
7063  	 	 	 // create temp image file (without alpha channel)
7064  	 	 	 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7065  	 	 	 // create temp alpha file
7066  	 	 	 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7067  	 	 	 // check for cached images
7068  	 	 	 if (in_array($tempfile_plain, $this->imagekeys)) {
7069  	 	 	 	 // get existing image data
7070  	 	 	 	 $info = $this->getImageBuffer($tempfile_plain);
7071  	 	 	 	 // check if the newer image is larger
7072  	 	 	 	 $oldsize = ($info['w'] * $info['h']);
7073  	 	 	 	 if ((($oldsize < $newsize) AND ($resize)) OR (($oldsize < $pixsize) AND (!$resize))) {
7074  	 	 	 	 	 $newimage = true;
7075  	 	 	 	 } else {
7076  	 	 	 	 	 $newimage = false;
7077  	 	 	 	 	 // embed mask image
7078  	 	 	 	 	 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7079  	 	 	 	 	 // embed image, masked with previously embedded mask
7080  	 	 	 	 	 return $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7081  	 	 	 	 }
7082  	 	 	 }
7083  	 	 }
7084  	 	 if ($newimage) {
7085  	 	 	 //First use of image, get info
7086  	 	 	 $type = strtolower($type);
7087  	 	 	 if ($type == '') {
7088  	 	 	 	 $type = TCPDF_IMAGES::getImageFileType($file, $imsize);
7089  	 	 	 } elseif ($type == 'jpg') {
7090  	 	 	 	 $type = 'jpeg';
7091  	 	 	 }
7092  	 	 	 $mqr = TCPDF_STATIC::get_mqr();
7093  	 	 	 TCPDF_STATIC::set_mqr(false);
7094  	 	 	 // Specific image handlers (defined on TCPDF_IMAGES CLASS)
7095  	 	 	 $mtd = '_parse'.$type;
7096  	 	 	 // GD image handler function
7097  	 	 	 $gdfunction = 'imagecreatefrom'.$type;
7098  	 	 	 $info = false;
7099  	 	 	 if ((method_exists('TCPDF_IMAGES', $mtd)) AND (!($resize AND (function_exists($gdfunction) OR extension_loaded('imagick'))))) {
7100  	 	 	 	 // TCPDF image functions
7101  	 	 	 	 $info = TCPDF_IMAGES::$mtd($file);
7102  	 	 	 	 if (($ismask === false) AND ($imgmask === false) AND (strpos($file, '__tcpdf_'.$this->file_id.'_imgmask_') === FALSE)
7103  	 	 	 	 	 AND (($info === 'pngalpha') OR (isset($info['trns']) AND !empty($info['trns'])))) {
7104  	 	 	 	 	 return $this->ImagePngAlpha($file, $x, $y, $pixw, $pixh, $w, $h, 'PNG', $link, $align, $resize, $dpi, $palign, $filehash);
7105  	 	 	 	 }
7106  	 	 	 }
7107  	 	 	 if (($info === false) AND function_exists($gdfunction)) {
7108  	 	 	 	 try {
7109  	 	 	 	 	 // GD library
7110  	 	 	 	 	 $img = $gdfunction($file);
7111  	 	 	 	 	 if ($img !== false) {
7112  	 	 	 	 	 	 if ($resize) {
7113  	 	 	 	 	 	 	 $imgr = imagecreatetruecolor($neww, $newh);
7114  	 	 	 	 	 	 	 if (($type == 'gif') OR ($type == 'png')) {
7115  	 	 	 	 	 	 	 	 $imgr = TCPDF_IMAGES::setGDImageTransparency($imgr, $img);
7116  	 	 	 	 	 	 	 }
7117  	 	 	 	 	 	 	 imagecopyresampled($imgr, $img, 0, 0, 0, 0, $neww, $newh, $pixw, $pixh);
7118  	 	 	 	 	 	 	 $img = $imgr;
7119  	 	 	 	 	 	 }
7120  	 	 	 	 	 	 if (($type == 'gif') OR ($type == 'png')) {
7121  	 	 	 	 	 	 	 $info = TCPDF_IMAGES::_toPNG($img, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7122  	 	 	 	 	 	 } else {
7123  	 	 	 	 	 	 	 $info = TCPDF_IMAGES::_toJPEG($img, $this->jpeg_quality, TCPDF_STATIC::getObjFilename('img', $this->file_id));
7124  	 	 	 	 	 	 }
7125  	 	 	 	 	 }
7126  	 	 	 	 } catch(Exception $e) {
7127  	 	 	 	 	 $info = false;
7128  	 	 	 	 }
7129  	 	 	 }
7130  	 	 	 if (($info === false) AND extension_loaded('imagick')) {
7131  	 	 	 	 try {
7132  	 	 	 	 	 // ImageMagick library
7133  	 	 	 	 	 $img = new Imagick();
7134  	 	 	 	 	 if ($type == 'svg') {
7135  	 	 	 	 	 	 if ($file[0] === '@') {
7136  	 	 	 	 	 	 	 // image from string
7137  	 	 	 	 	 	 	 $svgimg = substr($file, 1);
7138  	 	 	 	 	 	 } else {
7139  	 	 	 	 	 	 	 // get SVG file content
7140                              $svgimg = $this->getCachedFileContents($file);
7141  	 	 	 	 	 	 }
7142  	 	 	 	 	 	 if ($svgimg !== FALSE) {
7143  	 	 	 	 	 	 	 // get width and height
7144  	 	 	 	 	 	 	 $regs = array();
7145  	 	 	 	 	 	 	 if (preg_match('/<svg([^\>]*)>/si', $svgimg, $regs)) {
7146  	 	 	 	 	 	 	 	 $svgtag = $regs[1];
7147  	 	 	 	 	 	 	 	 $tmp = array();
7148  	 	 	 	 	 	 	 	 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7149  	 	 	 	 	 	 	 	 	 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7150  	 	 	 	 	 	 	 	 	 $owu = sprintf('%F', ($ow * $dpi / 72)).$this->pdfunit;
7151  	 	 	 	 	 	 	 	 	 $svgtag = preg_replace('/[\s]+width[\s]*=[\s]*"[^"]*"/si', ' width="'.$owu.'"', $svgtag, 1);
7152  	 	 	 	 	 	 	 	 } else {
7153  	 	 	 	 	 	 	 	 	 $ow = $w;
7154  	 	 	 	 	 	 	 	 }
7155  	 	 	 	 	 	 	 	 $tmp = array();
7156  	 	 	 	 	 	 	 	 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $svgtag, $tmp)) {
7157  	 	 	 	 	 	 	 	 	 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
7158  	 	 	 	 	 	 	 	 	 $ohu = sprintf('%F', ($oh * $dpi / 72)).$this->pdfunit;
7159  	 	 	 	 	 	 	 	 	 $svgtag = preg_replace('/[\s]+height[\s]*=[\s]*"[^"]*"/si', ' height="'.$ohu.'"', $svgtag, 1);
7160  	 	 	 	 	 	 	 	 } else {
7161  	 	 	 	 	 	 	 	 	 $oh = $h;
7162  	 	 	 	 	 	 	 	 }
7163  	 	 	 	 	 	 	 	 $tmp = array();
7164  	 	 	 	 	 	 	 	 if (!preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $svgtag, $tmp)) {
7165  	 	 	 	 	 	 	 	 	 $vbw = ($ow * $this->imgscale * $this->k);
7166  	 	 	 	 	 	 	 	 	 $vbh = ($oh * $this->imgscale * $this->k);
7167  	 	 	 	 	 	 	 	 	 $vbox = sprintf(' viewBox="0 0 %F %F" ', $vbw, $vbh);
7168  	 	 	 	 	 	 	 	 	 $svgtag = $vbox.$svgtag;
7169  	 	 	 	 	 	 	 	 }
7170  	 	 	 	 	 	 	 	 $svgimg = preg_replace('/<svg([^\>]*)>/si', '<svg'.$svgtag.'>', $svgimg, 1);
7171  	 	 	 	 	 	 	 }
7172  	 	 	 	 	 	 	 $img->readImageBlob($svgimg);
7173  	 	 	 	 	 	 }
7174  	 	 	 	 	 } else {
7175  	 	 	 	 	 	 $img->readImage($file);
7176  	 	 	 	 	 }
7177  	 	 	 	 	 if ($resize) {
7178  	 	 	 	 	 	 $img->resizeImage($neww, $newh, 10, 1, false);
7179  	 	 	 	 	 }
7180  	 	 	 	 	 $img->setCompressionQuality($this->jpeg_quality);
7181  	 	 	 	 	 $img->setImageFormat('jpeg');
7182  	 	 	 	 	 $tempname = TCPDF_STATIC::getObjFilename('img', $this->file_id);
7183  	 	 	 	 	 $img->writeImage($tempname);
7184  	 	 	 	 	 $info = TCPDF_IMAGES::_parsejpeg($tempname);
7185  	 	 	 	 	 unlink($tempname);
7186  	 	 	 	 	 $img->destroy();
7187  	 	 	 	 } catch(Exception $e) {
7188  	 	 	 	 	 $info = false;
7189  	 	 	 	 }
7190  	 	 	 }
7191  	 	 	 if ($info === false) {
7192  	 	 	 	 // unable to process image
7193  	 	 	 	 return;
7194  	 	 	 }
7195  	 	 	 TCPDF_STATIC::set_mqr($mqr);
7196  	 	 	 if ($ismask) {
7197  	 	 	 	 // force grayscale
7198  	 	 	 	 $info['cs'] = 'DeviceGray';
7199  	 	 	 }
7200  	 	 	 if ($imgmask !== false) {
7201  	 	 	 	 $info['masked'] = $imgmask;
7202  	 	 	 }
7203  	 	 	 if (!empty($exurl)) {
7204  	 	 	 	 $info['exurl'] = $exurl;
7205  	 	 	 }
7206  	 	 	 // array of alternative images
7207  	 	 	 $info['altimgs'] = $altimgs;
7208  	 	 	 // add image to document
7209  	 	 	 $info['i'] = $this->setImageBuffer($file, $info);
7210  	 	 }
7211  	 	 // set alignment
7212  	 	 $this->img_rb_x = $x + $w;
7213  	 	 $this->img_rb_y = $y + $h;
7214  
7215  	 	 // set alignment
7216  	 	 if ($palign == 'L') {
7217  	 	 	 $ximg = $this->lMargin;
7218  	 	 } elseif ($palign == 'C') {
7219  	 	 	 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
7220  	 	 } elseif ($palign == 'R') {
7221  	 	 	 $ximg = $this->w - $this->rMargin - $w;
7222  	 	 } else {
7223  	 	 	 $ximg = $x;
7224  	 	 }
7225  
7226  	 	 if ($ismask OR $hidden) {
7227  	 	 	 // image is not displayed
7228  	 	 	 return $info['i'];
7229  	 	 }
7230  	 	 $xkimg = $ximg * $this->k;
7231  	 	 if (!$alt) {
7232  	 	 	 // only non-alternative immages will be set
7233  	 	 	 $this->_out(sprintf('q %F 0 0 %F %F %F cm /I%u Do Q', ($w * $this->k), ($h * $this->k), $xkimg, (($this->h - ($y + $h)) * $this->k), $info['i']));
7234  	 	 }
7235  	 	 if (!empty($border)) {
7236  	 	 	 $bx = $this->x;
7237  	 	 	 $by = $this->y;
7238  	 	 	 $this->x = $ximg;
7239  	 	 	 if ($this->rtl) {
7240  	 	 	 	 $this->x += $w;
7241  	 	 	 }
7242  	 	 	 $this->y = $y;
7243  	 	 	 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
7244  	 	 	 $this->x = $bx;
7245  	 	 	 $this->y = $by;
7246  	 	 }
7247  	 	 if ($link) {
7248  	 	 	 $this->Link($ximg, $y, $w, $h, $link, 0);
7249  	 	 }
7250  	 	 // set pointer to align the next text/objects
7251  	 	 switch($align) {
7252  	 	 	 case 'T': {
7253  	 	 	 	 $this->y = $y;
7254  	 	 	 	 $this->x = $this->img_rb_x;
7255  	 	 	 	 break;
7256  	 	 	 }
7257  	 	 	 case 'M': {
7258  	 	 	 	 $this->y = $y + round($h/2);
7259  	 	 	 	 $this->x = $this->img_rb_x;
7260  	 	 	 	 break;
7261  	 	 	 }
7262  	 	 	 case 'B': {
7263  	 	 	 	 $this->y = $this->img_rb_y;
7264  	 	 	 	 $this->x = $this->img_rb_x;
7265  	 	 	 	 break;
7266  	 	 	 }
7267  	 	 	 case 'N': {
7268  	 	 	 	 $this->SetY($this->img_rb_y);
7269  	 	 	 	 break;
7270  	 	 	 }
7271  	 	 	 default:{
7272  	 	 	 	 break;
7273  	 	 	 }
7274  	 	 }
7275  	 	 $this->endlinex = $this->img_rb_x;
7276  	 	 if ($this->inxobj) {
7277  	 	 	 // we are inside an XObject template
7278  	 	 	 $this->xobjects[$this->xobjid]['images'][] = $info['i'];
7279  	 	 }
7280  	 	 return $info['i'];
7281  	 }
7282  
7283  	 /**
7284  	  * Extract info from a PNG image with alpha channel using the Imagick or GD library.
7285  	  * @param $file (string) Name of the file containing the image.
7286  	  * @param $x (float) Abscissa of the upper-left corner.
7287  	  * @param $y (float) Ordinate of the upper-left corner.
7288  	  * @param $wpx (float) Original width of the image in pixels.
7289  	  * @param $hpx (float) original height of the image in pixels.
7290  	  * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
7291  	  * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
7292  	  * @param $type (string) 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.
7293  	  * @param $link (mixed) URL or identifier returned by AddLink().
7294  	  * @param $align (string) 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>
7295  	  * @param $resize (boolean) If true resize (reduce) the image to fit $w and $h (requires GD library).
7296  	  * @param $dpi (int) dot-per-inch resolution used on resize
7297  	  * @param $palign (string) 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>
7298  	  * @param $filehash (string) File hash used to build unique file names.
7299  	  * @author Nicola Asuni
7300  	  * @protected
7301  	  * @since 4.3.007 (2008-12-04)
7302  	  * @see Image()
7303  	  */
7304  	protected function ImagePngAlpha($file, $x, $y, $wpx, $hpx, $w, $h, $type, $link, $align, $resize, $dpi, $palign, $filehash='') {
7305  	 	 // create temp images
7306  	 	 if (empty($filehash)) {
7307  	 	 	 $filehash = md5($file);
7308  	 	 }
7309  	 	 // create temp image file (without alpha channel)
7310  	 	 $tempfile_plain = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_plain_'.$filehash;
7311  	 	 // create temp alpha file
7312  	 	 $tempfile_alpha = K_PATH_CACHE.'__tcpdf_'.$this->file_id.'_imgmask_alpha_'.$filehash;
7313  	 	 $parsed = false;
7314  	 	 $parse_error = '';
7315  	 	 // ImageMagick extension
7316  	 	 if (($parsed === false) AND extension_loaded('imagick')) {
7317  	 	 	 try {
7318  	 	 	 	 // ImageMagick library
7319  	 	 	 	 $img = new Imagick();
7320  	 	 	 	 $img->readImage($file);
7321  	 	 	 	 // clone image object
7322  	 	 	 	 $imga = TCPDF_STATIC::objclone($img);
7323  	 	 	 	 // extract alpha channel
7324  	 	 	 	 if (method_exists($img, 'setImageAlphaChannel') AND defined('Imagick::ALPHACHANNEL_EXTRACT')) {
7325  	 	 	 	 	 $img->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
7326  	 	 	 	 } else {
7327  	 	 	 	 	 $img->separateImageChannel(8); // 8 = (imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE);
7328  	 	 	 	 	 $img->negateImage(true);
7329  	 	 	 	 }
7330  	 	 	 	 $img->setImageFormat('png');
7331  	 	 	 	 $img->writeImage($tempfile_alpha);
7332  	 	 	 	 // remove alpha channel
7333  	 	 	 	 if (method_exists($imga, 'setImageMatte')) {
7334  	 	 	 	 	 $imga->setImageMatte(false);
7335  	 	 	 	 } else {
7336  	 	 	 	 	 $imga->separateImageChannel(39); // 39 = (imagick::CHANNEL_ALL & ~(imagick::CHANNEL_ALPHA | imagick::CHANNEL_OPACITY | imagick::CHANNEL_MATTE));
7337  	 	 	 	 }
7338  	 	 	 	 $imga->setImageFormat('png');
7339  	 	 	 	 $imga->writeImage($tempfile_plain);
7340  	 	 	 	 $parsed = true;
7341  	 	 	 } catch (Exception $e) {
7342  	 	 	 	 // Imagemagick fails, try with GD
7343  	 	 	 	 $parse_error = 'Imagick library error: '.$e->getMessage();
7344  	 	 	 }
7345  	 	 }
7346  	 	 // GD extension
7347  	 	 if (($parsed === false) AND function_exists('imagecreatefrompng')) {
7348  	 	 	 try {
7349  	 	 	 	 // generate images
7350  	 	 	 	 $img = imagecreatefrompng($file);
7351  	 	 	 	 $imgalpha = imagecreate($wpx, $hpx);
7352  	 	 	 	 // generate gray scale palette (0 -> 255)
7353  	 	 	 	 for ($c = 0; $c < 256; ++$c) {
7354  	 	 	 	 	 ImageColorAllocate($imgalpha, $c, $c, $c);
7355  	 	 	 	 }
7356  	 	 	 	 // extract alpha channel
7357  	 	 	 	 for ($xpx = 0; $xpx < $wpx; ++$xpx) {
7358  	 	 	 	 	 for ($ypx = 0; $ypx < $hpx; ++$ypx) {
7359  	 	 	 	 	 	 $color = imagecolorat($img, $xpx, $ypx);
7360  	 	 	 	 	 	 // get and correct gamma color
7361  	 	 	 	 	 	 $alpha = $this->getGDgamma($img, $color);
7362  	 	 	 	 	 	 imagesetpixel($imgalpha, $xpx, $ypx, $alpha);
7363  	 	 	 	 	 }
7364  	 	 	 	 }
7365  	 	 	 	 imagepng($imgalpha, $tempfile_alpha);
7366  	 	 	 	 imagedestroy($imgalpha);
7367  	 	 	 	 // extract image without alpha channel
7368  	 	 	 	 $imgplain = imagecreatetruecolor($wpx, $hpx);
7369  	 	 	 	 imagecopy($imgplain, $img, 0, 0, 0, 0, $wpx, $hpx);
7370  	 	 	 	 imagepng($imgplain, $tempfile_plain);
7371  	 	 	 	 imagedestroy($imgplain);
7372  	 	 	 	 $parsed = true;
7373  	 	 	 } catch (Exception $e) {
7374  	 	 	 	 // GD fails
7375  	 	 	 	 $parse_error = 'GD library error: '.$e->getMessage();
7376  	 	 	 }
7377  	 	 }
7378  	 	 if ($parsed === false) {
7379  	 	 	 if (empty($parse_error)) {
7380  	 	 	 	 $this->Error('TCPDF requires the Imagick or GD extension to handle PNG images with alpha channel.');
7381  	 	 	 } else {
7382  	 	 	 	 $this->Error($parse_error);
7383  	 	 	 }
7384  	 	 }
7385  	 	 // embed mask image
7386  	 	 $imgmask = $this->Image($tempfile_alpha, $x, $y, $w, $h, 'PNG', '', '', $resize, $dpi, '', true, false);
7387  	 	 // embed image, masked with previously embedded mask
7388  	 	 $this->Image($tempfile_plain, $x, $y, $w, $h, $type, $link, $align, $resize, $dpi, $palign, false, $imgmask);
7389  	 }
7390  
7391  	 /**
7392  	  * Get the GD-corrected PNG gamma value from alpha color
7393  	  * @param $img (int) GD image Resource ID.
7394  	  * @param $c (int) alpha color
7395  	  * @protected
7396  	  * @since 4.3.007 (2008-12-04)
7397  	  */
7398  	protected function getGDgamma($img, $c) {
7399  	 	 if (!isset($this->gdgammacache['#'.$c])) {
7400  	 	 	 $colors = imagecolorsforindex($img, $c);
7401  	 	 	 // GD alpha is only 7 bit (0 -> 127)
7402  	 	 	 $this->gdgammacache['#'.$c] = (((127 - $colors['alpha']) / 127) * 255);
7403  	 	 	 // correct gamma
7404  	 	 	 $this->gdgammacache['#'.$c] = (pow(($this->gdgammacache['#'.$c] / 255), 2.2) * 255);
7405  	 	 	 // store the latest values on cache to improve performances
7406  	 	 	 if (count($this->gdgammacache) > 8) {
7407  	 	 	 	 // remove one element from the cache array
7408  	 	 	 	 array_shift($this->gdgammacache);
7409  	 	 	 }
7410  	 	 }
7411  	 	 return $this->gdgammacache['#'.$c];
7412  	 }
7413  
7414  	 /**
7415  	  * Performs a line break.
7416  	  * The current abscissa goes back to the left margin and the ordinate increases by the amount passed in parameter.
7417  	  * @param $h (float) The height of the break. By default, the value equals the height of the last printed cell.
7418  	  * @param $cell (boolean) if true add the current left (or right o for RTL) padding to the X coordinate
7419  	  * @public
7420  	  * @since 1.0
7421  	  * @see Cell()
7422  	  */
7423  	 public function Ln($h='', $cell=false) {
7424  	 	 if (($this->num_columns > 1) 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'])) {
7425  	 	 	 // revove vertical space from the top of the column
7426  	 	 	 return;
7427  	 	 }
7428  	 	 if ($cell) {
7429  	 	 	 if ($this->rtl) {
7430  	 	 	 	 $cellpadding = $this->cell_padding['R'];
7431  	 	 	 } else {
7432  	 	 	 	 $cellpadding = $this->cell_padding['L'];
7433  	 	 	 }
7434  	 	 } else {
7435  	 	 	 $cellpadding = 0;
7436  	 	 }
7437  	 	 if ($this->rtl) {
7438  	 	 	 $this->x = $this->w - $this->rMargin - $cellpadding;
7439  	 	 } else {
7440  	 	 	 $this->x = $this->lMargin + $cellpadding;
7441  	 	 }
7442  	 	 if (is_string($h)) {
7443  	 	 	 $h = $this->lasth;
7444  	 	 }
7445  	 	 $this->y += $h;
7446  	 	 $this->newline = true;
7447  	 }
7448  
7449  	 /**
7450  	  * Returns the relative X value of current position.
7451  	  * The value is relative to the left border for LTR languages and to the right border for RTL languages.
7452  	  * @return float
7453  	  * @public
7454  	  * @since 1.2
7455  	  * @see SetX(), GetY(), SetY()
7456  	  */
7457  	public function GetX() {
7458  	 	 //Get x position
7459  	 	 if ($this->rtl) {
7460  	 	 	 return ($this->w - $this->x);
7461  	 	 } else {
7462  	 	 	 return $this->x;
7463  	 	 }
7464  	 }
7465  
7466  	 /**
7467  	  * Returns the absolute X value of current position.
7468  	  * @return float
7469  	  * @public
7470  	  * @since 1.2
7471  	  * @see SetX(), GetY(), SetY()
7472  	  */
7473  	public function GetAbsX() {
7474  	 	 return $this->x;
7475  	 }
7476  
7477  	 /**
7478  	  * Returns the ordinate of the current position.
7479  	  * @return float
7480  	  * @public
7481  	  * @since 1.0
7482  	  * @see SetY(), GetX(), SetX()
7483  	  */
7484  	public function GetY() {
7485  	 	 return $this->y;
7486  	 }
7487  
7488  	 /**
7489  	  * Defines the abscissa of the current position.
7490  	  * If the passed value is negative, it is relative to the right of the page (or left if language is RTL).
7491  	  * @param $x (float) The value of the abscissa in user units.
7492  	  * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7493  	  * @public
7494  	  * @since 1.2
7495  	  * @see GetX(), GetY(), SetY(), SetXY()
7496  	  */
7497  	public function SetX($x, $rtloff=false) {
7498  	 	 $x = floatval($x);
7499  	 	 if (!$rtloff AND $this->rtl) {
7500  	 	 	 if ($x >= 0) {
7501  	 	 	 	 $this->x = $this->w - $x;
7502  	 	 	 } else {
7503  	 	 	 	 $this->x = abs($x);
7504  	 	 	 }
7505  	 	 } else {
7506  	 	 	 if ($x >= 0) {
7507  	 	 	 	 $this->x = $x;
7508  	 	 	 } else {
7509  	 	 	 	 $this->x = $this->w + $x;
7510  	 	 	 }
7511  	 	 }
7512  	 	 if ($this->x < 0) {
7513  	 	 	 $this->x = 0;
7514  	 	 }
7515  	 	 if ($this->x > $this->w) {
7516  	 	 	 $this->x = $this->w;
7517  	 	 }
7518  	 }
7519  
7520  	 /**
7521  	  * Moves the current abscissa back to the left margin and sets the ordinate.
7522  	  * If the passed value is negative, it is relative to the bottom of the page.
7523  	  * @param $y (float) The value of the ordinate in user units.
7524  	  * @param $resetx (bool) if true (default) reset the X position.
7525  	  * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7526  	  * @public
7527  	  * @since 1.0
7528  	  * @see GetX(), GetY(), SetY(), SetXY()
7529  	  */
7530  	public function SetY($y, $resetx=true, $rtloff=false) {
7531  	 	 $y = floatval($y);
7532  	 	 if ($resetx) {
7533  	 	 	 //reset x
7534  	 	 	 if (!$rtloff AND $this->rtl) {
7535  	 	 	 	 $this->x = $this->w - $this->rMargin;
7536  	 	 	 } else {
7537  	 	 	 	 $this->x = $this->lMargin;
7538  	 	 	 }
7539  	 	 }
7540  	 	 if ($y >= 0) {
7541  	 	 	 $this->y = $y;
7542  	 	 } else {
7543  	 	 	 $this->y = $this->h + $y;
7544  	 	 }
7545  	 	 if ($this->y < 0) {
7546  	 	 	 $this->y = 0;
7547  	 	 }
7548  	 	 if ($this->y > $this->h) {
7549  	 	 	 $this->y = $this->h;
7550  	 	 }
7551  	 }
7552  
7553  	 /**
7554  	  * Defines the abscissa and ordinate of the current position.
7555  	  * If the passed values are negative, they are relative respectively to the right and bottom of the page.
7556  	  * @param $x (float) The value of the abscissa.
7557  	  * @param $y (float) The value of the ordinate.
7558  	  * @param $rtloff (boolean) if true always uses the page top-left corner as origin of axis.
7559  	  * @public
7560  	  * @since 1.2
7561  	  * @see SetX(), SetY()
7562  	  */
7563  	public function SetXY($x, $y, $rtloff=false) {
7564  	 	 $this->SetY($y, false, $rtloff);
7565  	 	 $this->SetX($x, $rtloff);
7566  	 }
7567  
7568  	 /**
7569  	  * Set the absolute X coordinate of the current pointer.
7570  	  * @param $x (float) The value of the abscissa in user units.
7571  	  * @public
7572  	  * @since 5.9.186 (2012-09-13)
7573  	  * @see setAbsX(), setAbsY(), SetAbsXY()
7574  	  */
7575  	public function SetAbsX($x) {
7576  	 	 $this->x = floatval($x);
7577  	 }
7578  
7579  	 /**
7580  	  * Set the absolute Y coordinate of the current pointer.
7581  	  * @param $y (float) (float) The value of the ordinate in user units.
7582  	  * @public
7583  	  * @since 5.9.186 (2012-09-13)
7584  	  * @see setAbsX(), setAbsY(), SetAbsXY()
7585  	  */
7586  	public function SetAbsY($y) {
7587  	 	 $this->y = floatval($y);
7588  	 }
7589  
7590  	 /**
7591  	  * Set the absolute X and Y coordinates of the current pointer.
7592  	  * @param $x (float) The value of the abscissa in user units.
7593  	  * @param $y (float) (float) The value of the ordinate in user units.
7594  	  * @public
7595  	  * @since 5.9.186 (2012-09-13)
7596  	  * @see setAbsX(), setAbsY(), SetAbsXY()
7597  	  */
7598  	public function SetAbsXY($x, $y) {
7599  	 	 $this->SetAbsX($x);
7600  	 	 $this->SetAbsY($y);
7601  	 }
7602  
7603  	 /**
7604  	  * Send the document to a given destination: string, local file or browser.
7605  	  * In the last case, the plug-in may be used (if present) or a download ("Save as" dialog box) may be forced.<br />
7606  	  * The method first calls Close() if necessary to terminate the document.
7607  	  * @param $name (string) The name of the file when saved. Note that special characters are removed and blanks characters are replaced with the underscore character.
7608  	  * @param $dest (string) 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><li>E: return the document as base64 mime multi-part email attachment (RFC 2045)</li></ul>
7609  	  * @return string
7610  	  * @public
7611  	  * @since 1.0
7612  	  * @see Close()
7613  	  */
7614  	public function Output($name='doc.pdf', $dest='I') {
7615  	 	 //Output PDF to some destination
7616  	 	 //Finish document if necessary
7617  	 	 if ($this->state < 3) {
7618  	 	 	 $this->Close();
7619  	 	 }
7620  	 	 //Normalize parameters
7621  	 	 if (is_bool($dest)) {
7622  	 	 	 $dest = $dest ? 'D' : 'F';
7623  	 	 }
7624  	 	 $dest = strtoupper($dest);
7625  	 	 if ($dest[0] != 'F') {
7626  	 	 	 $name = preg_replace('/[\s]+/', '_', $name);
7627  	 	 	 $name = preg_replace('/[^a-zA-Z0-9_\.-]/', '', $name);
7628  	 	 }
7629  	 	 if ($this->sign) {
7630  	 	 	 // *** apply digital signature to the document ***
7631  	 	 	 // get the document content
7632  	 	 	 $pdfdoc = $this->getBuffer();
7633  	 	 	 // remove last newline
7634  	 	 	 $pdfdoc = substr($pdfdoc, 0, -1);
7635  	 	 	 // remove filler space
7636  	 	 	 $byterange_string_len = strlen(TCPDF_STATIC::$byterange_string);
7637  	 	 	 // define the ByteRange
7638  	 	 	 $byte_range = array();
7639  	 	 	 $byte_range[0] = 0;
7640  	 	 	 $byte_range[1] = strpos($pdfdoc, TCPDF_STATIC::$byterange_string) + $byterange_string_len + 10;
7641  	 	 	 $byte_range[2] = $byte_range[1] + $this->signature_max_length + 2;
7642  	 	 	 $byte_range[3] = strlen($pdfdoc) - $byte_range[2];
7643  	 	 	 $pdfdoc = substr($pdfdoc, 0, $byte_range[1]).substr($pdfdoc, $byte_range[2]);
7644  	 	 	 // replace the ByteRange
7645  	 	 	 $byterange = sprintf('/ByteRange[0 %u %u %u]', $byte_range[1], $byte_range[2], $byte_range[3]);
7646  	 	 	 $byterange .= str_repeat(' ', ($byterange_string_len - strlen($byterange)));
7647  	 	 	 $pdfdoc = str_replace(TCPDF_STATIC::$byterange_string, $byterange, $pdfdoc);
7648  	 	 	 // write the document to a temporary folder
7649  	 	 	 $tempdoc = TCPDF_STATIC::getObjFilename('doc', $this->file_id);
7650  	 	 	 $f = TCPDF_STATIC::fopenLocal($tempdoc, 'wb');
7651  	 	 	 if (!$f) {
7652  	 	 	 	 $this->Error('Unable to create temporary file: '.$tempdoc);
7653  	 	 	 }
7654  	 	 	 $pdfdoc_length = strlen($pdfdoc);
7655  	 	 	 fwrite($f, $pdfdoc, $pdfdoc_length);
7656  	 	 	 fclose($f);
7657  	 	 	 // get digital signature via openssl library
7658  	 	 	 $tempsign = TCPDF_STATIC::getObjFilename('sig', $this->file_id);
7659  	 	 	 if (empty($this->signature_data['extracerts'])) {
7660  	 	 	 	 openssl_pkcs7_sign($tempdoc, $tempsign, $this->signature_data['signcert'], array($this->signature_data['privkey'], $this->signature_data['password']), array(), PKCS7_BINARY | PKCS7_DETACHED);
7661  	 	 	 } else {
7662  	 	 	 	 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']);
7663  	 	 	 }
7664  	 	 	 // read signature
7665  	 	 	 $signature = file_get_contents($tempsign);
7666  	 	 	 // extract signature
7667  	 	 	 $signature = substr($signature, $pdfdoc_length);
7668  	 	 	 $signature = substr($signature, (strpos($signature, "%%EOF\n\n------") + 13));
7669  	 	 	 $tmparr = explode("\n\n", $signature);
7670  	 	 	 $signature = $tmparr[1];
7671  	 	 	 // decode signature
7672  	 	 	 $signature = base64_decode(trim($signature));
7673  	 	 	 // add TSA timestamp to signature
7674  	 	 	 $signature = $this->applyTSA($signature);
7675  	 	 	 // convert signature to hex
7676  	 	 	 $signature = current(unpack('H*', $signature));
7677  	 	 	 $signature = str_pad($signature, $this->signature_max_length, '0');
7678  	 	 	 // Add signature to the document
7679  	 	 	 $this->buffer = substr($pdfdoc, 0, $byte_range[1]).'<'.$signature.'>'.substr($pdfdoc, $byte_range[1]);
7680  	 	 	 $this->bufferlen = strlen($this->buffer);
7681  	 	 }
7682  	 	 switch($dest) {
7683  	 	 	 case 'I': {
7684  	 	 	 	 // Send PDF to the standard output
7685  	 	 	 	 if (ob_get_contents()) {
7686  	 	 	 	 	 $this->Error('Some data has already been output, can\'t send PDF file');
7687  	 	 	 	 }
7688  	 	 	 	 if (php_sapi_name() != 'cli') {
7689  	 	 	 	 	 // send output to a browser
7690  	 	 	 	 	 header('Content-Type: application/pdf');
7691  	 	 	 	 	 if (headers_sent()) {
7692  	 	 	 	 	 	 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7693  	 	 	 	 	 }
7694  	 	 	 	 	 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7695  	 	 	 	 	 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7696  	 	 	 	 	 header('Pragma: public');
7697  	 	 	 	 	 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7698  	 	 	 	 	 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7699  	 	 	 	 	 header('Content-Disposition: inline; filename="'.basename($name).'"');
7700  	 	 	 	 	 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7701  	 	 	 	 } else {
7702  	 	 	 	 	 echo $this->getBuffer();
7703  	 	 	 	 }
7704  	 	 	 	 break;
7705  	 	 	 }
7706  	 	 	 case 'D': {
7707  	 	 	 	 // download PDF as file
7708  	 	 	 	 if (ob_get_contents()) {
7709  	 	 	 	 	 $this->Error('Some data has already been output, can\'t send PDF file');
7710  	 	 	 	 }
7711  	 	 	 	 header('Content-Description: File Transfer');
7712  	 	 	 	 if (headers_sent()) {
7713  	 	 	 	 	 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7714  	 	 	 	 }
7715  	 	 	 	 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7716  	 	 	 	 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7717  	 	 	 	 header('Pragma: public');
7718  	 	 	 	 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7719  	 	 	 	 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7720  	 	 	 	 // force download dialog
7721  	 	 	 	 if (strpos(php_sapi_name(), 'cgi') === false) {
7722  	 	 	 	 	 header('Content-Type: application/force-download');
7723  	 	 	 	 	 header('Content-Type: application/octet-stream', false);
7724  	 	 	 	 	 header('Content-Type: application/download', false);
7725  	 	 	 	 	 header('Content-Type: application/pdf', false);
7726  	 	 	 	 } else {
7727  	 	 	 	 	 header('Content-Type: application/pdf');
7728  	 	 	 	 }
7729  	 	 	 	 // use the Content-Disposition header to supply a recommended filename
7730  	 	 	 	 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7731  	 	 	 	 header('Content-Transfer-Encoding: binary');
7732  	 	 	 	 TCPDF_STATIC::sendOutputData($this->getBuffer(), $this->bufferlen);
7733  	 	 	 	 break;
7734  	 	 	 }
7735  	 	 	 case 'F':
7736  	 	 	 case 'FI':
7737  	 	 	 case 'FD': {
7738  	 	 	 	 // save PDF to a local file
7739  	 	 	 	 $f = TCPDF_STATIC::fopenLocal($name, 'wb');
7740  	 	 	 	 if (!$f) {
7741  	 	 	 	 	 $this->Error('Unable to create output file: '.$name);
7742  	 	 	 	 }
7743  	 	 	 	 fwrite($f, $this->getBuffer(), $this->bufferlen);
7744  	 	 	 	 fclose($f);
7745  	 	 	 	 if ($dest == 'FI') {
7746  	 	 	 	 	 // send headers to browser
7747  	 	 	 	 	 header('Content-Type: application/pdf');
7748  	 	 	 	 	 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7749  	 	 	 	 	 //header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
7750  	 	 	 	 	 header('Pragma: public');
7751  	 	 	 	 	 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7752  	 	 	 	 	 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7753  	 	 	 	 	 header('Content-Disposition: inline; filename="'.basename($name).'"');
7754  	 	 	 	 	 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7755  	 	 	 	 } elseif ($dest == 'FD') {
7756  	 	 	 	 	 // send headers to browser
7757  	 	 	 	 	 if (ob_get_contents()) {
7758  	 	 	 	 	 	 $this->Error('Some data has already been output, can\'t send PDF file');
7759  	 	 	 	 	 }
7760  	 	 	 	 	 header('Content-Description: File Transfer');
7761  	 	 	 	 	 if (headers_sent()) {
7762  	 	 	 	 	 	 $this->Error('Some data has already been output to browser, can\'t send PDF file');
7763  	 	 	 	 	 }
7764  	 	 	 	 	 header('Cache-Control: private, must-revalidate, post-check=0, pre-check=0, max-age=1');
7765  	 	 	 	 	 header('Pragma: public');
7766  	 	 	 	 	 header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
7767  	 	 	 	 	 header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
7768  	 	 	 	 	 // force download dialog
7769  	 	 	 	 	 if (strpos(php_sapi_name(), 'cgi') === false) {
7770  	 	 	 	 	 	 header('Content-Type: application/force-download');
7771  	 	 	 	 	 	 header('Content-Type: application/octet-stream', false);
7772  	 	 	 	 	 	 header('Content-Type: application/download', false);
7773  	 	 	 	 	 	 header('Content-Type: application/pdf', false);
7774  	 	 	 	 	 } else {
7775  	 	 	 	 	 	 header('Content-Type: application/pdf');
7776  	 	 	 	 	 }
7777  	 	 	 	 	 // use the Content-Disposition header to supply a recommended filename
7778  	 	 	 	 	 header('Content-Disposition: attachment; filename="'.basename($name).'"');
7779  	 	 	 	 	 header('Content-Transfer-Encoding: binary');
7780  	 	 	 	 	 TCPDF_STATIC::sendOutputData(file_get_contents($name), filesize($name));
7781  	 	 	 	 }
7782  	 	 	 	 break;
7783  	 	 	 }
7784  	 	 	 case 'E': {
7785  	 	 	 	 // return PDF as base64 mime multi-part email attachment (RFC 2045)
7786  	 	 	 	 $retval = 'Content-Type: application/pdf;'."\r\n";
7787  	 	 	 	 $retval .= ' name="'.$name.'"'."\r\n";
7788  	 	 	 	 $retval .= 'Content-Transfer-Encoding: base64'."\r\n";
7789  	 	 	 	 $retval .= 'Content-Disposition: attachment;'."\r\n";
7790  	 	 	 	 $retval .= ' filename="'.$name.'"'."\r\n\r\n";
7791  	 	 	 	 $retval .= chunk_split(base64_encode($this->getBuffer()), 76, "\r\n");
7792  	 	 	 	 return $retval;
7793  	 	 	 }
7794  	 	 	 case 'S': {
7795  	 	 	 	 // returns PDF as a string
7796  	 	 	 	 return $this->getBuffer();
7797  	 	 	 }
7798  	 	 	 default: {
7799  	 	 	 	 $this->Error('Incorrect output destination: '.$dest);
7800  	 	 	 }
7801  	 	 }
7802  	 	 return '';
7803  	 }
7804  
7805  	 protected static $cleaned_ids = array();
7806  	 /**
7807  	  * Unset all class variables except the following critical variables.
7808  	  * @param $destroyall (boolean) if true destroys all class variables, otherwise preserves critical variables.
7809  	  * @param $preserve_objcopy (boolean) if true preserves the objcopy variable
7810  	  * @public
7811  	  * @since 4.5.016 (2009-02-24)
7812  	  */
7813  	public function _destroy($destroyall=false, $preserve_objcopy=false) {
7814  	 	 if (isset(self::$cleaned_ids[$this->file_id])) {
7815  	 	 	 $destroyall = false;
7816  	 	 }
7817  	 	 if ($destroyall AND !$preserve_objcopy && isset($this->file_id)) {
7818  	 	 	 self::$cleaned_ids[$this->file_id] = true;
7819  	 	 	 // remove all temporary files
7820  	 	 	 if ($handle = @opendir(K_PATH_CACHE)) {
7821  	 	 	 	 while ( false !== ( $file_name = readdir( $handle ) ) ) {
7822  	 	 	 	 	 if (strpos($file_name, '__tcpdf_'.$this->file_id.'_') === 0) {
7823  	 	 	 	 	 	 unlink(K_PATH_CACHE.$file_name);
7824  	 	 	 	 	 }
7825  	 	 	 	 }
7826  	 	 	 	 closedir($handle);
7827  	 	 	 }
7828  	 	 	 if (isset($this->imagekeys)) {
7829  	 	 	 	 foreach($this->imagekeys as $file) {
7830  	 	 	 	 	 if (strpos($file, K_PATH_CACHE) === 0 && TCPDF_STATIC::file_exists($file)) {
7831  	 	 	 	 	 	 @unlink($file);
7832  	 	 	 	 	 }
7833  	 	 	 	 }
7834  	 	 	 }
7835  	 	 }
7836  	 	 $preserve = array(
7837  	 	 	 'file_id',
7838  	 	 	 'state',
7839  	 	 	 'bufferlen',
7840  	 	 	 'buffer',
7841  	 	 	 'cached_files',
7842  	 	 	 'imagekeys',
7843  	 	 	 'sign',
7844  	 	 	 'signature_data',
7845  	 	 	 'signature_max_length',
7846  	 	 	 'byterange_string',
7847  	 	 	 'tsa_timestamp',
7848  	 	 	 'tsa_data'
7849  	 	 );
7850  	 	 foreach (array_keys(get_object_vars($this)) as $val) {
7851  	 	 	 if ($destroyall OR !in_array($val, $preserve)) {
7852  	 	 	 	 if ((!$preserve_objcopy OR ($val != 'objcopy')) AND ($val != 'file_id') AND isset($this->$val)) {
7853  	 	 	 	 	 unset($this->$val);
7854  	 	 	 	 }
7855  	 	 	 }
7856  	 	 }
7857  	 }
7858  
7859  	 /**
7860  	  * Check for locale-related bug
7861  	  * @protected
7862  	  */
7863  	protected function _dochecks() {
7864  	 	 //Check for locale-related bug
7865  	 	 if (1.1 == 1) {
7866  	 	 	 $this->Error('Don\'t alter the locale before including class file');
7867  	 	 }
7868  	 	 //Check for decimal separator
7869  	 	 if (sprintf('%.1F', 1.0) != '1.0') {
7870  	 	 	 setlocale(LC_NUMERIC, 'C');
7871  	 	 }
7872  	 }
7873  
7874  	 /**
7875  	  * Return an array containing variations for the basic page number alias.
7876  	  * @param $a (string) Base alias.
7877  	  * @return array of page number aliases
7878  	  * @protected
7879  	  */
7880  	protected function getInternalPageNumberAliases($a= '') {
7881  	 	 $alias = array();
7882  	 	 // build array of Unicode + ASCII variants (the order is important)
7883  	 	 $alias = array('u' => array(), 'a' => array());
7884  	 	 $u = '{'.$a.'}';
7885  	 	 $alias['u'][] = TCPDF_STATIC::_escape($u);
7886  	 	 if ($this->isunicode) {
7887  	 	 	 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($u, $this->isunicode, $this->CurrentFont));
7888  	 	 	 $alias['u'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($u, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7889  	 	 	 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::UTF8ToLatin1($a, $this->isunicode, $this->CurrentFont));
7890  	 	 	 $alias['a'][] = TCPDF_STATIC::_escape(TCPDF_FONTS::utf8StrRev($a, false, $this->tmprtl, $this->isunicode, $this->CurrentFont));
7891  	 	 }
7892  	 	 $alias['a'][] = TCPDF_STATIC::_escape($a);
7893  	 	 return $alias;
7894  	 }
7895  
7896  	 /**
7897  	  * Return an array containing all internal page aliases.
7898  	  * @return array of page number aliases
7899  	  * @protected
7900  	  */
7901  	protected function getAllInternalPageNumberAliases() {
7902  	 	 $basic_alias = array(TCPDF_STATIC::$alias_tot_pages, TCPDF_STATIC::$alias_num_page, TCPDF_STATIC::$alias_group_tot_pages, TCPDF_STATIC::$alias_group_num_page, TCPDF_STATIC::$alias_right_shift);
7903  	 	 $pnalias = array();
7904  	 	 foreach($basic_alias as $k => $a) {
7905  	 	 	 $pnalias[$k] = $this->getInternalPageNumberAliases($a);
7906  	 	 }
7907  	 	 return $pnalias;
7908  	 }
7909  
7910  	 /**
7911  	  * Replace right shift page number aliases with spaces to correct right alignment.
7912  	  * This works perfectly only when using monospaced fonts.
7913  	  * @param $page (string) Page content.
7914  	  * @param $aliases (array) Array of page aliases.
7915  	  * @param $diff (int) initial difference to add.
7916  	  * @return replaced page content.
7917  	  * @protected
7918  	  */
7919  	protected function replaceRightShiftPageNumAliases($page, $aliases, $diff) {
7920  	 	 foreach ($aliases as $type => $alias) {
7921  	 	 	 foreach ($alias as $a) {
7922  	 	 	 	 // find position of compensation factor
7923  	 	 	 	 $startnum = (strpos($a, ':') + 1);
7924  	 	 	 	 $a = substr($a, 0, $startnum);
7925  	 	 	 	 if (($pos = strpos($page, $a)) !== false) {
7926  	 	 	 	 	 // end of alias
7927  	 	 	 	 	 $endnum = strpos($page, '}', $pos);
7928  	 	 	 	 	 // string to be replaced
7929  	 	 	 	 	 $aa = substr($page, $pos, ($endnum - $pos + 1));
7930  	 	 	 	 	 // get compensation factor
7931  	 	 	 	 	 $ratio = substr($page, ($pos + $startnum), ($endnum - $pos - $startnum));
7932  	 	 	 	 	 $ratio = preg_replace('/[^0-9\.]/', '', $ratio);
7933  	 	 	 	 	 $ratio = floatval($ratio);
7934  	 	 	 	 	 if ($type == 'u') {
7935  	 	 	 	 	 	 $chrdiff = floor(($diff + 12) * $ratio);
7936  	 	 	 	 	 	 $shift = str_repeat(' ', $chrdiff);
7937  	 	 	 	 	 	 $shift = TCPDF_FONTS::UTF8ToUTF16BE($shift, false, $this->isunicode, $this->CurrentFont);
7938  	 	 	 	 	 } else {
7939  	 	 	 	 	 	 $chrdiff = floor(($diff + 11) * $ratio);
7940  	 	 	 	 	 	 $shift = str_repeat(' ', $chrdiff);
7941  	 	 	 	 	 }
7942  	 	 	 	 	 $page = str_replace($aa, $shift, $page);
7943  	 	 	 	 }
7944  	 	 	 }
7945  	 	 }
7946  	 	 return $page;
7947  	 }
7948  
7949  	 /**
7950  	  * Set page boxes to be included on page descriptions.
7951  	  * @param $boxes (array) Array of page boxes to set on document: ('MediaBox', 'CropBox', 'BleedBox', 'TrimBox', 'ArtBox').
7952  	  * @protected
7953  	  */
7954  	protected function setPageBoxTypes($boxes) {
7955  	 	 $this->page_boxes = array();
7956  	 	 foreach ($boxes as $box) {
7957  	 	 	 if (in_array($box, TCPDF_STATIC::$pageboxes)) {
7958  	 	 	 	 $this->page_boxes[] = $box;
7959  	 	 	 }
7960  	 	 }
7961  	 }
7962  
7963  	 /**
7964  	  * Output pages (and replace page number aliases).
7965  	  * @protected
7966  	  */
7967  	protected function _putpages() {
7968  	 	 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
7969  	 	 // get internal aliases for page numbers
7970  	 	 $pnalias = $this->getAllInternalPageNumberAliases();
7971  	 	 $num_pages = $this->numpages;
7972  	 	 $ptpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $num_pages - 1));
7973  	 	 $ptpu = TCPDF_FONTS::UTF8ToUTF16BE($ptpa, false, $this->isunicode, $this->CurrentFont);
7974  	 	 $ptp_num_chars = $this->GetNumChars($ptpa);
7975  	 	 $pagegroupnum = 0;
7976  	 	 $groupnum = 0;
7977  	 	 $ptgu = 1;
7978  	 	 $ptga = 1;
7979  	 	 $ptg_num_chars = 1;
7980  	 	 for ($n = 1; $n <= $num_pages; ++$n) {
7981  	 	 	 // get current page
7982  	 	 	 $temppage = $this->getPageBuffer($n);
7983  	 	 	 $pagelen = strlen($temppage);
7984  	 	 	 // set replacements for total pages number
7985  	 	 	 $pnpa = TCPDF_STATIC::formatPageNumber(($this->starting_page_number + $n - 1));
7986  	 	 	 $pnpu = TCPDF_FONTS::UTF8ToUTF16BE($pnpa, false, $this->isunicode, $this->CurrentFont);
7987  	 	 	 $pnp_num_chars = $this->GetNumChars($pnpa);
7988  	 	 	 $pdiff = 0; // difference used for right shift alignment of page numbers
7989  	 	 	 $gdiff = 0; // difference used for right shift alignment of page group numbers
7990  	 	 	 if (!empty($this->pagegroups)) {
7991  	 	 	 	 if (isset($this->newpagegroup[$n])) {
7992  	 	 	 	 	 $pagegroupnum = 0;
7993  	 	 	 	 	 ++$groupnum;
7994  	 	 	 	 	 $ptga = TCPDF_STATIC::formatPageNumber($this->pagegroups[$groupnum]);
7995  	 	 	 	 	 $ptgu = TCPDF_FONTS::UTF8ToUTF16BE($ptga, false, $this->isunicode, $this->CurrentFont);
7996  	 	 	 	 	 $ptg_num_chars = $this->GetNumChars($ptga);
7997  	 	 	 	 }
7998  	 	 	 	 ++$pagegroupnum;
7999  	 	 	 	 $pnga = TCPDF_STATIC::formatPageNumber($pagegroupnum);
8000  	 	 	 	 $pngu = TCPDF_FONTS::UTF8ToUTF16BE($pnga, false, $this->isunicode, $this->CurrentFont);
8001  	 	 	 	 $png_num_chars = $this->GetNumChars($pnga);
8002  	 	 	 	 // replace page numbers
8003  	 	 	 	 $replace = array();
8004  	 	 	 	 $replace[] = array($ptgu, $ptg_num_chars, 9, $pnalias[2]['u']);
8005  	 	 	 	 $replace[] = array($ptga, $ptg_num_chars, 7, $pnalias[2]['a']);
8006  	 	 	 	 $replace[] = array($pngu, $png_num_chars, 9, $pnalias[3]['u']);
8007  	 	 	 	 $replace[] = array($pnga, $png_num_chars, 7, $pnalias[3]['a']);
8008  	 	 	 	 list($temppage, $gdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $gdiff);
8009  	 	 	 }
8010  	 	 	 // replace page numbers
8011  	 	 	 $replace = array();
8012  	 	 	 $replace[] = array($ptpu, $ptp_num_chars, 9, $pnalias[0]['u']);
8013  	 	 	 $replace[] = array($ptpa, $ptp_num_chars, 7, $pnalias[0]['a']);
8014  	 	 	 $replace[] = array($pnpu, $pnp_num_chars, 9, $pnalias[1]['u']);
8015  	 	 	 $replace[] = array($pnpa, $pnp_num_chars, 7, $pnalias[1]['a']);
8016  	 	 	 list($temppage, $pdiff) = TCPDF_STATIC::replacePageNumAliases($temppage, $replace, $pdiff);
8017  	 	 	 // replace right shift alias
8018  	 	 	 $temppage = $this->replaceRightShiftPageNumAliases($temppage, $pnalias[4], max($pdiff, $gdiff));
8019  	 	 	 // replace EPS marker
8020  	 	 	 $temppage = str_replace($this->epsmarker, '', $temppage);
8021  	 	 	 //Page
8022  	 	 	 $this->page_obj_id[$n] = $this->_newobj();
8023  	 	 	 $out = '<<';
8024  	 	 	 $out .= ' /Type /Page';
8025  	 	 	 $out .= ' /Parent 1 0 R';
8026  	 	 	 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
8027  	 	 	 	 $out .= ' /LastModified '.$this->_datestring(0, $this->doc_modification_timestamp);
8028  	 	 	 }
8029  	 	 	 $out .= ' /Resources 2 0 R';
8030  	 	 	 foreach ($this->page_boxes as $box) {
8031  	 	 	 	 $out .= ' /'.$box;
8032  	 	 	 	 $out .= sprintf(' [%F %F %F %F]', $this->pagedim[$n][$box]['llx'], $this->pagedim[$n][$box]['lly'], $this->pagedim[$n][$box]['urx'], $this->pagedim[$n][$box]['ury']);
8033  	 	 	 }
8034  	 	 	 if (isset($this->pagedim[$n]['BoxColorInfo']) AND !empty($this->pagedim[$n]['BoxColorInfo'])) {
8035  	 	 	 	 $out .= ' /BoxColorInfo <<';
8036  	 	 	 	 foreach ($this->page_boxes as $box) {
8037  	 	 	 	 	 if (isset($this->pagedim[$n]['BoxColorInfo'][$box])) {
8038  	 	 	 	 	 	 $out .= ' /'.$box.' <<';
8039  	 	 	 	 	 	 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['C'])) {
8040  	 	 	 	 	 	 	 $color = $this->pagedim[$n]['BoxColorInfo'][$box]['C'];
8041  	 	 	 	 	 	 	 $out .= ' /C [';
8042  	 	 	 	 	 	 	 $out .= sprintf(' %F %F %F', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
8043  	 	 	 	 	 	 	 $out .= ' ]';
8044  	 	 	 	 	 	 }
8045  	 	 	 	 	 	 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['W'])) {
8046  	 	 	 	 	 	 	 $out .= ' /W '.($this->pagedim[$n]['BoxColorInfo'][$box]['W'] * $this->k);
8047  	 	 	 	 	 	 }
8048  	 	 	 	 	 	 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['S'])) {
8049  	 	 	 	 	 	 	 $out .= ' /S /'.$this->pagedim[$n]['BoxColorInfo'][$box]['S'];
8050  	 	 	 	 	 	 }
8051  	 	 	 	 	 	 if (isset($this->pagedim[$n]['BoxColorInfo'][$box]['D'])) {
8052  	 	 	 	 	 	 	 $dashes = $this->pagedim[$n]['BoxColorInfo'][$box]['D'];
8053  	 	 	 	 	 	 	 $out .= ' /D [';
8054  	 	 	 	 	 	 	 foreach ($dashes as $dash) {
8055  	 	 	 	 	 	 	 	 $out .= sprintf(' %F', ($dash * $this->k));
8056  	 	 	 	 	 	 	 }
8057  	 	 	 	 	 	 	 $out .= ' ]';
8058  	 	 	 	 	 	 }
8059  	 	 	 	 	 	 $out .= ' >>';
8060  	 	 	 	 	 }
8061  	 	 	 	 }
8062  	 	 	 	 $out .= ' >>';
8063  	 	 	 }
8064  	 	 	 $out .= ' /Contents '.($this->n + 1).' 0 R';
8065  	 	 	 $out .= ' /Rotate '.$this->pagedim[$n]['Rotate'];
8066  	 	 	 if (!$this->pdfa_mode) {
8067  	 	 	 	 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceRGB >>';
8068  	 	 	 }
8069  	 	 	 if (isset($this->pagedim[$n]['trans']) AND !empty($this->pagedim[$n]['trans'])) {
8070  	 	 	 	 // page transitions
8071  	 	 	 	 if (isset($this->pagedim[$n]['trans']['Dur'])) {
8072  	 	 	 	 	 $out .= ' /Dur '.$this->pagedim[$n]['trans']['Dur'];
8073  	 	 	 	 }
8074  	 	 	 	 $out .= ' /Trans <<';
8075  	 	 	 	 $out .= ' /Type /Trans';
8076  	 	 	 	 if (isset($this->pagedim[$n]['trans']['S'])) {
8077  	 	 	 	 	 $out .= ' /S /'.$this->pagedim[$n]['trans']['S'];
8078  	 	 	 	 }
8079  	 	 	 	 if (isset($this->pagedim[$n]['trans']['D'])) {
8080  	 	 	 	 	 $out .= ' /D '.$this->pagedim[$n]['trans']['D'];
8081  	 	 	 	 }
8082  	 	 	 	 if (isset($this->pagedim[$n]['trans']['Dm'])) {
8083  	 	 	 	 	 $out .= ' /Dm /'.$this->pagedim[$n]['trans']['Dm'];
8084  	 	 	 	 }
8085  	 	 	 	 if (isset($this->pagedim[$n]['trans']['M'])) {
8086  	 	 	 	 	 $out .= ' /M /'.$this->pagedim[$n]['trans']['M'];
8087  	 	 	 	 }
8088  	 	 	 	 if (isset($this->pagedim[$n]['trans']['Di'])) {
8089  	 	 	 	 	 $out .= ' /Di '.$this->pagedim[$n]['trans']['Di'];
8090  	 	 	 	 }
8091  	 	 	 	 if (isset($this->pagedim[$n]['trans']['SS'])) {
8092  	 	 	 	 	 $out .= ' /SS '.$this->pagedim[$n]['trans']['SS'];
8093  	 	 	 	 }
8094  	 	 	 	 if (isset($this->pagedim[$n]['trans']['B'])) {
8095  	 	 	 	 	 $out .= ' /B '.$this->pagedim[$n]['trans']['B'];
8096  	 	 	 	 }
8097  	 	 	 	 $out .= ' >>';
8098  	 	 	 }
8099  	 	 	 $out .= $this->_getannotsrefs($n);
8100  	 	 	 $out .= ' /PZ '.$this->pagedim[$n]['PZ'];
8101  	 	 	 $out .= ' >>';
8102  	 	 	 $out .= "\n".'endobj';
8103  	 	 	 $this->_out($out);
8104  	 	 	 //Page content
8105  	 	 	 $p = ($this->compress) ? gzcompress($temppage) : $temppage;
8106  	 	 	 $this->_newobj();
8107  	 	 	 $p = $this->_getrawstream($p);
8108  	 	 	 $this->_out('<<'.$filter.'/Length '.strlen($p).'>> stream'."\n".$p."\n".'endstream'."\n".'endobj');
8109  	 	 }
8110  	 	 //Pages root
8111  	 	 $out = $this->_getobj(1)."\n";
8112  	 	 $out .= '<< /Type /Pages /Kids [';
8113  	 	 foreach($this->page_obj_id as $page_obj) {
8114  	 	 	 $out .= ' '.$page_obj.' 0 R';
8115  	 	 }
8116  	 	 $out .= ' ] /Count '.$num_pages.' >>';
8117  	 	 $out .= "\n".'endobj';
8118  	 	 $this->_out($out);
8119  	 }
8120  
8121  	 /**
8122  	  * Get references to page annotations.
8123  	  * @param $n (int) page number
8124  	  * @return string
8125  	  * @protected
8126  	  * @author Nicola Asuni
8127  	  * @since 5.0.010 (2010-05-17)
8128  	  */
8129  	protected function _getannotsrefs($n) {
8130  	 	 if (!(isset($this->PageAnnots[$n]) OR ($this->sign AND isset($this->signature_data['cert_type'])))) {
8131  	 	 	 return '';
8132  	 	 }
8133  	 	 $out = ' /Annots [';
8134  	 	 if (isset($this->PageAnnots[$n])) {
8135  	 	 	 foreach ($this->PageAnnots[$n] as $key => $val) {
8136  	 	 	 	 if (!in_array($val['n'], $this->radio_groups)) {
8137  	 	 	 	 	 $out .= ' '.$val['n'].' 0 R';
8138  	 	 	 	 }
8139  	 	 	 }
8140  	 	 	 // add radiobutton groups
8141  	 	 	 if (isset($this->radiobutton_groups[$n])) {
8142  	 	 	 	 foreach ($this->radiobutton_groups[$n] as $key => $data) {
8143  	 	 	 	 	 if (isset($data['n'])) {
8144  	 	 	 	 	 	 $out .= ' '.$data['n'].' 0 R';
8145  	 	 	 	 	 }
8146  	 	 	 	 }
8147  	 	 	 }
8148  	 	 }
8149  	 	 if ($this->sign AND ($n == $this->signature_appearance['page']) AND isset($this->signature_data['cert_type'])) {
8150  	 	 	 // set reference for signature object
8151  	 	 	 $out .= ' '.$this->sig_obj_id.' 0 R';
8152  	 	 }
8153  	 	 if (!empty($this->empty_signature_appearance)) {
8154  	 	 	 foreach ($this->empty_signature_appearance as $esa) {
8155  	 	 	 	 if ($esa['page'] == $n) {
8156  	 	 	 	 	 // set reference for empty signature objects
8157  	 	 	 	 	 $out .= ' '.$esa['objid'].' 0 R';
8158  	 	 	 	 }
8159  	 	 	 }
8160  	 	 }
8161  	 	 $out .= ' ]';
8162  	 	 return $out;
8163  	 }
8164  
8165  	 /**
8166  	  * Output annotations objects for all pages.
8167  	  * !!! THIS METHOD IS NOT YET COMPLETED !!!
8168  	  * See section 12.5 of PDF 32000_2008 reference.
8169  	  * @protected
8170  	  * @author Nicola Asuni
8171  	  * @since 4.0.018 (2008-08-06)
8172  	  */
8173  	protected function _putannotsobjs() {
8174  	 	 // reset object counter
8175  	 	 for ($n=1; $n <= $this->numpages; ++$n) {
8176  	 	 	 if (isset($this->PageAnnots[$n])) {
8177  	 	 	 	 // set page annotations
8178  	 	 	 	 foreach ($this->PageAnnots[$n] as $key => $pl) {
8179  	 	 	 	 	 $annot_obj_id = $this->PageAnnots[$n][$key]['n'];
8180  	 	 	 	 	 // create annotation object for grouping radiobuttons
8181  	 	 	 	 	 if (isset($this->radiobutton_groups[$n][$pl['txt']]) AND is_array($this->radiobutton_groups[$n][$pl['txt']])) {
8182  	 	 	 	 	 	 $radio_button_obj_id = $this->radiobutton_groups[$n][$pl['txt']]['n'];
8183  	 	 	 	 	 	 $annots = '<<';
8184  	 	 	 	 	 	 $annots .= ' /Type /Annot';
8185  	 	 	 	 	 	 $annots .= ' /Subtype /Widget';
8186  	 	 	 	 	 	 $annots .= ' /Rect [0 0 0 0]';
8187  	 	 	 	 	 	 if ($this->radiobutton_groups[$n][$pl['txt']]['#readonly#']) {
8188  	 	 	 	 	 	 	 // read only
8189  	 	 	 	 	 	 	 $annots .= ' /F 68';
8190  	 	 	 	 	 	 	 $annots .= ' /Ff 49153';
8191  	 	 	 	 	 	 } else {
8192  	 	 	 	 	 	 	 $annots .= ' /F 4'; // default print for PDF/A
8193  	 	 	 	 	 	 	 $annots .= ' /Ff 49152';
8194  	 	 	 	 	 	 }
8195  	 	 	 	 	 	 $annots .= ' /T '.$this->_datastring($pl['txt'], $radio_button_obj_id);
8196  	 	 	 	 	 	 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8197  	 	 	 	 	 	 	 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $radio_button_obj_id);
8198  	 	 	 	 	 	 }
8199  	 	 	 	 	 	 $annots .= ' /FT /Btn';
8200  	 	 	 	 	 	 $annots .= ' /Kids [';
8201  	 	 	 	 	 	 $defval = '';
8202  	 	 	 	 	 	 foreach ($this->radiobutton_groups[$n][$pl['txt']] as $key => $data) {
8203  	 	 	 	 	 	 	 if (isset($data['kid'])) {
8204  	 	 	 	 	 	 	 	 $annots .= ' '.$data['kid'].' 0 R';
8205  	 	 	 	 	 	 	 	 if ($data['def'] !== 'Off') {
8206  	 	 	 	 	 	 	 	 	 $defval = $data['def'];
8207  	 	 	 	 	 	 	 	 }
8208  	 	 	 	 	 	 	 }
8209  	 	 	 	 	 	 }
8210  	 	 	 	 	 	 $annots .= ' ]';
8211  	 	 	 	 	 	 if (!empty($defval)) {
8212  	 	 	 	 	 	 	 $annots .= ' /V /'.$defval;
8213  	 	 	 	 	 	 }
8214  	 	 	 	 	 	 $annots .= ' >>';
8215  	 	 	 	 	 	 $this->_out($this->_getobj($radio_button_obj_id)."\n".$annots."\n".'endobj');
8216  	 	 	 	 	 	 $this->form_obj_id[] = $radio_button_obj_id;
8217  	 	 	 	 	 	 // store object id to be used on Parent entry of Kids
8218  	 	 	 	 	 	 $this->radiobutton_groups[$n][$pl['txt']] = $radio_button_obj_id;
8219  	 	 	 	 	 }
8220  	 	 	 	 	 $formfield = false;
8221  	 	 	 	 	 $pl['opt'] = array_change_key_case($pl['opt'], CASE_LOWER);
8222  	 	 	 	 	 $a = $pl['x'] * $this->k;
8223  	 	 	 	 	 $b = $this->pagedim[$n]['h'] - (($pl['y'] + $pl['h']) * $this->k);
8224  	 	 	 	 	 $c = $pl['w'] * $this->k;
8225  	 	 	 	 	 $d = $pl['h'] * $this->k;
8226  	 	 	 	 	 $rect = sprintf('%F %F %F %F', $a, $b, $a+$c, $b+$d);
8227  	 	 	 	 	 // create new annotation object
8228  	 	 	 	 	 $annots = '<</Type /Annot';
8229  	 	 	 	 	 $annots .= ' /Subtype /'.$pl['opt']['subtype'];
8230  	 	 	 	 	 $annots .= ' /Rect ['.$rect.']';
8231  	 	 	 	 	 $ft = array('Btn', 'Tx', 'Ch', 'Sig');
8232  	 	 	 	 	 if (isset($pl['opt']['ft']) AND in_array($pl['opt']['ft'], $ft)) {
8233  	 	 	 	 	 	 $annots .= ' /FT /'.$pl['opt']['ft'];
8234  	 	 	 	 	 	 $formfield = true;
8235  	 	 	 	 	 }
8236  	 	 	 	 	 if ($pl['opt']['subtype'] !== 'Link') {
8237  	 	 	 	 	 	 $annots .= ' /Contents '.$this->_textstring($pl['txt'], $annot_obj_id);
8238  	 	 	 	 	 }
8239  	 	 	 	 	 $annots .= ' /P '.$this->page_obj_id[$n].' 0 R';
8240  	 	 	 	 	 $annots .= ' /NM '.$this->_datastring(sprintf('%04u-%04u', $n, $key), $annot_obj_id);
8241  	 	 	 	 	 $annots .= ' /M '.$this->_datestring($annot_obj_id, $this->doc_modification_timestamp);
8242  	 	 	 	 	 if (isset($pl['opt']['f'])) {
8243  	 	 	 	 	 	 $fval = 0;
8244  	 	 	 	 	 	 if (is_array($pl['opt']['f'])) {
8245  	 	 	 	 	 	 	 foreach ($pl['opt']['f'] as $f) {
8246  	 	 	 	 	 	 	 	 switch (strtolower($f)) {
8247  	 	 	 	 	 	 	 	 	 case 'invisible': {
8248  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 0;
8249  	 	 	 	 	 	 	 	 	 	 break;
8250  	 	 	 	 	 	 	 	 	 }
8251  	 	 	 	 	 	 	 	 	 case 'hidden': {
8252  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 1;
8253  	 	 	 	 	 	 	 	 	 	 break;
8254  	 	 	 	 	 	 	 	 	 }
8255  	 	 	 	 	 	 	 	 	 case 'print': {
8256  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 2;
8257  	 	 	 	 	 	 	 	 	 	 break;
8258  	 	 	 	 	 	 	 	 	 }
8259  	 	 	 	 	 	 	 	 	 case 'nozoom': {
8260  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 3;
8261  	 	 	 	 	 	 	 	 	 	 break;
8262  	 	 	 	 	 	 	 	 	 }
8263  	 	 	 	 	 	 	 	 	 case 'norotate': {
8264  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 4;
8265  	 	 	 	 	 	 	 	 	 	 break;
8266  	 	 	 	 	 	 	 	 	 }
8267  	 	 	 	 	 	 	 	 	 case 'noview': {
8268  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 5;
8269  	 	 	 	 	 	 	 	 	 	 break;
8270  	 	 	 	 	 	 	 	 	 }
8271  	 	 	 	 	 	 	 	 	 case 'readonly': {
8272  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 6;
8273  	 	 	 	 	 	 	 	 	 	 break;
8274  	 	 	 	 	 	 	 	 	 }
8275  	 	 	 	 	 	 	 	 	 case 'locked': {
8276  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 8;
8277  	 	 	 	 	 	 	 	 	 	 break;
8278  	 	 	 	 	 	 	 	 	 }
8279  	 	 	 	 	 	 	 	 	 case 'togglenoview': {
8280  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 9;
8281  	 	 	 	 	 	 	 	 	 	 break;
8282  	 	 	 	 	 	 	 	 	 }
8283  	 	 	 	 	 	 	 	 	 case 'lockedcontents': {
8284  	 	 	 	 	 	 	 	 	 	 $fval += 1 << 10;
8285  	 	 	 	 	 	 	 	 	 	 break;
8286  	 	 	 	 	 	 	 	 	 }
8287  	 	 	 	 	 	 	 	 	 default: {
8288  	 	 	 	 	 	 	 	 	 	 break;
8289  	 	 	 	 	 	 	 	 	 }
8290  	 	 	 	 	 	 	 	 }
8291  	 	 	 	 	 	 	 }
8292  	 	 	 	 	 	 } else {
8293  	 	 	 	 	 	 	 $fval = intval($pl['opt']['f']);
8294  	 	 	 	 	 	 }
8295  	 	 	 	 	 } else {
8296  	 	 	 	 	 	 $fval = 4;
8297  	 	 	 	 	 }
8298  	 	 	 	 	 if ($this->pdfa_mode) {
8299  	 	 	 	 	 	 // force print flag for PDF/A mode
8300  	 	 	 	 	 	 $fval |= 4;
8301  	 	 	 	 	 }
8302  	 	 	 	 	 $annots .= ' /F '.intval($fval);
8303  	 	 	 	 	 if (isset($pl['opt']['as']) AND is_string($pl['opt']['as'])) {
8304  	 	 	 	 	 	 $annots .= ' /AS /'.$pl['opt']['as'];
8305  	 	 	 	 	 }
8306  	 	 	 	 	 if (isset($pl['opt']['ap'])) {
8307  	 	 	 	 	 	 // appearance stream
8308  	 	 	 	 	 	 $annots .= ' /AP <<';
8309  	 	 	 	 	 	 if (is_array($pl['opt']['ap'])) {
8310  	 	 	 	 	 	 	 foreach ($pl['opt']['ap'] as $apmode => $apdef) {
8311  	 	 	 	 	 	 	 	 // $apmode can be: n = normal; r = rollover; d = down;
8312  	 	 	 	 	 	 	 	 $annots .= ' /'.strtoupper($apmode);
8313  	 	 	 	 	 	 	 	 if (is_array($apdef)) {
8314  	 	 	 	 	 	 	 	 	 $annots .= ' <<';
8315  	 	 	 	 	 	 	 	 	 foreach ($apdef as $apstate => $stream) {
8316  	 	 	 	 	 	 	 	 	 	 // reference to XObject that define the appearance for this mode-state
8317  	 	 	 	 	 	 	 	 	 	 $apsobjid = $this->_putAPXObject($c, $d, $stream);
8318  	 	 	 	 	 	 	 	 	 	 $annots .= ' /'.$apstate.' '.$apsobjid.' 0 R';
8319  	 	 	 	 	 	 	 	 	 }
8320  	 	 	 	 	 	 	 	 	 $annots .= ' >>';
8321  	 	 	 	 	 	 	 	 } else {
8322  	 	 	 	 	 	 	 	 	 // reference to XObject that define the appearance for this mode
8323  	 	 	 	 	 	 	 	 	 $apsobjid = $this->_putAPXObject($c, $d, $apdef);
8324  	 	 	 	 	 	 	 	 	 $annots .= ' '.$apsobjid.' 0 R';
8325  	 	 	 	 	 	 	 	 }
8326  	 	 	 	 	 	 	 }
8327  	 	 	 	 	 	 } else {
8328  	 	 	 	 	 	 	 $annots .= $pl['opt']['ap'];
8329  	 	 	 	 	 	 }
8330  	 	 	 	 	 	 $annots .= ' >>';
8331  	 	 	 	 	 }
8332  	 	 	 	 	 if (isset($pl['opt']['bs']) AND (is_array($pl['opt']['bs']))) {
8333  	 	 	 	 	 	 $annots .= ' /BS <<';
8334  	 	 	 	 	 	 $annots .= ' /Type /Border';
8335  	 	 	 	 	 	 if (isset($pl['opt']['bs']['w'])) {
8336  	 	 	 	 	 	 	 $annots .= ' /W '.intval($pl['opt']['bs']['w']);
8337  	 	 	 	 	 	 }
8338  	 	 	 	 	 	 $bstyles = array('S', 'D', 'B', 'I', 'U');
8339  	 	 	 	 	 	 if (isset($pl['opt']['bs']['s']) AND in_array($pl['opt']['bs']['s'], $bstyles)) {
8340  	 	 	 	 	 	 	 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8341  	 	 	 	 	 	 }
8342  	 	 	 	 	 	 if (isset($pl['opt']['bs']['d']) AND (is_array($pl['opt']['bs']['d']))) {
8343  	 	 	 	 	 	 	 $annots .= ' /D [';
8344  	 	 	 	 	 	 	 foreach ($pl['opt']['bs']['d'] as $cord) {
8345  	 	 	 	 	 	 	 	 $annots .= ' '.intval($cord);
8346  	 	 	 	 	 	 	 }
8347  	 	 	 	 	 	 	 $annots .= ']';
8348  	 	 	 	 	 	 }
8349  	 	 	 	 	 	 $annots .= ' >>';
8350  	 	 	 	 	 } else {
8351  	 	 	 	 	 	 $annots .= ' /Border [';
8352  	 	 	 	 	 	 if (isset($pl['opt']['border']) AND (count($pl['opt']['border']) >= 3)) {
8353  	 	 	 	 	 	 	 $annots .= intval($pl['opt']['border'][0]).' ';
8354  	 	 	 	 	 	 	 $annots .= intval($pl['opt']['border'][1]).' ';
8355  	 	 	 	 	 	 	 $annots .= intval($pl['opt']['border'][2]);
8356  	 	 	 	 	 	 	 if (isset($pl['opt']['border'][3]) AND is_array($pl['opt']['border'][3])) {
8357  	 	 	 	 	 	 	 	 $annots .= ' [';
8358  	 	 	 	 	 	 	 	 foreach ($pl['opt']['border'][3] as $dash) {
8359  	 	 	 	 	 	 	 	 	 $annots .= intval($dash).' ';
8360  	 	 	 	 	 	 	 	 }
8361  	 	 	 	 	 	 	 	 $annots .= ']';
8362  	 	 	 	 	 	 	 }
8363  	 	 	 	 	 	 } else {
8364  	 	 	 	 	 	 	 $annots .= '0 0 0';
8365  	 	 	 	 	 	 }
8366  	 	 	 	 	 	 $annots .= ']';
8367  	 	 	 	 	 }
8368  	 	 	 	 	 if (isset($pl['opt']['be']) AND (is_array($pl['opt']['be']))) {
8369  	 	 	 	 	 	 $annots .= ' /BE <<';
8370  	 	 	 	 	 	 $bstyles = array('S', 'C');
8371  	 	 	 	 	 	 if (isset($pl['opt']['be']['s']) AND in_array($pl['opt']['be']['s'], $bstyles)) {
8372  	 	 	 	 	 	 	 $annots .= ' /S /'.$pl['opt']['bs']['s'];
8373  	 	 	 	 	 	 } else {
8374  	 	 	 	 	 	 	 $annots .= ' /S /S';
8375  	 	 	 	 	 	 }
8376  	 	 	 	 	 	 if (isset($pl['opt']['be']['i']) AND ($pl['opt']['be']['i'] >= 0) AND ($pl['opt']['be']['i'] <= 2)) {
8377  	 	 	 	 	 	 	 $annots .= ' /I '.sprintf(' %F', $pl['opt']['be']['i']);
8378  	 	 	 	 	 	 }
8379  	 	 	 	 	 	 $annots .= '>>';
8380  	 	 	 	 	 }
8381  	 	 	 	 	 if (isset($pl['opt']['c']) AND (is_array($pl['opt']['c'])) AND !empty($pl['opt']['c'])) {
8382  	 	 	 	 	 	 $annots .= ' /C '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['c']);
8383  	 	 	 	 	 }
8384  	 	 	 	 	 //$annots .= ' /StructParent ';
8385  	 	 	 	 	 //$annots .= ' /OC ';
8386  	 	 	 	 	 $markups = array('text', 'freetext', 'line', 'square', 'circle', 'polygon', 'polyline', 'highlight', 'underline', 'squiggly', 'strikeout', 'stamp', 'caret', 'ink', 'fileattachment', 'sound');
8387  	 	 	 	 	 if (in_array(strtolower($pl['opt']['subtype']), $markups)) {
8388  	 	 	 	 	 	 // this is a markup type
8389  	 	 	 	 	 	 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8390  	 	 	 	 	 	 	 $annots .= ' /T '.$this->_textstring($pl['opt']['t'], $annot_obj_id);
8391  	 	 	 	 	 	 }
8392  	 	 	 	 	 	 //$annots .= ' /Popup ';
8393  	 	 	 	 	 	 if (isset($pl['opt']['ca'])) {
8394  	 	 	 	 	 	 	 $annots .= ' /CA '.sprintf('%F', floatval($pl['opt']['ca']));
8395  	 	 	 	 	 	 }
8396  	 	 	 	 	 	 if (isset($pl['opt']['rc'])) {
8397  	 	 	 	 	 	 	 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8398  	 	 	 	 	 	 }
8399  	 	 	 	 	 	 $annots .= ' /CreationDate '.$this->_datestring($annot_obj_id, $this->doc_creation_timestamp);
8400  	 	 	 	 	 	 //$annots .= ' /IRT ';
8401  	 	 	 	 	 	 if (isset($pl['opt']['subj'])) {
8402  	 	 	 	 	 	 	 $annots .= ' /Subj '.$this->_textstring($pl['opt']['subj'], $annot_obj_id);
8403  	 	 	 	 	 	 }
8404  	 	 	 	 	 	 //$annots .= ' /RT ';
8405  	 	 	 	 	 	 //$annots .= ' /IT ';
8406  	 	 	 	 	 	 //$annots .= ' /ExData ';
8407  	 	 	 	 	 }
8408  	 	 	 	 	 $lineendings = array('Square', 'Circle', 'Diamond', 'OpenArrow', 'ClosedArrow', 'None', 'Butt', 'ROpenArrow', 'RClosedArrow', 'Slash');
8409  	 	 	 	 	 // Annotation types
8410  	 	 	 	 	 switch (strtolower($pl['opt']['subtype'])) {
8411  	 	 	 	 	 	 case 'text': {
8412  	 	 	 	 	 	 	 if (isset($pl['opt']['open'])) {
8413  	 	 	 	 	 	 	 	 $annots .= ' /Open '. (strtolower($pl['opt']['open']) == 'true' ? 'true' : 'false');
8414  	 	 	 	 	 	 	 }
8415  	 	 	 	 	 	 	 $iconsapp = array('Comment', 'Help', 'Insert', 'Key', 'NewParagraph', 'Note', 'Paragraph');
8416  	 	 	 	 	 	 	 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8417  	 	 	 	 	 	 	 	 $annots .= ' /Name /'.$pl['opt']['name'];
8418  	 	 	 	 	 	 	 } else {
8419  	 	 	 	 	 	 	 	 $annots .= ' /Name /Note';
8420  	 	 	 	 	 	 	 }
8421  	 	 	 	 	 	 	 $statemodels = array('Marked', 'Review');
8422  	 	 	 	 	 	 	 if (isset($pl['opt']['statemodel']) AND in_array($pl['opt']['statemodel'], $statemodels)) {
8423  	 	 	 	 	 	 	 	 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8424  	 	 	 	 	 	 	 } else {
8425  	 	 	 	 	 	 	 	 $pl['opt']['statemodel'] = 'Marked';
8426  	 	 	 	 	 	 	 	 $annots .= ' /StateModel /'.$pl['opt']['statemodel'];
8427  	 	 	 	 	 	 	 }
8428  	 	 	 	 	 	 	 if ($pl['opt']['statemodel'] == 'Marked') {
8429  	 	 	 	 	 	 	 	 $states = array('Accepted', 'Unmarked');
8430  	 	 	 	 	 	 	 } else {
8431  	 	 	 	 	 	 	 	 $states = array('Accepted', 'Rejected', 'Cancelled', 'Completed', 'None');
8432  	 	 	 	 	 	 	 }
8433  	 	 	 	 	 	 	 if (isset($pl['opt']['state']) AND in_array($pl['opt']['state'], $states)) {
8434  	 	 	 	 	 	 	 	 $annots .= ' /State /'.$pl['opt']['state'];
8435  	 	 	 	 	 	 	 } else {
8436  	 	 	 	 	 	 	 	 if ($pl['opt']['statemodel'] == 'Marked') {
8437  	 	 	 	 	 	 	 	 	 $annots .= ' /State /Unmarked';
8438  	 	 	 	 	 	 	 	 } else {
8439  	 	 	 	 	 	 	 	 	 $annots .= ' /State /None';
8440  	 	 	 	 	 	 	 	 }
8441  	 	 	 	 	 	 	 }
8442  	 	 	 	 	 	 	 break;
8443  	 	 	 	 	 	 }
8444  	 	 	 	 	 	 case 'link': {
8445  	 	 	 	 	 	 	 if (is_string($pl['txt']) && !empty($pl['txt'])) {
8446  	 	 	 	 	 	 	 	 if ($pl['txt'][0] == '#') {
8447  	 	 	 	 	 	 	 	 	 // internal destination
8448  	 	 	 	 	 	 	 	 	 $annots .= ' /A <</S /GoTo /D '.TCPDF_STATIC::encodeNameObject(substr($pl['txt'], 1)).'>>';
8449  	 	 	 	 	 	 	 	 } elseif ($pl['txt'][0] == '%') {
8450  	 	 	 	 	 	 	 	 	 // embedded PDF file
8451  	 	 	 	 	 	 	 	 	 $filename = basename(substr($pl['txt'], 1));
8452  	 	 	 	 	 	 	 	 	 $annots .= ' /A << /S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($n - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
8453  	 	 	 	 	 	 	 	 } elseif ($pl['txt'][0] == '*') {
8454  	 	 	 	 	 	 	 	 	 // embedded generic file
8455  	 	 	 	 	 	 	 	 	 $filename = basename(substr($pl['txt'], 1));
8456  	 	 	 	 	 	 	 	 	 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
8457  	 	 	 	 	 	 	 	 	 $annots .= ' /A << /S /JavaScript /JS '.$this->_textstring($jsa, $annot_obj_id).'>>';
8458  	 	 	 	 	 	 	 	 } else {
8459  	 	 	 	 	 	 	 	 	 $parsedUrl = parse_url($pl['txt']);
8460  	 	 	 	 	 	 	 	 	 if (empty($parsedUrl['scheme']) AND (!empty($parsedUrl['path']) && strtolower(substr($parsedUrl['path'], -4)) == '.pdf')) {
8461  	 	 	 	 	 	 	 	 	 	 // relative link to a PDF file
8462  	 	 	 	 	 	 	 	 	 	 $dest = '[0 /Fit]'; // default page 0
8463  	 	 	 	 	 	 	 	 	 	 if (!empty($parsedUrl['fragment'])) {
8464  	 	 	 	 	 	 	 	 	 	 	 // check for named destination
8465  	 	 	 	 	 	 	 	 	 	 	 $tmp = explode('=', $parsedUrl['fragment']);
8466  	 	 	 	 	 	 	 	 	 	 	 $dest = '('.((count($tmp) == 2) ? $tmp[1] : $tmp[0]).')';
8467  	 	 	 	 	 	 	 	 	 	 }
8468  	 	 	 	 	 	 	 	 	 	 $annots .= ' /A <</S /GoToR /D '.$dest.' /F '.$this->_datastring($this->unhtmlentities($parsedUrl['path']), $annot_obj_id).' /NewWindow true>>';
8469  	 	 	 	 	 	 	 	 	 } else {
8470  	 	 	 	 	 	 	 	 	 	 // external URI link
8471  	 	 	 	 	 	 	 	 	 	 $annots .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($pl['txt']), $annot_obj_id).'>>';
8472  	 	 	 	 	 	 	 	 	 }
8473  	 	 	 	 	 	 	 	 }
8474  	 	 	 	 	 	 	 } elseif (isset($this->links[$pl['txt']])) {
8475  	 	 	 	 	 	 	 	 // internal link ID
8476  	 	 	 	 	 	 	 	 $l = $this->links[$pl['txt']];
8477  	 	 	 	 	 	 	 	 if (isset($this->page_obj_id[($l['p'])])) {
8478  	 	 	 	 	 	 	 	 	 $annots .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
8479  	 	 	 	 	 	 	 	 }
8480  	 	 	 	 	 	 	 }
8481  	 	 	 	 	 	 	 $hmodes = array('N', 'I', 'O', 'P');
8482  	 	 	 	 	 	 	 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmodes)) {
8483  	 	 	 	 	 	 	 	 $annots .= ' /H /'.$pl['opt']['h'];
8484  	 	 	 	 	 	 	 } else {
8485  	 	 	 	 	 	 	 	 $annots .= ' /H /I';
8486  	 	 	 	 	 	 	 }
8487  	 	 	 	 	 	 	 //$annots .= ' /PA ';
8488  	 	 	 	 	 	 	 //$annots .= ' /Quadpoints ';
8489  	 	 	 	 	 	 	 break;
8490  	 	 	 	 	 	 }
8491  	 	 	 	 	 	 case 'freetext': {
8492  	 	 	 	 	 	 	 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8493  	 	 	 	 	 	 	 	 $annots .= ' /DA ('.$pl['opt']['da'].')';
8494  	 	 	 	 	 	 	 }
8495  	 	 	 	 	 	 	 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8496  	 	 	 	 	 	 	 	 $annots .= ' /Q '.intval($pl['opt']['q']);
8497  	 	 	 	 	 	 	 }
8498  	 	 	 	 	 	 	 if (isset($pl['opt']['rc'])) {
8499  	 	 	 	 	 	 	 	 $annots .= ' /RC '.$this->_textstring($pl['opt']['rc'], $annot_obj_id);
8500  	 	 	 	 	 	 	 }
8501  	 	 	 	 	 	 	 if (isset($pl['opt']['ds'])) {
8502  	 	 	 	 	 	 	 	 $annots .= ' /DS '.$this->_textstring($pl['opt']['ds'], $annot_obj_id);
8503  	 	 	 	 	 	 	 }
8504  	 	 	 	 	 	 	 if (isset($pl['opt']['cl']) AND is_array($pl['opt']['cl'])) {
8505  	 	 	 	 	 	 	 	 $annots .= ' /CL [';
8506  	 	 	 	 	 	 	 	 foreach ($pl['opt']['cl'] as $cl) {
8507  	 	 	 	 	 	 	 	 	 $annots .= sprintf('%F ', $cl * $this->k);
8508  	 	 	 	 	 	 	 	 }
8509  	 	 	 	 	 	 	 	 $annots .= ']';
8510  	 	 	 	 	 	 	 }
8511  	 	 	 	 	 	 	 $tfit = array('FreeText', 'FreeTextCallout', 'FreeTextTypeWriter');
8512  	 	 	 	 	 	 	 if (isset($pl['opt']['it']) AND in_array($pl['opt']['it'], $tfit)) {
8513  	 	 	 	 	 	 	 	 $annots .= ' /IT /'.$pl['opt']['it'];
8514  	 	 	 	 	 	 	 }
8515  	 	 	 	 	 	 	 if (isset($pl['opt']['rd']) AND is_array($pl['opt']['rd'])) {
8516  	 	 	 	 	 	 	 	 $l = $pl['opt']['rd'][0] * $this->k;
8517  	 	 	 	 	 	 	 	 $r = $pl['opt']['rd'][1] * $this->k;
8518  	 	 	 	 	 	 	 	 $t = $pl['opt']['rd'][2] * $this->k;
8519  	 	 	 	 	 	 	 	 $b = $pl['opt']['rd'][3] * $this->k;
8520  	 	 	 	 	 	 	 	 $annots .= ' /RD ['.sprintf('%F %F %F %F', $l, $r, $t, $b).']';
8521  	 	 	 	 	 	 	 }
8522  	 	 	 	 	 	 	 if (isset($pl['opt']['le']) AND in_array($pl['opt']['le'], $lineendings)) {
8523  	 	 	 	 	 	 	 	 $annots .= ' /LE /'.$pl['opt']['le'];
8524  	 	 	 	 	 	 	 }
8525  	 	 	 	 	 	 	 break;
8526  	 	 	 	 	 	 }
8527  	 	 	 	 	 	 case 'line': {
8528  	 	 	 	 	 	 	 break;
8529  	 	 	 	 	 	 }
8530  	 	 	 	 	 	 case 'square': {
8531  	 	 	 	 	 	 	 break;
8532  	 	 	 	 	 	 }
8533  	 	 	 	 	 	 case 'circle': {
8534  	 	 	 	 	 	 	 break;
8535  	 	 	 	 	 	 }
8536  	 	 	 	 	 	 case 'polygon': {
8537  	 	 	 	 	 	 	 break;
8538  	 	 	 	 	 	 }
8539  	 	 	 	 	 	 case 'polyline': {
8540  	 	 	 	 	 	 	 break;
8541  	 	 	 	 	 	 }
8542  	 	 	 	 	 	 case 'highlight': {
8543  	 	 	 	 	 	 	 break;
8544  	 	 	 	 	 	 }
8545  	 	 	 	 	 	 case 'underline': {
8546  	 	 	 	 	 	 	 break;
8547  	 	 	 	 	 	 }
8548  	 	 	 	 	 	 case 'squiggly': {
8549  	 	 	 	 	 	 	 break;
8550  	 	 	 	 	 	 }
8551  	 	 	 	 	 	 case 'strikeout': {
8552  	 	 	 	 	 	 	 break;
8553  	 	 	 	 	 	 }
8554  	 	 	 	 	 	 case 'stamp': {
8555  	 	 	 	 	 	 	 break;
8556  	 	 	 	 	 	 }
8557  	 	 	 	 	 	 case 'caret': {
8558  	 	 	 	 	 	 	 break;
8559  	 	 	 	 	 	 }
8560  	 	 	 	 	 	 case 'ink': {
8561  	 	 	 	 	 	 	 break;
8562  	 	 	 	 	 	 }
8563  	 	 	 	 	 	 case 'popup': {
8564  	 	 	 	 	 	 	 break;
8565  	 	 	 	 	 	 }
8566  	 	 	 	 	 	 case 'fileattachment': {
8567  	 	 	 	 	 	 	 if ($this->pdfa_mode && $this->pdfa_version != 3) {
8568  	 	 	 	 	 	 	 	 // embedded files are not allowed in PDF/A mode version 1 and 2
8569  	 	 	 	 	 	 	 	 break;
8570  	 	 	 	 	 	 	 }
8571  	 	 	 	 	 	 	 if (!isset($pl['opt']['fs'])) {
8572  	 	 	 	 	 	 	 	 break;
8573  	 	 	 	 	 	 	 }
8574  	 	 	 	 	 	 	 $filename = basename($pl['opt']['fs']);
8575  	 	 	 	 	 	 	 if (isset($this->embeddedfiles[$filename]['f'])) {
8576  	 	 	 	 	 	 	 	 $annots .= ' /FS '.$this->embeddedfiles[$filename]['f'].' 0 R';
8577  	 	 	 	 	 	 	 	 $iconsapp = array('Graph', 'Paperclip', 'PushPin', 'Tag');
8578  	 	 	 	 	 	 	 	 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8579  	 	 	 	 	 	 	 	 	 $annots .= ' /Name /'.$pl['opt']['name'];
8580  	 	 	 	 	 	 	 	 } else {
8581  	 	 	 	 	 	 	 	 	 $annots .= ' /Name /PushPin';
8582  	 	 	 	 	 	 	 	 }
8583  	 	 	 	 	 	 	 	 // index (zero-based) of the annotation in the Annots array of this page
8584  	 	 	 	 	 	 	 	 $this->embeddedfiles[$filename]['a'] = $key;
8585  	 	 	 	 	 	 	 }
8586  	 	 	 	 	 	 	 break;
8587  	 	 	 	 	 	 }
8588  	 	 	 	 	 	 case 'sound': {
8589  	 	 	 	 	 	 	 if (!isset($pl['opt']['fs'])) {
8590  	 	 	 	 	 	 	 	 break;
8591  	 	 	 	 	 	 	 }
8592  	 	 	 	 	 	 	 $filename = basename($pl['opt']['fs']);
8593  	 	 	 	 	 	 	 if (isset($this->embeddedfiles[$filename]['f'])) {
8594  	 	 	 	 	 	 	 	 // ... TO BE COMPLETED ...
8595  	 	 	 	 	 	 	 	 // /R /C /B /E /CO /CP
8596  	 	 	 	 	 	 	 	 $annots .= ' /Sound '.$this->embeddedfiles[$filename]['f'].' 0 R';
8597  	 	 	 	 	 	 	 	 $iconsapp = array('Speaker', 'Mic');
8598  	 	 	 	 	 	 	 	 if (isset($pl['opt']['name']) AND in_array($pl['opt']['name'], $iconsapp)) {
8599  	 	 	 	 	 	 	 	 	 $annots .= ' /Name /'.$pl['opt']['name'];
8600  	 	 	 	 	 	 	 	 } else {
8601  	 	 	 	 	 	 	 	 	 $annots .= ' /Name /Speaker';
8602  	 	 	 	 	 	 	 	 }
8603  	 	 	 	 	 	 	 }
8604  	 	 	 	 	 	 	 break;
8605  	 	 	 	 	 	 }
8606  	 	 	 	 	 	 case 'movie': {
8607  	 	 	 	 	 	 	 break;
8608  	 	 	 	 	 	 }
8609  	 	 	 	 	 	 case 'widget': {
8610  	 	 	 	 	 	 	 $hmode = array('N', 'I', 'O', 'P', 'T');
8611  	 	 	 	 	 	 	 if (isset($pl['opt']['h']) AND in_array($pl['opt']['h'], $hmode)) {
8612  	 	 	 	 	 	 	 	 $annots .= ' /H /'.$pl['opt']['h'];
8613  	 	 	 	 	 	 	 }
8614  	 	 	 	 	 	 	 if (isset($pl['opt']['mk']) AND (is_array($pl['opt']['mk'])) AND !empty($pl['opt']['mk'])) {
8615  	 	 	 	 	 	 	 	 $annots .= ' /MK <<';
8616  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['r'])) {
8617  	 	 	 	 	 	 	 	 	 $annots .= ' /R '.$pl['opt']['mk']['r'];
8618  	 	 	 	 	 	 	 	 }
8619  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['bc']) AND (is_array($pl['opt']['mk']['bc']))) {
8620  	 	 	 	 	 	 	 	 	 $annots .= ' /BC '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bc']);
8621  	 	 	 	 	 	 	 	 }
8622  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['bg']) AND (is_array($pl['opt']['mk']['bg']))) {
8623  	 	 	 	 	 	 	 	 	 $annots .= ' /BG '.TCPDF_COLORS::getColorStringFromArray($pl['opt']['mk']['bg']);
8624  	 	 	 	 	 	 	 	 }
8625  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['ca'])) {
8626  	 	 	 	 	 	 	 	 	 $annots .= ' /CA '.$pl['opt']['mk']['ca'];
8627  	 	 	 	 	 	 	 	 }
8628  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['rc'])) {
8629  	 	 	 	 	 	 	 	 	 $annots .= ' /RC '.$pl['opt']['mk']['rc'];
8630  	 	 	 	 	 	 	 	 }
8631  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['ac'])) {
8632  	 	 	 	 	 	 	 	 	 $annots .= ' /AC '.$pl['opt']['mk']['ac'];
8633  	 	 	 	 	 	 	 	 }
8634  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['i'])) {
8635  	 	 	 	 	 	 	 	 	 $info = $this->getImageBuffer($pl['opt']['mk']['i']);
8636  	 	 	 	 	 	 	 	 	 if ($info !== false) {
8637  	 	 	 	 	 	 	 	 	 	 $annots .= ' /I '.$info['n'].' 0 R';
8638  	 	 	 	 	 	 	 	 	 }
8639  	 	 	 	 	 	 	 	 }
8640  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['ri'])) {
8641  	 	 	 	 	 	 	 	 	 $info = $this->getImageBuffer($pl['opt']['mk']['ri']);
8642  	 	 	 	 	 	 	 	 	 if ($info !== false) {
8643  	 	 	 	 	 	 	 	 	 	 $annots .= ' /RI '.$info['n'].' 0 R';
8644  	 	 	 	 	 	 	 	 	 }
8645  	 	 	 	 	 	 	 	 }
8646  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['ix'])) {
8647  	 	 	 	 	 	 	 	 	 $info = $this->getImageBuffer($pl['opt']['mk']['ix']);
8648  	 	 	 	 	 	 	 	 	 if ($info !== false) {
8649  	 	 	 	 	 	 	 	 	 	 $annots .= ' /IX '.$info['n'].' 0 R';
8650  	 	 	 	 	 	 	 	 	 }
8651  	 	 	 	 	 	 	 	 }
8652  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['if']) AND (is_array($pl['opt']['mk']['if'])) AND !empty($pl['opt']['mk']['if'])) {
8653  	 	 	 	 	 	 	 	 	 $annots .= ' /IF <<';
8654  	 	 	 	 	 	 	 	 	 $if_sw = array('A', 'B', 'S', 'N');
8655  	 	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['if']['sw']) AND in_array($pl['opt']['mk']['if']['sw'], $if_sw)) {
8656  	 	 	 	 	 	 	 	 	 	 $annots .= ' /SW /'.$pl['opt']['mk']['if']['sw'];
8657  	 	 	 	 	 	 	 	 	 }
8658  	 	 	 	 	 	 	 	 	 $if_s = array('A', 'P');
8659  	 	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['if']['s']) AND in_array($pl['opt']['mk']['if']['s'], $if_s)) {
8660  	 	 	 	 	 	 	 	 	 	 $annots .= ' /S /'.$pl['opt']['mk']['if']['s'];
8661  	 	 	 	 	 	 	 	 	 }
8662  	 	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['if']['a']) AND (is_array($pl['opt']['mk']['if']['a'])) AND !empty($pl['opt']['mk']['if']['a'])) {
8663  	 	 	 	 	 	 	 	 	 	 $annots .= sprintf(' /A [%F %F]', $pl['opt']['mk']['if']['a'][0], $pl['opt']['mk']['if']['a'][1]);
8664  	 	 	 	 	 	 	 	 	 }
8665  	 	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['if']['fb']) AND ($pl['opt']['mk']['if']['fb'])) {
8666  	 	 	 	 	 	 	 	 	 	 $annots .= ' /FB true';
8667  	 	 	 	 	 	 	 	 	 }
8668  	 	 	 	 	 	 	 	 	 $annots .= '>>';
8669  	 	 	 	 	 	 	 	 }
8670  	 	 	 	 	 	 	 	 if (isset($pl['opt']['mk']['tp']) AND ($pl['opt']['mk']['tp'] >= 0) AND ($pl['opt']['mk']['tp'] <= 6)) {
8671  	 	 	 	 	 	 	 	 	 $annots .= ' /TP '.intval($pl['opt']['mk']['tp']);
8672  	 	 	 	 	 	 	 	 }
8673  	 	 	 	 	 	 	 	 $annots .= '>>';
8674  	 	 	 	 	 	 	 } // end MK
8675  	 	 	 	 	 	 	 // --- Entries for field dictionaries ---
8676  	 	 	 	 	 	 	 if (isset($this->radiobutton_groups[$n][$pl['txt']])) {
8677  	 	 	 	 	 	 	 	 // set parent
8678  	 	 	 	 	 	 	 	 $annots .= ' /Parent '.$this->radiobutton_groups[$n][$pl['txt']].' 0 R';
8679  	 	 	 	 	 	 	 }
8680  	 	 	 	 	 	 	 if (isset($pl['opt']['t']) AND is_string($pl['opt']['t'])) {
8681  	 	 	 	 	 	 	 	 $annots .= ' /T '.$this->_datastring($pl['opt']['t'], $annot_obj_id);
8682  	 	 	 	 	 	 	 }
8683  	 	 	 	 	 	 	 if (isset($pl['opt']['tu']) AND is_string($pl['opt']['tu'])) {
8684  	 	 	 	 	 	 	 	 $annots .= ' /TU '.$this->_datastring($pl['opt']['tu'], $annot_obj_id);
8685  	 	 	 	 	 	 	 }
8686  	 	 	 	 	 	 	 if (isset($pl['opt']['tm']) AND is_string($pl['opt']['tm'])) {
8687  	 	 	 	 	 	 	 	 $annots .= ' /TM '.$this->_datastring($pl['opt']['tm'], $annot_obj_id);
8688  	 	 	 	 	 	 	 }
8689  	 	 	 	 	 	 	 if (isset($pl['opt']['ff'])) {
8690  	 	 	 	 	 	 	 	 if (is_array($pl['opt']['ff'])) {
8691  	 	 	 	 	 	 	 	 	 // array of bit settings
8692  	 	 	 	 	 	 	 	 	 $flag = 0;
8693  	 	 	 	 	 	 	 	 	 foreach($pl['opt']['ff'] as $val) {
8694  	 	 	 	 	 	 	 	 	 	 $flag += 1 << ($val - 1);
8695  	 	 	 	 	 	 	 	 	 }
8696  	 	 	 	 	 	 	 	 } else {
8697  	 	 	 	 	 	 	 	 	 $flag = intval($pl['opt']['ff']);
8698  	 	 	 	 	 	 	 	 }
8699  	 	 	 	 	 	 	 	 $annots .= ' /Ff '.$flag;
8700  	 	 	 	 	 	 	 }
8701  	 	 	 	 	 	 	 if (isset($pl['opt']['maxlen'])) {
8702  	 	 	 	 	 	 	 	 $annots .= ' /MaxLen '.intval($pl['opt']['maxlen']);
8703  	 	 	 	 	 	 	 }
8704  	 	 	 	 	 	 	 if (isset($pl['opt']['v'])) {
8705  	 	 	 	 	 	 	 	 $annots .= ' /V';
8706  	 	 	 	 	 	 	 	 if (is_array($pl['opt']['v'])) {
8707  	 	 	 	 	 	 	 	 	 foreach ($pl['opt']['v'] AS $optval) {
8708  	 	 	 	 	 	 	 	 	 	 if (is_float($optval)) {
8709  	 	 	 	 	 	 	 	 	 	 	 $optval = sprintf('%F', $optval);
8710  	 	 	 	 	 	 	 	 	 	 }
8711  	 	 	 	 	 	 	 	 	 	 $annots .= ' '.$optval;
8712  	 	 	 	 	 	 	 	 	 }
8713  	 	 	 	 	 	 	 	 } else {
8714  	 	 	 	 	 	 	 	 	 $annots .= ' '.$this->_textstring($pl['opt']['v'], $annot_obj_id);
8715  	 	 	 	 	 	 	 	 }
8716  	 	 	 	 	 	 	 }
8717  	 	 	 	 	 	 	 if (isset($pl['opt']['dv'])) {
8718  	 	 	 	 	 	 	 	 $annots .= ' /DV';
8719  	 	 	 	 	 	 	 	 if (is_array($pl['opt']['dv'])) {
8720  	 	 	 	 	 	 	 	 	 foreach ($pl['opt']['dv'] AS $optval) {
8721  	 	 	 	 	 	 	 	 	 	 if (is_float($optval)) {
8722  	 	 	 	 	 	 	 	 	 	 	 $optval = sprintf('%F', $optval);
8723  	 	 	 	 	 	 	 	 	 	 }
8724  	 	 	 	 	 	 	 	 	 	 $annots .= ' '.$optval;
8725  	 	 	 	 	 	 	 	 	 }
8726  	 	 	 	 	 	 	 	 } else {
8727  	 	 	 	 	 	 	 	 	 $annots .= ' '.$this->_textstring($pl['opt']['dv'], $annot_obj_id);
8728  	 	 	 	 	 	 	 	 }
8729  	 	 	 	 	 	 	 }
8730  	 	 	 	 	 	 	 if (isset($pl['opt']['rv'])) {
8731  	 	 	 	 	 	 	 	 $annots .= ' /RV';
8732  	 	 	 	 	 	 	 	 if (is_array($pl['opt']['rv'])) {
8733  	 	 	 	 	 	 	 	 	 foreach ($pl['opt']['rv'] AS $optval) {
8734  	 	 	 	 	 	 	 	 	 	 if (is_float($optval)) {
8735  	 	 	 	 	 	 	 	 	 	 	 $optval = sprintf('%F', $optval);
8736  	 	 	 	 	 	 	 	 	 	 }
8737  	 	 	 	 	 	 	 	 	 	 $annots .= ' '.$optval;
8738  	 	 	 	 	 	 	 	 	 }
8739  	 	 	 	 	 	 	 	 } else {
8740  	 	 	 	 	 	 	 	 	 $annots .= ' '.$this->_textstring($pl['opt']['rv'], $annot_obj_id);
8741  	 	 	 	 	 	 	 	 }
8742  	 	 	 	 	 	 	 }
8743  	 	 	 	 	 	 	 if (isset($pl['opt']['a']) AND !empty($pl['opt']['a'])) {
8744  	 	 	 	 	 	 	 	 $annots .= ' /A << '.$pl['opt']['a'].' >>';
8745  	 	 	 	 	 	 	 }
8746  	 	 	 	 	 	 	 if (isset($pl['opt']['aa']) AND !empty($pl['opt']['aa'])) {
8747  	 	 	 	 	 	 	 	 $annots .= ' /AA << '.$pl['opt']['aa'].' >>';
8748  	 	 	 	 	 	 	 }
8749  	 	 	 	 	 	 	 if (isset($pl['opt']['da']) AND !empty($pl['opt']['da'])) {
8750  	 	 	 	 	 	 	 	 $annots .= ' /DA ('.$pl['opt']['da'].')';
8751  	 	 	 	 	 	 	 }
8752  	 	 	 	 	 	 	 if (isset($pl['opt']['q']) AND ($pl['opt']['q'] >= 0) AND ($pl['opt']['q'] <= 2)) {
8753  	 	 	 	 	 	 	 	 $annots .= ' /Q '.intval($pl['opt']['q']);
8754  	 	 	 	 	 	 	 }
8755  	 	 	 	 	 	 	 if (isset($pl['opt']['opt']) AND (is_array($pl['opt']['opt'])) AND !empty($pl['opt']['opt'])) {
8756  	 	 	 	 	 	 	 	 $annots .= ' /Opt [';
8757  	 	 	 	 	 	 	 	 foreach($pl['opt']['opt'] AS $copt) {
8758  	 	 	 	 	 	 	 	 	 if (is_array($copt)) {
8759  	 	 	 	 	 	 	 	 	 	 $annots .= ' ['.$this->_textstring($copt[0], $annot_obj_id).' '.$this->_textstring($copt[1], $annot_obj_id).']';
8760  	 	 	 	 	 	 	 	 	 } else {
8761  	 	 	 	 	 	 	 	 	 	 $annots .= ' '.$this->_textstring($copt, $annot_obj_id);
8762  	 	 	 	 	 	 	 	 	 }
8763  	 	 	 	 	 	 	 	 }
8764  	 	 	 	 	 	 	 	 $annots .= ']';
8765  	 	 	 	 	 	 	 }
8766  	 	 	 	 	 	 	 if (isset($pl['opt']['ti'])) {
8767  	 	 	 	 	 	 	 	 $annots .= ' /TI '.intval($pl['opt']['ti']);
8768  	 	 	 	 	 	 	 }
8769  	 	 	 	 	 	 	 if (isset($pl['opt']['i']) AND (is_array($pl['opt']['i'])) AND !empty($pl['opt']['i'])) {
8770  	 	 	 	 	 	 	 	 $annots .= ' /I [';
8771  	 	 	 	 	 	 	 	 foreach($pl['opt']['i'] AS $copt) {
8772  	 	 	 	 	 	 	 	 	 $annots .= intval($copt).' ';
8773  	 	 	 	 	 	 	 	 }
8774  	 	 	 	 	 	 	 	 $annots .= ']';
8775  	 	 	 	 	 	 	 }
8776  	 	 	 	 	 	 	 break;
8777  	 	 	 	 	 	 }
8778  	 	 	 	 	 	 case 'screen': {
8779  	 	 	 	 	 	 	 break;
8780  	 	 	 	 	 	 }
8781  	 	 	 	 	 	 case 'printermark': {
8782  	 	 	 	 	 	 	 break;
8783  	 	 	 	 	 	 }
8784  	 	 	 	 	 	 case 'trapnet': {
8785  	 	 	 	 	 	 	 break;
8786  	 	 	 	 	 	 }
8787  	 	 	 	 	 	 case 'watermark': {
8788  	 	 	 	 	 	 	 break;
8789  	 	 	 	 	 	 }
8790  	 	 	 	 	 	 case '3d': {
8791  	 	 	 	 	 	 	 break;
8792  	 	 	 	 	 	 }
8793  	 	 	 	 	 	 default: {
8794  	 	 	 	 	 	 	 break;
8795  	 	 	 	 	 	 }
8796  	 	 	 	 	 }
8797  	 	 	 	 	 $annots .= '>>';
8798  	 	 	 	 	 // create new annotation object
8799  	 	 	 	 	 $this->_out($this->_getobj($annot_obj_id)."\n".$annots."\n".'endobj');
8800  	 	 	 	 	 if ($formfield AND !isset($this->radiobutton_groups[$n][$pl['txt']])) {
8801  	 	 	 	 	 	 // store reference of form object
8802  	 	 	 	 	 	 $this->form_obj_id[] = $annot_obj_id;
8803  	 	 	 	 	 }
8804  	 	 	 	 }
8805  	 	 	 }
8806  	 	 } // end for each page
8807  	 }
8808  
8809  	 /**
8810  	  * Put appearance streams XObject used to define annotation's appearance states.
8811  	  * @param $w (int) annotation width
8812  	  * @param $h (int) annotation height
8813  	  * @param $stream (string) appearance stream
8814  	  * @return int object ID
8815  	  * @protected
8816  	  * @since 4.8.001 (2009-09-09)
8817  	  */
8818  	protected function _putAPXObject($w=0, $h=0, $stream='') {
8819  	 	 $stream = trim($stream);
8820  	 	 $out = $this->_getobj()."\n";
8821  	 	 $this->xobjects['AX'.$this->n] = array('n' => $this->n);
8822  	 	 $out .= '<<';
8823  	 	 $out .= ' /Type /XObject';
8824  	 	 $out .= ' /Subtype /Form';
8825  	 	 $out .= ' /FormType 1';
8826  	 	 if ($this->compress) {
8827  	 	 	 $stream = gzcompress($stream);
8828  	 	 	 $out .= ' /Filter /FlateDecode';
8829  	 	 }
8830  	 	 $rect = sprintf('%F %F', $w, $h);
8831  	 	 $out .= ' /BBox [0 0 '.$rect.']';
8832  	 	 $out .= ' /Matrix [1 0 0 1 0 0]';
8833  	 	 $out .= ' /Resources 2 0 R';
8834  	 	 $stream = $this->_getrawstream($stream);
8835  	 	 $out .= ' /Length '.strlen($stream);
8836  	 	 $out .= ' >>';
8837  	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
8838  	 	 $out .= "\n".'endobj';
8839  	 	 $this->_out($out);
8840  	 	 return $this->n;
8841  	 }
8842  
8843  	 /**
8844  	  * Output fonts.
8845  	  * @author Nicola Asuni
8846  	  * @protected
8847  	  */
8848  	protected function _putfonts() {
8849  	 	 $nf = $this->n;
8850  	 	 foreach ($this->diffs as $diff) {
8851  	 	 	 //Encodings
8852  	 	 	 $this->_newobj();
8853  	 	 	 $this->_out('<< /Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences ['.$diff.'] >>'."\n".'endobj');
8854  	 	 }
8855  	 	 $mqr = TCPDF_STATIC::get_mqr();
8856  	 	 TCPDF_STATIC::set_mqr(false);
8857  	 	 foreach ($this->FontFiles as $file => $info) {
8858  	 	 	 // search and get font file to embedd
8859  	 	 	 $fontfile = TCPDF_FONTS::getFontFullPath($file, $info['fontdir']);
8860  	 	 	 if (!TCPDF_STATIC::empty_string($fontfile)) {
8861  	 	 	 	 $font = file_get_contents($fontfile);
8862  	 	 	 	 $compressed = (substr($file, -2) == '.z');
8863  	 	 	 	 if ((!$compressed) AND (isset($info['length2']))) {
8864  	 	 	 	 	 $header = (ord($font[0]) == 128);
8865  	 	 	 	 	 if ($header) {
8866  	 	 	 	 	 	 // strip first binary header
8867  	 	 	 	 	 	 $font = substr($font, 6);
8868  	 	 	 	 	 }
8869  	 	 	 	 	 if ($header AND (ord($font[$info['length1']]) == 128)) {
8870  	 	 	 	 	 	 // strip second binary header
8871  	 	 	 	 	 	 $font = substr($font, 0, $info['length1']).substr($font, ($info['length1'] + 6));
8872  	 	 	 	 	 }
8873  	 	 	 	 } elseif ($info['subset'] AND ((!$compressed) OR ($compressed AND function_exists('gzcompress')))) {
8874  	 	 	 	 	 if ($compressed) {
8875  	 	 	 	 	 	 // uncompress font
8876  	 	 	 	 	 	 $font = gzuncompress($font);
8877  	 	 	 	 	 }
8878  	 	 	 	 	 // merge subset characters
8879  	 	 	 	 	 $subsetchars = array(); // used chars
8880  	 	 	 	 	 foreach ($info['fontkeys'] as $fontkey) {
8881  	 	 	 	 	 	 $fontinfo = $this->getFontBuffer($fontkey);
8882  	 	 	 	 	 	 $subsetchars += $fontinfo['subsetchars'];
8883  	 	 	 	 	 }
8884  	 	 	 	 	 // rebuild a font subset
8885  	 	 	 	 	 $font = TCPDF_FONTS::_getTrueTypeFontSubset($font, $subsetchars);
8886  	 	 	 	 	 // calculate new font length
8887  	 	 	 	 	 $info['length1'] = strlen($font);
8888  	 	 	 	 	 if ($compressed) {
8889  	 	 	 	 	 	 // recompress font
8890  	 	 	 	 	 	 $font = gzcompress($font);
8891  	 	 	 	 	 }
8892  	 	 	 	 }
8893  	 	 	 	 $this->_newobj();
8894  	 	 	 	 $this->FontFiles[$file]['n'] = $this->n;
8895  	 	 	 	 $stream = $this->_getrawstream($font);
8896  	 	 	 	 $out = '<< /Length '.strlen($stream);
8897  	 	 	 	 if ($compressed) {
8898  	 	 	 	 	 $out .= ' /Filter /FlateDecode';
8899  	 	 	 	 }
8900  	 	 	 	 $out .= ' /Length1 '.$info['length1'];
8901  	 	 	 	 if (isset($info['length2'])) {
8902  	 	 	 	 	 $out .= ' /Length2 '.$info['length2'].' /Length3 0';
8903  	 	 	 	 }
8904  	 	 	 	 $out .= ' >>';
8905  	 	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
8906  	 	 	 	 $out .= "\n".'endobj';
8907  	 	 	 	 $this->_out($out);
8908  	 	 	 }
8909  	 	 }
8910  	 	 TCPDF_STATIC::set_mqr($mqr);
8911  	 	 foreach ($this->fontkeys as $k) {
8912  	 	 	 //Font objects
8913  	 	 	 $font = $this->getFontBuffer($k);
8914  	 	 	 $type = $font['type'];
8915  	 	 	 $name = $font['name'];
8916  	 	 	 if ($type == 'core') {
8917  	 	 	 	 // standard core font
8918  	 	 	 	 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8919  	 	 	 	 $out .= '<</Type /Font';
8920  	 	 	 	 $out .= ' /Subtype /Type1';
8921  	 	 	 	 $out .= ' /BaseFont /'.$name;
8922  	 	 	 	 $out .= ' /Name /F'.$font['i'];
8923  	 	 	 	 if ((strtolower($name) != 'symbol') AND (strtolower($name) != 'zapfdingbats')) {
8924  	 	 	 	 	 $out .= ' /Encoding /WinAnsiEncoding';
8925  	 	 	 	 }
8926  	 	 	 	 if ($k == 'helvetica') {
8927  	 	 	 	 	 // add default font for annotations
8928  	 	 	 	 	 $this->annotation_fonts[$k] = $font['i'];
8929  	 	 	 	 }
8930  	 	 	 	 $out .= ' >>';
8931  	 	 	 	 $out .= "\n".'endobj';
8932  	 	 	 	 $this->_out($out);
8933  	 	 	 } elseif (($type == 'Type1') OR ($type == 'TrueType')) {
8934  	 	 	 	 // additional Type1 or TrueType font
8935  	 	 	 	 $out = $this->_getobj($this->font_obj_ids[$k])."\n";
8936  	 	 	 	 $out .= '<</Type /Font';
8937  	 	 	 	 $out .= ' /Subtype /'.$type;
8938  	 	 	 	 $out .= ' /BaseFont /'.$name;
8939  	 	 	 	 $out .= ' /Name /F'.$font['i'];
8940  	 	 	 	 $out .= ' /FirstChar 32 /LastChar 255';
8941  	 	 	 	 $out .= ' /Widths '.($this->n + 1).' 0 R';
8942  	 	 	 	 $out .= ' /FontDescriptor '.($this->n + 2).' 0 R';
8943  	 	 	 	 if ($font['enc']) {
8944  	 	 	 	 	 if (isset($font['diff'])) {
8945  	 	 	 	 	 	 $out .= ' /Encoding '.($nf + $font['diff']).' 0 R';
8946  	 	 	 	 	 } else {
8947  	 	 	 	 	 	 $out .= ' /Encoding /WinAnsiEncoding';
8948  	 	 	 	 	 }
8949  	 	 	 	 }
8950  	 	 	 	 $out .= ' >>';
8951  	 	 	 	 $out .= "\n".'endobj';
8952  	 	 	 	 $this->_out($out);
8953  	 	 	 	 // Widths
8954  	 	 	 	 $this->_newobj();
8955  	 	 	 	 $s = '[';
8956  	 	 	 	 for ($i = 32; $i < 256; ++$i) {
8957  	 	 	 	 	 if (isset($font['cw'][$i])) {
8958  	 	 	 	 	 	 $s .= $font['cw'][$i].' ';
8959  	 	 	 	 	 } else {
8960  	 	 	 	 	 	 $s .= $font['dw'].' ';
8961  	 	 	 	 	 }
8962  	 	 	 	 }
8963  	 	 	 	 $s .= ']';
8964  	 	 	 	 $s .= "\n".'endobj';
8965  	 	 	 	 $this->_out($s);
8966  	 	 	 	 //Descriptor
8967  	 	 	 	 $this->_newobj();
8968  	 	 	 	 $s = '<</Type /FontDescriptor /FontName /'.$name;
8969  	 	 	 	 foreach ($font['desc'] as $fdk => $fdv) {
8970  	 	 	 	 	 if (is_float($fdv)) {
8971  	 	 	 	 	 	 $fdv = sprintf('%F', $fdv);
8972  	 	 	 	 	 }
8973  	 	 	 	 	 $s .= ' /'.$fdk.' '.$fdv.'';
8974  	 	 	 	 }
8975  	 	 	 	 if (!TCPDF_STATIC::empty_string($font['file'])) {
8976  	 	 	 	 	 $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R';
8977  	 	 	 	 }
8978  	 	 	 	 $s .= '>>';
8979  	 	 	 	 $s .= "\n".'endobj';
8980  	 	 	 	 $this->_out($s);
8981  	 	 	 } else {
8982  	 	 	 	 // additional types
8983  	 	 	 	 $mtd = '_put'.strtolower($type);
8984  	 	 	 	 if (!method_exists($this, $mtd)) {
8985  	 	 	 	 	 $this->Error('Unsupported font type: '.$type);
8986  	 	 	 	 }
8987  	 	 	 	 $this->$mtd($font);
8988  	 	 	 }
8989  	 	 }
8990  	 }
8991  
8992  	 /**
8993  	  * Adds unicode fonts.<br>
8994  	  * Based on PDF Reference 1.3 (section 5)
8995  	  * @param $font (array) font data
8996  	  * @protected
8997  	  * @author Nicola Asuni
8998  	  * @since 1.52.0.TC005 (2005-01-05)
8999  	  */
9000  	protected function _puttruetypeunicode($font) {
9001  	 	 $fontname = '';
9002  	 	 if ($font['subset']) {
9003  	 	 	 // change name for font subsetting
9004  	 	 	 $subtag = sprintf('%06u', $font['i']);
9005  	 	 	 $subtag = strtr($subtag, '0123456789', 'ABCDEFGHIJ');
9006  	 	 	 $fontname .= $subtag.'+';
9007  	 	 }
9008  	 	 $fontname .= $font['name'];
9009  	 	 // Type0 Font
9010  	 	 // A composite font composed of other fonts, organized hierarchically
9011  	 	 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9012  	 	 $out .= '<< /Type /Font';
9013  	 	 $out .= ' /Subtype /Type0';
9014  	 	 $out .= ' /BaseFont /'.$fontname;
9015  	 	 $out .= ' /Name /F'.$font['i'];
9016  	 	 $out .= ' /Encoding /'.$font['enc'];
9017  	 	 $out .= ' /ToUnicode '.($this->n + 1).' 0 R';
9018  	 	 $out .= ' /DescendantFonts ['.($this->n + 2).' 0 R]';
9019  	 	 $out .= ' >>';
9020  	 	 $out .= "\n".'endobj';
9021  	 	 $this->_out($out);
9022  	 	 // ToUnicode map for Identity-H
9023  	 	 $stream = TCPDF_FONT_DATA::$uni_identity_h;
9024  	 	 // ToUnicode Object
9025  	 	 $this->_newobj();
9026  	 	 $stream = ($this->compress) ? gzcompress($stream) : $stream;
9027  	 	 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9028  	 	 $stream = $this->_getrawstream($stream);
9029  	 	 $this->_out('<<'.$filter.'/Length '.strlen($stream).'>> stream'."\n".$stream."\n".'endstream'."\n".'endobj');
9030  	 	 // CIDFontType2
9031  	 	 // A CIDFont whose glyph descriptions are based on TrueType font technology
9032  	 	 $oid = $this->_newobj();
9033  	 	 $out = '<< /Type /Font';
9034  	 	 $out .= ' /Subtype /CIDFontType2';
9035  	 	 $out .= ' /BaseFont /'.$fontname;
9036  	 	 // A dictionary containing entries that define the character collection of the CIDFont.
9037  	 	 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9038  	 	 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9039  	 	 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9040  	 	 $out .= ' /CIDSystemInfo << '.$cidinfo.' >>';
9041  	 	 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9042  	 	 $out .= ' /DW '.$font['dw']; // default width
9043  	 	 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, 0);
9044  	 	 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
9045  	 	 	 $out .= "\n".'/CIDToGIDMap '.($this->n + 2).' 0 R';
9046  	 	 }
9047  	 	 $out .= ' >>';
9048  	 	 $out .= "\n".'endobj';
9049  	 	 $this->_out($out);
9050  	 	 // Font descriptor
9051  	 	 // A font descriptor describing the CIDFont default metrics other than its glyph widths
9052  	 	 $this->_newobj();
9053  	 	 $out = '<< /Type /FontDescriptor';
9054  	 	 $out .= ' /FontName /'.$fontname;
9055  	 	 foreach ($font['desc'] as $key => $value) {
9056  	 	 	 if (is_float($value)) {
9057  	 	 	 	 $value = sprintf('%F', $value);
9058  	 	 	 }
9059  	 	 	 $out .= ' /'.$key.' '.$value;
9060  	 	 }
9061  	 	 $fontdir = false;
9062  	 	 if (!TCPDF_STATIC::empty_string($font['file'])) {
9063  	 	 	 // A stream containing a TrueType font
9064  	 	 	 $out .= ' /FontFile2 '.$this->FontFiles[$font['file']]['n'].' 0 R';
9065  	 	 	 $fontdir = $this->FontFiles[$font['file']]['fontdir'];
9066  	 	 }
9067  	 	 $out .= ' >>';
9068  	 	 $out .= "\n".'endobj';
9069  	 	 $this->_out($out);
9070  	 	 if (isset($font['ctg']) AND (!TCPDF_STATIC::empty_string($font['ctg']))) {
9071  	 	 	 $this->_newobj();
9072  	 	 	 // Embed CIDToGIDMap
9073  	 	 	 // A specification of the mapping from CIDs to glyph indices
9074  	 	 	 // search and get CTG font file to embedd
9075  	 	 	 $ctgfile = strtolower($font['ctg']);
9076  	 	 	 // search and get ctg font file to embedd
9077  	 	 	 $fontfile = TCPDF_FONTS::getFontFullPath($ctgfile, $fontdir);
9078  	 	 	 if (TCPDF_STATIC::empty_string($fontfile)) {
9079  	 	 	 	 $this->Error('Font file not found: '.$ctgfile);
9080  	 	 	 }
9081  	 	 	 $stream = $this->_getrawstream(file_get_contents($fontfile));
9082  	 	 	 $out = '<< /Length '.strlen($stream).'';
9083  	 	 	 if (substr($fontfile, -2) == '.z') { // check file extension
9084  	 	 	 	 // Decompresses data encoded using the public-domain
9085  	 	 	 	 // zlib/deflate compression method, reproducing the
9086  	 	 	 	 // original text or binary data
9087  	 	 	 	 $out .= ' /Filter /FlateDecode';
9088  	 	 	 }
9089  	 	 	 $out .= ' >>';
9090  	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
9091  	 	 	 $out .= "\n".'endobj';
9092  	 	 	 $this->_out($out);
9093  	 	 }
9094  	 }
9095  
9096  	 /**
9097  	  * Output CID-0 fonts.
9098  	  * A Type 0 CIDFont contains glyph descriptions based on the Adobe Type 1 font format
9099  	  * @param $font (array) font data
9100  	  * @protected
9101  	  * @author Andrew Whitehead, Nicola Asuni, Yukihiro Nakadaira
9102  	  * @since 3.2.000 (2008-06-23)
9103  	  */
9104  	protected function _putcidfont0($font) {
9105  	 	 $cidoffset = 0;
9106  	 	 if (!isset($font['cw'][1])) {
9107  	 	 	 $cidoffset = 31;
9108  	 	 }
9109  	 	 if (isset($font['cidinfo']['uni2cid'])) {
9110  	 	 	 // convert unicode to cid.
9111  	 	 	 $uni2cid = $font['cidinfo']['uni2cid'];
9112  	 	 	 $cw = array();
9113  	 	 	 foreach ($font['cw'] as $uni => $width) {
9114  	 	 	 	 if (isset($uni2cid[$uni])) {
9115  	 	 	 	 	 $cw[($uni2cid[$uni] + $cidoffset)] = $width;
9116  	 	 	 	 } elseif ($uni < 256) {
9117  	 	 	 	 	 $cw[$uni] = $width;
9118  	 	 	 	 } // else unknown character
9119  	 	 	 }
9120  	 	 	 $font = array_merge($font, array('cw' => $cw));
9121  	 	 }
9122  	 	 $name = $font['name'];
9123  	 	 $enc = $font['enc'];
9124  	 	 if ($enc) {
9125  	 	 	 $longname = $name.'-'.$enc;
9126  	 	 } else {
9127  	 	 	 $longname = $name;
9128  	 	 }
9129  	 	 $out = $this->_getobj($this->font_obj_ids[$font['fontkey']])."\n";
9130  	 	 $out .= '<</Type /Font';
9131  	 	 $out .= ' /Subtype /Type0';
9132  	 	 $out .= ' /BaseFont /'.$longname;
9133  	 	 $out .= ' /Name /F'.$font['i'];
9134  	 	 if ($enc) {
9135  	 	 	 $out .= ' /Encoding /'.$enc;
9136  	 	 }
9137  	 	 $out .= ' /DescendantFonts ['.($this->n + 1).' 0 R]';
9138  	 	 $out .= ' >>';
9139  	 	 $out .= "\n".'endobj';
9140  	 	 $this->_out($out);
9141  	 	 $oid = $this->_newobj();
9142  	 	 $out = '<</Type /Font';
9143  	 	 $out .= ' /Subtype /CIDFontType0';
9144  	 	 $out .= ' /BaseFont /'.$name;
9145  	 	 $cidinfo = '/Registry '.$this->_datastring($font['cidinfo']['Registry'], $oid);
9146  	 	 $cidinfo .= ' /Ordering '.$this->_datastring($font['cidinfo']['Ordering'], $oid);
9147  	 	 $cidinfo .= ' /Supplement '.$font['cidinfo']['Supplement'];
9148  	 	 $out .= ' /CIDSystemInfo <<'.$cidinfo.'>>';
9149  	 	 $out .= ' /FontDescriptor '.($this->n + 1).' 0 R';
9150  	 	 $out .= ' /DW '.$font['dw'];
9151  	 	 $out .= "\n".TCPDF_FONTS::_putfontwidths($font, $cidoffset);
9152  	 	 $out .= ' >>';
9153  	 	 $out .= "\n".'endobj';
9154  	 	 $this->_out($out);
9155  	 	 $this->_newobj();
9156  	 	 $s = '<</Type /FontDescriptor /FontName /'.$name;
9157  	 	 foreach ($font['desc'] as $k => $v) {
9158  	 	 	 if ($k != 'Style') {
9159  	 	 	 	 if (is_float($v)) {
9160  	 	 	 	 	 $v = sprintf('%F', $v);
9161  	 	 	 	 }
9162  	 	 	 	 $s .= ' /'.$k.' '.$v.'';
9163  	 	 	 }
9164  	 	 }
9165  	 	 $s .= '>>';
9166  	 	 $s .= "\n".'endobj';
9167  	 	 $this->_out($s);
9168  	 }
9169  
9170  	 /**
9171  	  * Output images.
9172  	  * @protected
9173  	  */
9174  	protected function _putimages() {
9175  	 	 $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
9176  	 	 foreach ($this->imagekeys as $file) {
9177  	 	 	 $info = $this->getImageBuffer($file);
9178  	 	 	 // set object for alternate images array
9179  	 	 	 if ((!$this->pdfa_mode) AND isset($info['altimgs']) AND !empty($info['altimgs'])) {
9180  	 	 	 	 $altoid = $this->_newobj();
9181  	 	 	 	 $out = '[';
9182  	 	 	 	 foreach ($info['altimgs'] as $altimage) {
9183  	 	 	 	 	 if (isset($this->xobjects['I'.$altimage[0]]['n'])) {
9184  	 	 	 	 	 	 $out .= ' << /Image '.$this->xobjects['I'.$altimage[0]]['n'].' 0 R';
9185  	 	 	 	 	 	 $out .= ' /DefaultForPrinting';
9186  	 	 	 	 	 	 if ($altimage[1] === true) {
9187  	 	 	 	 	 	 	 $out .= ' true';
9188  	 	 	 	 	 	 } else {
9189  	 	 	 	 	 	 	 $out .= ' false';
9190  	 	 	 	 	 	 }
9191  	 	 	 	 	 	 $out .= ' >>';
9192  	 	 	 	 	 }
9193  	 	 	 	 }
9194  	 	 	 	 $out .= ' ]';
9195  	 	 	 	 $out .= "\n".'endobj';
9196  	 	 	 	 $this->_out($out);
9197  	 	 	 }
9198  	 	 	 // set image object
9199  	 	 	 $oid = $this->_newobj();
9200  	 	 	 $this->xobjects['I'.$info['i']] = array('n' => $oid);
9201  	 	 	 $this->setImageSubBuffer($file, 'n', $this->n);
9202  	 	 	 $out = '<</Type /XObject';
9203  	 	 	 $out .= ' /Subtype /Image';
9204  	 	 	 $out .= ' /Width '.$info['w'];
9205  	 	 	 $out .= ' /Height '.$info['h'];
9206  	 	 	 if (array_key_exists('masked', $info)) {
9207  	 	 	 	 $out .= ' /SMask '.($this->n - 1).' 0 R';
9208  	 	 	 }
9209  	 	 	 // set color space
9210  	 	 	 $icc = false;
9211  	 	 	 if (isset($info['icc']) AND ($info['icc'] !== false)) {
9212  	 	 	 	 // ICC Colour Space
9213  	 	 	 	 $icc = true;
9214  	 	 	 	 $out .= ' /ColorSpace [/ICCBased '.($this->n + 1).' 0 R]';
9215  	 	 	 } elseif ($info['cs'] == 'Indexed') {
9216  	 	 	 	 // Indexed Colour Space
9217  	 	 	 	 $out .= ' /ColorSpace [/Indexed /DeviceRGB '.((strlen($info['pal']) / 3) - 1).' '.($this->n + 1).' 0 R]';
9218  	 	 	 } else {
9219  	 	 	 	 // Device Colour Space
9220  	 	 	 	 $out .= ' /ColorSpace /'.$info['cs'];
9221  	 	 	 }
9222  	 	 	 if ($info['cs'] == 'DeviceCMYK') {
9223  	 	 	 	 $out .= ' /Decode [1 0 1 0 1 0 1 0]';
9224  	 	 	 }
9225  	 	 	 $out .= ' /BitsPerComponent '.$info['bpc'];
9226  	 	 	 if (isset($altoid) AND ($altoid > 0)) {
9227  	 	 	 	 // reference to alternate images dictionary
9228  	 	 	 	 $out .= ' /Alternates '.$altoid.' 0 R';
9229  	 	 	 }
9230  	 	 	 if (isset($info['exurl']) AND !empty($info['exurl'])) {
9231  	 	 	 	 // external stream
9232  	 	 	 	 $out .= ' /Length 0';
9233  	 	 	 	 $out .= ' /F << /FS /URL /F '.$this->_datastring($info['exurl'], $oid).' >>';
9234  	 	 	 	 if (isset($info['f'])) {
9235  	 	 	 	 	 $out .= ' /FFilter /'.$info['f'];
9236  	 	 	 	 }
9237  	 	 	 	 $out .= ' >>';
9238  	 	 	 	 $out .= ' stream'."\n".'endstream';
9239  	 	 	 } else {
9240  	 	 	 	 if (isset($info['f'])) {
9241  	 	 	 	 	 $out .= ' /Filter /'.$info['f'];
9242  	 	 	 	 }
9243  	 	 	 	 if (isset($info['parms'])) {
9244  	 	 	 	 	 $out .= ' '.$info['parms'];
9245  	 	 	 	 }
9246  	 	 	 	 if (isset($info['trns']) AND is_array($info['trns'])) {
9247  	 	 	 	 	 $trns = '';
9248  	 	 	 	 	 $count_info = count($info['trns']);
9249  	 	 	 	 	 if ($info['cs'] == 'Indexed') {
9250  	 	 	 	 	 	 $maxval =(pow(2, $info['bpc']) - 1);
9251  	 	 	 	 	 	 for ($i = 0; $i < $count_info; ++$i) {
9252  	 	 	 	 	 	 	 if (($info['trns'][$i] != 0) AND ($info['trns'][$i] != $maxval)) {
9253  	 	 	 	 	 	 	 	 // this is not a binary type mask @TODO: create a SMask
9254  	 	 	 	 	 	 	 	 $trns = '';
9255  	 	 	 	 	 	 	 	 break;
9256  	 	 	 	 	 	 	 } elseif (empty($trns) AND ($info['trns'][$i] == 0)) {
9257  	 	 	 	 	 	 	 	 // store the first fully transparent value
9258  	 	 	 	 	 	 	 	 $trns .= $i.' '.$i.' ';
9259  	 	 	 	 	 	 	 }
9260  	 	 	 	 	 	 }
9261  	 	 	 	 	 } else {
9262  	 	 	 	 	 	 // grayscale or RGB
9263  	 	 	 	 	 	 for ($i = 0; $i < $count_info; ++$i) {
9264  	 	 	 	 	 	 	 if ($info['trns'][$i] == 0) {
9265  	 	 	 	 	 	 	 	 $trns .= $info['trns'][$i].' '.$info['trns'][$i].' ';
9266  	 	 	 	 	 	 	 }
9267  	 	 	 	 	 	 }
9268  	 	 	 	 	 }
9269  	 	 	 	 	 // Colour Key Masking
9270  	 	 	 	 	 if (!empty($trns)) {
9271  	 	 	 	 	 	 $out .= ' /Mask ['.$trns.']';
9272  	 	 	 	 	 }
9273  	 	 	 	 }
9274  	 	 	 	 $stream = $this->_getrawstream($info['data']);
9275  	 	 	 	 $out .= ' /Length '.strlen($stream).' >>';
9276  	 	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
9277  	 	 	 }
9278  	 	 	 $out .= "\n".'endobj';
9279  	 	 	 $this->_out($out);
9280  	 	 	 if ($icc) {
9281  	 	 	 	 // ICC colour profile
9282  	 	 	 	 $this->_newobj();
9283  	 	 	 	 $icc = ($this->compress) ? gzcompress($info['icc']) : $info['icc'];
9284  	 	 	 	 $icc = $this->_getrawstream($icc);
9285  	 	 	 	 $this->_out('<</N '.$info['ch'].' /Alternate /'.$info['cs'].' '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9286  	 	 	 } elseif ($info['cs'] == 'Indexed') {
9287  	 	 	 	 // colour palette
9288  	 	 	 	 $this->_newobj();
9289  	 	 	 	 $pal = ($this->compress) ? gzcompress($info['pal']) : $info['pal'];
9290  	 	 	 	 $pal = $this->_getrawstream($pal);
9291  	 	 	 	 $this->_out('<<'.$filter.'/Length '.strlen($pal).'>> stream'."\n".$pal."\n".'endstream'."\n".'endobj');
9292  	 	 	 }
9293  	 	 }
9294  	 }
9295  
9296  	 /**
9297  	  * Output Form XObjects Templates.
9298  	  * @author Nicola Asuni
9299  	  * @since 5.8.017 (2010-08-24)
9300  	  * @protected
9301  	  * @see startTemplate(), endTemplate(), printTemplate()
9302  	  */
9303  	protected function _putxobjects() {
9304  	 	 foreach ($this->xobjects as $key => $data) {
9305  	 	 	 if (isset($data['outdata'])) {
9306  	 	 	 	 $stream = str_replace($this->epsmarker, '', trim($data['outdata']));
9307  	 	 	 	 $out = $this->_getobj($data['n'])."\n";
9308  	 	 	 	 $out .= '<<';
9309  	 	 	 	 $out .= ' /Type /XObject';
9310  	 	 	 	 $out .= ' /Subtype /Form';
9311  	 	 	 	 $out .= ' /FormType 1';
9312  	 	 	 	 if ($this->compress) {
9313  	 	 	 	 	 $stream = gzcompress($stream);
9314  	 	 	 	 	 $out .= ' /Filter /FlateDecode';
9315  	 	 	 	 }
9316  	 	 	 	 $out .= sprintf(' /BBox [%F %F %F %F]', ($data['x'] * $this->k), (-$data['y'] * $this->k), (($data['w'] + $data['x']) * $this->k), (($data['h'] - $data['y']) * $this->k));
9317  	 	 	 	 $out .= ' /Matrix [1 0 0 1 0 0]';
9318  	 	 	 	 $out .= ' /Resources <<';
9319  	 	 	 	 $out .= ' /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9320  	 	 	 	 if (!$this->pdfa_mode) {
9321  	 	 	 	 	 // transparency
9322  	 	 	 	 	 if (isset($data['extgstates']) AND !empty($data['extgstates'])) {
9323  	 	 	 	 	 	 $out .= ' /ExtGState <<';
9324  	 	 	 	 	 	 foreach ($data['extgstates'] as $k => $extgstate) {
9325  	 	 	 	 	 	 	 if (isset($this->extgstates[$k]['name'])) {
9326  	 	 	 	 	 	 	 	 $out .= ' /'.$this->extgstates[$k]['name'];
9327  	 	 	 	 	 	 	 } else {
9328  	 	 	 	 	 	 	 	 $out .= ' /GS'.$k;
9329  	 	 	 	 	 	 	 }
9330  	 	 	 	 	 	 	 $out .= ' '.$this->extgstates[$k]['n'].' 0 R';
9331  	 	 	 	 	 	 }
9332  	 	 	 	 	 	 $out .= ' >>';
9333  	 	 	 	 	 }
9334  	 	 	 	 	 if (isset($data['gradients']) AND !empty($data['gradients'])) {
9335  	 	 	 	 	 	 $gp = '';
9336  	 	 	 	 	 	 $gs = '';
9337  	 	 	 	 	 	 foreach ($data['gradients'] as $id => $grad) {
9338  	 	 	 	 	 	 	 // gradient patterns
9339  	 	 	 	 	 	 	 $gp .= ' /p'.$id.' '.$this->gradients[$id]['pattern'].' 0 R';
9340  	 	 	 	 	 	 	 // gradient shadings
9341  	 	 	 	 	 	 	 $gs .= ' /Sh'.$id.' '.$this->gradients[$id]['id'].' 0 R';
9342  	 	 	 	 	 	 }
9343  	 	 	 	 	 	 $out .= ' /Pattern <<'.$gp.' >>';
9344  	 	 	 	 	 	 $out .= ' /Shading <<'.$gs.' >>';
9345  	 	 	 	 	 }
9346  	 	 	 	 }
9347  	 	 	 	 // spot colors
9348  	 	 	 	 if (isset($data['spot_colors']) AND !empty($data['spot_colors'])) {
9349  	 	 	 	 	 $out .= ' /ColorSpace <<';
9350  	 	 	 	 	 foreach ($data['spot_colors'] as $name => $color) {
9351  	 	 	 	 	 	 $out .= ' /CS'.$color['i'].' '.$this->spot_colors[$name]['n'].' 0 R';
9352  	 	 	 	 	 }
9353  	 	 	 	 	 $out .= ' >>';
9354  	 	 	 	 }
9355  	 	 	 	 // fonts
9356  	 	 	 	 if (!empty($data['fonts'])) {
9357  	 	 	 	 	 $out .= ' /Font <<';
9358  	 	 	 	 	 foreach ($data['fonts'] as $fontkey => $fontid) {
9359  	 	 	 	 	 	 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9360  	 	 	 	 	 }
9361  	 	 	 	 	 $out .= ' >>';
9362  	 	 	 	 }
9363  	 	 	 	 // images or nested xobjects
9364  	 	 	 	 if (!empty($data['images']) OR !empty($data['xobjects'])) {
9365  	 	 	 	 	 $out .= ' /XObject <<';
9366  	 	 	 	 	 foreach ($data['images'] as $imgid) {
9367  	 	 	 	 	 	 $out .= ' /I'.$imgid.' '.$this->xobjects['I'.$imgid]['n'].' 0 R';
9368  	 	 	 	 	 }
9369  	 	 	 	 	 foreach ($data['xobjects'] as $sub_id => $sub_objid) {
9370  	 	 	 	 	 	 $out .= ' /'.$sub_id.' '.$sub_objid['n'].' 0 R';
9371  	 	 	 	 	 }
9372  	 	 	 	 	 $out .= ' >>';
9373  	 	 	 	 }
9374  	 	 	 	 $out .= ' >>'; //end resources
9375  	 	 	 	 if (isset($data['group']) AND ($data['group'] !== false)) {
9376  	 	 	 	 	 // set transparency group
9377  	 	 	 	 	 $out .= ' /Group << /Type /Group /S /Transparency';
9378  	 	 	 	 	 if (is_array($data['group'])) {
9379  	 	 	 	 	 	 if (isset($data['group']['CS']) AND !empty($data['group']['CS'])) {
9380  	 	 	 	 	 	 	 $out .= ' /CS /'.$data['group']['CS'];
9381  	 	 	 	 	 	 }
9382  	 	 	 	 	 	 if (isset($data['group']['I'])) {
9383  	 	 	 	 	 	 	 $out .= ' /I /'.($data['group']['I']===true?'true':'false');
9384  	 	 	 	 	 	 }
9385  	 	 	 	 	 	 if (isset($data['group']['K'])) {
9386  	 	 	 	 	 	 	 $out .= ' /K /'.($data['group']['K']===true?'true':'false');
9387  	 	 	 	 	 	 }
9388  	 	 	 	 	 }
9389  	 	 	 	 	 $out .= ' >>';
9390  	 	 	 	 }
9391  	 	 	 	 $stream = $this->_getrawstream($stream, $data['n']);
9392  	 	 	 	 $out .= ' /Length '.strlen($stream);
9393  	 	 	 	 $out .= ' >>';
9394  	 	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
9395  	 	 	 	 $out .= "\n".'endobj';
9396  	 	 	 	 $this->_out($out);
9397  	 	 	 }
9398  	 	 }
9399  	 }
9400  
9401  	 /**
9402  	  * Output Spot Colors Resources.
9403  	  * @protected
9404  	  * @since 4.0.024 (2008-09-12)
9405  	  */
9406  	protected function _putspotcolors() {
9407  	 	 foreach ($this->spot_colors as $name => $color) {
9408  	 	 	 $this->_newobj();
9409  	 	 	 $this->spot_colors[$name]['n'] = $this->n;
9410  	 	 	 $out = '[/Separation /'.str_replace(' ', '#20', $name);
9411  	 	 	 $out .= ' /DeviceCMYK <<';
9412  	 	 	 $out .= ' /Range [0 1 0 1 0 1 0 1] /C0 [0 0 0 0]';
9413  	 	 	 $out .= ' '.sprintf('/C1 [%F %F %F %F] ', ($color['C'] / 100), ($color['M'] / 100), ($color['Y'] / 100), ($color['K'] / 100));
9414  	 	 	 $out .= ' /FunctionType 2 /Domain [0 1] /N 1>>]';
9415  	 	 	 $out .= "\n".'endobj';
9416  	 	 	 $this->_out($out);
9417  	 	 }
9418  	 }
9419  
9420  	 /**
9421  	  * Return XObjects Dictionary.
9422  	  * @return string XObjects dictionary
9423  	  * @protected
9424  	  * @since 5.8.014 (2010-08-23)
9425  	  */
9426  	protected function _getxobjectdict() {
9427  	 	 $out = '';
9428  	 	 foreach ($this->xobjects as $id => $objid) {
9429  	 	 	 $out .= ' /'.$id.' '.$objid['n'].' 0 R';
9430  	 	 }
9431  	 	 return $out;
9432  	 }
9433  
9434  	 /**
9435  	  * Output Resources Dictionary.
9436  	  * @protected
9437  	  */
9438  	protected function _putresourcedict() {
9439  	 	 $out = $this->_getobj(2)."\n";
9440  	 	 $out .= '<< /ProcSet [/PDF /Text /ImageB /ImageC /ImageI]';
9441  	 	 $out .= ' /Font <<';
9442  	 	 foreach ($this->fontkeys as $fontkey) {
9443  	 	 	 $font = $this->getFontBuffer($fontkey);
9444  	 	 	 $out .= ' /F'.$font['i'].' '.$font['n'].' 0 R';
9445  	 	 }
9446  	 	 $out .= ' >>';
9447  	 	 $out .= ' /XObject <<';
9448  	 	 $out .= $this->_getxobjectdict();
9449  	 	 $out .= ' >>';
9450  	 	 // layers
9451  	 	 if (!empty($this->pdflayers)) {
9452  	 	 	 $out .= ' /Properties <<';
9453  	 	 	 foreach ($this->pdflayers as $layer) {
9454  	 	 	 	 $out .= ' /'.$layer['layer'].' '.$layer['objid'].' 0 R';
9455  	 	 	 }
9456  	 	 	 $out .= ' >>';
9457  	 	 }
9458  	 	 if (!$this->pdfa_mode) {
9459  	 	 	 // transparency
9460  	 	 	 if (isset($this->extgstates) AND !empty($this->extgstates)) {
9461  	 	 	 	 $out .= ' /ExtGState <<';
9462  	 	 	 	 foreach ($this->extgstates as $k => $extgstate) {
9463  	 	 	 	 	 if (isset($extgstate['name'])) {
9464  	 	 	 	 	 	 $out .= ' /'.$extgstate['name'];
9465  	 	 	 	 	 } else {
9466  	 	 	 	 	 	 $out .= ' /GS'.$k;
9467  	 	 	 	 	 }
9468  	 	 	 	 	 $out .= ' '.$extgstate['n'].' 0 R';
9469  	 	 	 	 }
9470  	 	 	 	 $out .= ' >>';
9471  	 	 	 }
9472  	 	 	 if (isset($this->gradients) AND !empty($this->gradients)) {
9473  	 	 	 	 $gp = '';
9474  	 	 	 	 $gs = '';
9475  	 	 	 	 foreach ($this->gradients as $id => $grad) {
9476  	 	 	 	 	 // gradient patterns
9477  	 	 	 	 	 $gp .= ' /p'.$id.' '.$grad['pattern'].' 0 R';
9478  	 	 	 	 	 // gradient shadings
9479  	 	 	 	 	 $gs .= ' /Sh'.$id.' '.$grad['id'].' 0 R';
9480  	 	 	 	 }
9481  	 	 	 	 $out .= ' /Pattern <<'.$gp.' >>';
9482  	 	 	 	 $out .= ' /Shading <<'.$gs.' >>';
9483  	 	 	 }
9484  	 	 }
9485  	 	 // spot colors
9486  	 	 if (isset($this->spot_colors) AND !empty($this->spot_colors)) {
9487  	 	 	 $out .= ' /ColorSpace <<';
9488  	 	 	 foreach ($this->spot_colors as $color) {
9489  	 	 	 	 $out .= ' /CS'.$color['i'].' '.$color['n'].' 0 R';
9490  	 	 	 }
9491  	 	 	 $out .= ' >>';
9492  	 	 }
9493  	 	 $out .= ' >>';
9494  	 	 $out .= "\n".'endobj';
9495  	 	 $this->_out($out);
9496  	 }
9497  
9498  	 /**
9499  	  * Output Resources.
9500  	  * @protected
9501  	  */
9502  	protected function _putresources() {
9503  	 	 $this->_putextgstates();
9504  	 	 $this->_putocg();
9505  	 	 $this->_putfonts();
9506  	 	 $this->_putimages();
9507  	 	 $this->_putspotcolors();
9508  	 	 $this->_putshaders();
9509  	 	 $this->_putxobjects();
9510  	 	 $this->_putresourcedict();
9511  	 	 $this->_putdests();
9512  	 	 $this->_putEmbeddedFiles();
9513  	 	 $this->_putannotsobjs();
9514  	 	 $this->_putjavascript();
9515  	 	 $this->_putbookmarks();
9516  	 	 $this->_putencryption();
9517  	 }
9518  
9519  	 /**
9520  	  * Adds some Metadata information (Document Information Dictionary)
9521  	  * (see Chapter 14.3.3 Document Information Dictionary of PDF32000_2008.pdf Reference)
9522  	  * @return int object id
9523  	  * @protected
9524  	  */
9525  	protected function _putinfo() {
9526  	 	 $oid = $this->_newobj();
9527  	 	 $out = '<<';
9528  	 	 // store current isunicode value
9529  	 	 $prev_isunicode = $this->isunicode;
9530  	 	 if ($this->docinfounicode) {
9531  	 	 	 $this->isunicode = true;
9532  	 	 }
9533  	 	 if (!TCPDF_STATIC::empty_string($this->title)) {
9534  	 	 	 // The document's title.
9535  	 	 	 $out .= ' /Title '.$this->_textstring($this->title, $oid);
9536  	 	 }
9537  	 	 if (!TCPDF_STATIC::empty_string($this->author)) {
9538  	 	 	 // The name of the person who created the document.
9539  	 	 	 $out .= ' /Author '.$this->_textstring($this->author, $oid);
9540  	 	 }
9541  	 	 if (!TCPDF_STATIC::empty_string($this->subject)) {
9542  	 	 	 // The subject of the document.
9543  	 	 	 $out .= ' /Subject '.$this->_textstring($this->subject, $oid);
9544  	 	 }
9545  	 	 if (!TCPDF_STATIC::empty_string($this->keywords)) {
9546  	 	 	 // Keywords associated with the document.
9547  	 	 	 $out .= ' /Keywords '.$this->_textstring($this->keywords, $oid);
9548  	 	 }
9549  	 	 if (!TCPDF_STATIC::empty_string($this->creator)) {
9550  	 	 	 // 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.
9551  	 	 	 $out .= ' /Creator '.$this->_textstring($this->creator, $oid);
9552  	 	 }
9553  	 	 // restore previous isunicode value
9554  	 	 $this->isunicode = $prev_isunicode;
9555  	 	 // default producer
9556  	 	 $out .= ' /Producer '.$this->_textstring(TCPDF_STATIC::getTCPDFProducer(), $oid);
9557  	 	 // The date and time the document was created, in human-readable form
9558  	 	 $out .= ' /CreationDate '.$this->_datestring(0, $this->doc_creation_timestamp);
9559  	 	 // The date and time the document was most recently modified, in human-readable form
9560  	 	 $out .= ' /ModDate '.$this->_datestring(0, $this->doc_modification_timestamp);
9561  	 	 // A name object indicating whether the document has been modified to include trapping information
9562  	 	 $out .= ' /Trapped /False';
9563  	 	 $out .= ' >>';
9564  	 	 $out .= "\n".'endobj';
9565  	 	 $this->_out($out);
9566  	 	 return $oid;
9567  	 }
9568  
9569  	 /**
9570  	  * Set additional XMP data to be added on the default XMP data just before the end of "x:xmpmeta" tag.
9571  	  * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9572  	  * @param $xmp (string) Custom XMP data.
9573  	  * @since 5.9.128 (2011-10-06)
9574  	  * @public
9575  	  */
9576  	public function setExtraXMP($xmp) {
9577  	 	 $this->custom_xmp = $xmp;
9578  	 }
9579  
9580  	 /**
9581  	  * Set additional XMP data to be added on the default XMP data just before the end of "rdf:RDF" tag.
9582  	  * IMPORTANT: This data is added as-is without controls, so you have to validate your data before using this method!
9583  	  * @param $xmp (string) Custom XMP RDF data.
9584  	  * @since 6.3.0 (2019-09-19)
9585  	  * @public
9586  	  */
9587  	public function setExtraXMPRDF($xmp) {
9588  	 	 $this->custom_xmp_rdf = $xmp;
9589  	 }
9590  
9591  	 /**
9592  	  * Put XMP data object and return ID.
9593  	  * @return (int) The object ID.
9594  	  * @since 5.9.121 (2011-09-28)
9595  	  * @protected
9596  	  */
9597  	protected function _putXMP() {
9598  	 	 $oid = $this->_newobj();
9599  	 	 // store current isunicode value
9600  	 	 $prev_isunicode = $this->isunicode;
9601  	 	 $this->isunicode = true;
9602  	 	 $prev_encrypted = $this->encrypted;
9603  	 	 $this->encrypted = false;
9604  	 	 // set XMP data
9605  	 	 $xmp = '<?xpacket begin="'.TCPDF_FONTS::unichr(0xfeff, $this->isunicode).'" id="W5M0MpCehiHzreSzNTczkc9d"?>'."\n";
9606  	 	 $xmp .= '<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 4.2.1-c043 52.372728, 2009/01/18-15:08:04">'."\n";
9607  	 	 $xmp .= "\t".'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'."\n";
9608  	 	 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n";
9609  	 	 $xmp .= "\t\t\t".'<dc:format>application/pdf</dc:format>'."\n";
9610  	 	 $xmp .= "\t\t\t".'<dc:title>'."\n";
9611  	 	 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9612  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->title).'</rdf:li>'."\n";
9613  	 	 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9614  	 	 $xmp .= "\t\t\t".'</dc:title>'."\n";
9615  	 	 $xmp .= "\t\t\t".'<dc:creator>'."\n";
9616  	 	 $xmp .= "\t\t\t\t".'<rdf:Seq>'."\n";
9617  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->author).'</rdf:li>'."\n";
9618  	 	 $xmp .= "\t\t\t\t".'</rdf:Seq>'."\n";
9619  	 	 $xmp .= "\t\t\t".'</dc:creator>'."\n";
9620  	 	 $xmp .= "\t\t\t".'<dc:description>'."\n";
9621  	 	 $xmp .= "\t\t\t\t".'<rdf:Alt>'."\n";
9622  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li xml:lang="x-default">'.TCPDF_STATIC::_escapeXML($this->subject).'</rdf:li>'."\n";
9623  	 	 $xmp .= "\t\t\t\t".'</rdf:Alt>'."\n";
9624  	 	 $xmp .= "\t\t\t".'</dc:description>'."\n";
9625  	 	 $xmp .= "\t\t\t".'<dc:subject>'."\n";
9626  	 	 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9627  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li>'.TCPDF_STATIC::_escapeXML($this->keywords).'</rdf:li>'."\n";
9628  	 	 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9629  	 	 $xmp .= "\t\t\t".'</dc:subject>'."\n";
9630  	 	 $xmp .= "\t\t".'</rdf:Description>'."\n";
9631  	 	 // convert doc creation date format
9632  	 	 $dcdate = TCPDF_STATIC::getFormattedDate($this->doc_creation_timestamp);
9633  	 	 $doccreationdate = substr($dcdate, 0, 4).'-'.substr($dcdate, 4, 2).'-'.substr($dcdate, 6, 2);
9634  	 	 $doccreationdate .= 'T'.substr($dcdate, 8, 2).':'.substr($dcdate, 10, 2).':'.substr($dcdate, 12, 2);
9635  	 	 $doccreationdate .= substr($dcdate, 14, 3).':'.substr($dcdate, 18, 2);
9636  	 	 $doccreationdate = TCPDF_STATIC::_escapeXML($doccreationdate);
9637  	 	 // convert doc modification date format
9638  	 	 $dmdate = TCPDF_STATIC::getFormattedDate($this->doc_modification_timestamp);
9639  	 	 $docmoddate = substr($dmdate, 0, 4).'-'.substr($dmdate, 4, 2).'-'.substr($dmdate, 6, 2);
9640  	 	 $docmoddate .= 'T'.substr($dmdate, 8, 2).':'.substr($dmdate, 10, 2).':'.substr($dmdate, 12, 2);
9641  	 	 $docmoddate .= substr($dmdate, 14, 3).':'.substr($dmdate, 18, 2);
9642  	 	 $docmoddate = TCPDF_STATIC::_escapeXML($docmoddate);
9643  	 	 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/">'."\n";
9644  	 	 $xmp .= "\t\t\t".'<xmp:CreateDate>'.$doccreationdate.'</xmp:CreateDate>'."\n";
9645  	 	 $xmp .= "\t\t\t".'<xmp:CreatorTool>'.$this->creator.'</xmp:CreatorTool>'."\n";
9646  	 	 $xmp .= "\t\t\t".'<xmp:ModifyDate>'.$docmoddate.'</xmp:ModifyDate>'."\n";
9647  	 	 $xmp .= "\t\t\t".'<xmp:MetadataDate>'.$doccreationdate.'</xmp:MetadataDate>'."\n";
9648  	 	 $xmp .= "\t\t".'</rdf:Description>'."\n";
9649  	 	 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdf="http://ns.adobe.com/pdf/1.3/">'."\n";
9650  	 	 $xmp .= "\t\t\t".'<pdf:Keywords>'.TCPDF_STATIC::_escapeXML($this->keywords).'</pdf:Keywords>'."\n";
9651  	 	 $xmp .= "\t\t\t".'<pdf:Producer>'.TCPDF_STATIC::_escapeXML(TCPDF_STATIC::getTCPDFProducer()).'</pdf:Producer>'."\n";
9652  	 	 $xmp .= "\t\t".'</rdf:Description>'."\n";
9653  	 	 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/">'."\n";
9654  	 	 $uuid = 'uuid:'.substr($this->file_id, 0, 8).'-'.substr($this->file_id, 8, 4).'-'.substr($this->file_id, 12, 4).'-'.substr($this->file_id, 16, 4).'-'.substr($this->file_id, 20, 12);
9655  	 	 $xmp .= "\t\t\t".'<xmpMM:DocumentID>'.$uuid.'</xmpMM:DocumentID>'."\n";
9656  	 	 $xmp .= "\t\t\t".'<xmpMM:InstanceID>'.$uuid.'</xmpMM:InstanceID>'."\n";
9657  	 	 $xmp .= "\t\t".'</rdf:Description>'."\n";
9658  	 	 if ($this->pdfa_mode) {
9659  	 	 	 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/">'."\n";
9660  	 	 	 $xmp .= "\t\t\t".'<pdfaid:part>'.$this->pdfa_version.'</pdfaid:part>'."\n";
9661  	 	 	 $xmp .= "\t\t\t".'<pdfaid:conformance>B</pdfaid:conformance>'."\n";
9662  	 	 	 $xmp .= "\t\t".'</rdf:Description>'."\n";
9663  	 	 }
9664  	 	 // XMP extension schemas
9665  	 	 $xmp .= "\t\t".'<rdf:Description rdf:about="" xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/" xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#" xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">'."\n";
9666  	 	 $xmp .= "\t\t\t".'<pdfaExtension:schemas>'."\n";
9667  	 	 $xmp .= "\t\t\t\t".'<rdf:Bag>'."\n";
9668  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9669  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/pdf/1.3/</pdfaSchema:namespaceURI>'."\n";
9670  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdf</pdfaSchema:prefix>'."\n";
9671  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>Adobe PDF Schema</pdfaSchema:schema>'."\n";
9672  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9673  	 	 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9674  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9675  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9676  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Adobe PDF Schema</pdfaProperty:description>'."\n";
9677  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9678  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9679  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9680  	 	 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9681  	 	 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9682  	 	 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9683  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9684  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://ns.adobe.com/xap/1.0/mm/</pdfaSchema:namespaceURI>'."\n";
9685  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>xmpMM</pdfaSchema:prefix>'."\n";
9686  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>XMP Media Management Schema</pdfaSchema:schema>'."\n";
9687  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9688  	 	 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9689  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9690  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9691  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>UUID based identifier for specific incarnation of a document</pdfaProperty:description>'."\n";
9692  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>InstanceID</pdfaProperty:name>'."\n";
9693  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>URI</pdfaProperty:valueType>'."\n";
9694  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9695  	 	 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9696  	 	 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9697  	 	 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9698  	 	 $xmp .= "\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9699  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:namespaceURI>http://www.aiim.org/pdfa/ns/id/</pdfaSchema:namespaceURI>'."\n";
9700  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:prefix>pdfaid</pdfaSchema:prefix>'."\n";
9701  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:schema>PDF/A ID Schema</pdfaSchema:schema>'."\n";
9702  	 	 $xmp .= "\t\t\t\t\t\t".'<pdfaSchema:property>'."\n";
9703  	 	 $xmp .= "\t\t\t\t\t\t\t".'<rdf:Seq>'."\n";
9704  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9705  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9706  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Part of PDF/A standard</pdfaProperty:description>'."\n";
9707  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>part</pdfaProperty:name>'."\n";
9708  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Integer</pdfaProperty:valueType>'."\n";
9709  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9710  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9711  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9712  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Amendment of PDF/A standard</pdfaProperty:description>'."\n";
9713  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>amd</pdfaProperty:name>'."\n";
9714  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9715  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9716  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'<rdf:li rdf:parseType="Resource">'."\n";
9717  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:category>internal</pdfaProperty:category>'."\n";
9718  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:description>Conformance level of PDF/A standard</pdfaProperty:description>'."\n";
9719  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:name>conformance</pdfaProperty:name>'."\n";
9720  	 	 $xmp .= "\t\t\t\t\t\t\t\t\t".'<pdfaProperty:valueType>Text</pdfaProperty:valueType>'."\n";
9721  	 	 $xmp .= "\t\t\t\t\t\t\t\t".'</rdf:li>'."\n";
9722  	 	 $xmp .= "\t\t\t\t\t\t\t".'</rdf:Seq>'."\n";
9723  	 	 $xmp .= "\t\t\t\t\t\t".'</pdfaSchema:property>'."\n";
9724  	 	 $xmp .= "\t\t\t\t\t".'</rdf:li>'."\n";
9725  	 	 $xmp .= "\t\t\t\t".'</rdf:Bag>'."\n";
9726  	 	 $xmp .= "\t\t\t".'</pdfaExtension:schemas>'."\n";
9727  	 	 $xmp .= "\t\t".'</rdf:Description>'."\n";
9728  	 	 $xmp .= $this->custom_xmp_rdf;
9729  	 	 $xmp .= "\t".'</rdf:RDF>'."\n";
9730  	 	 $xmp .= $this->custom_xmp;
9731  	 	 $xmp .= '</x:xmpmeta>'."\n";
9732  	 	 $xmp .= '<?xpacket end="w"?>';
9733  	 	 $out = '<< /Type /Metadata /Subtype /XML /Length '.strlen($xmp).' >> stream'."\n".$xmp."\n".'endstream'."\n".'endobj';
9734  	 	 // restore previous isunicode value
9735  	 	 $this->isunicode = $prev_isunicode;
9736  	 	 $this->encrypted = $prev_encrypted;
9737  	 	 $this->_out($out);
9738  	 	 return $oid;
9739  	 }
9740  
9741  	 /**
9742  	  * Output Catalog.
9743  	  * @return int object id
9744  	  * @protected
9745  	  */
9746  	protected function _putcatalog() {
9747  	 	 // put XMP
9748  	 	 $xmpobj = $this->_putXMP();
9749  	 	 // if required, add standard sRGB ICC colour profile
9750  	 	 if ($this->pdfa_mode OR $this->force_srgb) {
9751  	 	 	 $iccobj = $this->_newobj();
9752  	 	 	 $icc = file_get_contents(dirname(__FILE__).'/include/sRGB.icc');
9753  	 	 	 $filter = '';
9754  	 	 	 if ($this->compress) {
9755  	 	 	 	 $filter = ' /Filter /FlateDecode';
9756  	 	 	 	 $icc = gzcompress($icc);
9757  	 	 	 }
9758  	 	 	 $icc = $this->_getrawstream($icc);
9759  	 	 	 $this->_out('<</N 3 '.$filter.'/Length '.strlen($icc).'>> stream'."\n".$icc."\n".'endstream'."\n".'endobj');
9760  	 	 }
9761  	 	 // start catalog
9762  	 	 $oid = $this->_newobj();
9763  	 	 $out = '<< /Type /Catalog';
9764  	 	 $out .= ' /Version /'.$this->PDFVersion;
9765  	 	 //$out .= ' /Extensions <<>>';
9766  	 	 $out .= ' /Pages 1 0 R';
9767  	 	 //$out .= ' /PageLabels ' //...;
9768  	 	 $out .= ' /Names <<';
9769  	 	 if ((!$this->pdfa_mode) AND !empty($this->n_js)) {
9770  	 	 	 $out .= ' /JavaScript '.$this->n_js;
9771  	 	 }
9772  	 	 if (!empty($this->efnames)) {
9773  	 	 	 $out .= ' /EmbeddedFiles <</Names [';
9774  	 	 	 foreach ($this->efnames AS $fn => $fref) {
9775  	 	 	 	 $out .= ' '.$this->_datastring($fn).' '.$fref;
9776  	 	 	 }
9777  	 	 	 $out .= ' ]>>';
9778  	 	 }
9779  	 	 $out .= ' >>';
9780  	 	 if (!empty($this->dests)) {
9781  	 	 	 $out .= ' /Dests '.($this->n_dests).' 0 R';
9782  	 	 }
9783  	 	 $out .= $this->_putviewerpreferences();
9784  	 	 if (isset($this->LayoutMode) AND (!TCPDF_STATIC::empty_string($this->LayoutMode))) {
9785  	 	 	 $out .= ' /PageLayout /'.$this->LayoutMode;
9786  	 	 }
9787  	 	 if (isset($this->PageMode) AND (!TCPDF_STATIC::empty_string($this->PageMode))) {
9788  	 	 	 $out .= ' /PageMode /'.$this->PageMode;
9789  	 	 }
9790  	 	 if (count($this->outlines) > 0) {
9791  	 	 	 $out .= ' /Outlines '.$this->OutlineRoot.' 0 R';
9792  	 	 	 $out .= ' /PageMode /UseOutlines';
9793  	 	 }
9794  	 	 //$out .= ' /Threads []';
9795  	 	 if ($this->ZoomMode == 'fullpage') {
9796  	 	 	 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /Fit]';
9797  	 	 } elseif ($this->ZoomMode == 'fullwidth') {
9798  	 	 	 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /FitH null]';
9799  	 	 } elseif ($this->ZoomMode == 'real') {
9800  	 	 	 $out .= ' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null 1]';
9801  	 	 } elseif (!is_string($this->ZoomMode)) {
9802  	 	 	 $out .= sprintf(' /OpenAction ['.$this->page_obj_id[1].' 0 R /XYZ null null %F]', ($this->ZoomMode / 100));
9803  	 	 }
9804  	 	 //$out .= ' /AA <<>>';
9805  	 	 //$out .= ' /URI <<>>';
9806  	 	 $out .= ' /Metadata '.$xmpobj.' 0 R';
9807  	 	 //$out .= ' /StructTreeRoot <<>>';
9808  	 	 //$out .= ' /MarkInfo <<>>';
9809  	 	 if (isset($this->l['a_meta_language'])) {
9810  	 	 	 $out .= ' /Lang '.$this->_textstring($this->l['a_meta_language'], $oid);
9811  	 	 }
9812  	 	 //$out .= ' /SpiderInfo <<>>';
9813  	 	 // set OutputIntent to sRGB IEC61966-2.1 if required
9814  	 	 if ($this->pdfa_mode OR $this->force_srgb) {
9815  	 	 	 $out .= ' /OutputIntents [<<';
9816  	 	 	 $out .= ' /Type /OutputIntent';
9817  	 	 	 $out .= ' /S /GTS_PDFA1';
9818  	 	 	 $out .= ' /OutputCondition '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9819  	 	 	 $out .= ' /OutputConditionIdentifier '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9820  	 	 	 $out .= ' /RegistryName '.$this->_textstring('http://www.color.org', $oid);
9821  	 	 	 $out .= ' /Info '.$this->_textstring('sRGB IEC61966-2.1', $oid);
9822  	 	 	 $out .= ' /DestOutputProfile '.$iccobj.' 0 R';
9823  	 	 	 $out .= ' >>]';
9824  	 	 }
9825  	 	 //$out .= ' /PieceInfo <<>>';
9826  	 	 if (!empty($this->pdflayers)) {
9827  	 	 	 $lyrobjs = '';
9828  	 	 	 $lyrobjs_off = '';
9829  	 	 	 $lyrobjs_lock = '';
9830  	 	 	 foreach ($this->pdflayers as $layer) {
9831  	 	 	 	 $layer_obj_ref = ' '.$layer['objid'].' 0 R';
9832  	 	 	 	 $lyrobjs .= $layer_obj_ref;
9833  	 	 	 	 if ($layer['view'] === false) {
9834  	 	 	 	 	 $lyrobjs_off .= $layer_obj_ref;
9835  	 	 	 	 }
9836  	 	 	 	 if ($layer['lock']) {
9837  	 	 	 	 	 $lyrobjs_lock .= $layer_obj_ref;
9838  	 	 	 	 }
9839  	 	 	 }
9840  	 	 	 $out .= ' /OCProperties << /OCGs ['.$lyrobjs.']';
9841  	 	 	 $out .= ' /D <<';
9842  	 	 	 $out .= ' /Name '.$this->_textstring('Layers', $oid);
9843  	 	 	 $out .= ' /Creator '.$this->_textstring('TCPDF', $oid);
9844  	 	 	 $out .= ' /BaseState /ON';
9845  	 	 	 $out .= ' /OFF ['.$lyrobjs_off.']';
9846  	 	 	 $out .= ' /Locked ['.$lyrobjs_lock.']';
9847  	 	 	 $out .= ' /Intent /View';
9848  	 	 	 $out .= ' /AS [';
9849  	 	 	 $out .= ' << /Event /Print /OCGs ['.$lyrobjs.'] /Category [/Print] >>';
9850  	 	 	 $out .= ' << /Event /View /OCGs ['.$lyrobjs.'] /Category [/View] >>';
9851  	 	 	 $out .= ' ]';
9852  	 	 	 $out .= ' /Order ['.$lyrobjs.']';
9853  	 	 	 $out .= ' /ListMode /AllPages';
9854  	 	 	 //$out .= ' /RBGroups ['..']';
9855  	 	 	 //$out .= ' /Locked ['..']';
9856  	 	 	 $out .= ' >>';
9857  	 	 	 $out .= ' >>';
9858  	 	 }
9859  	 	 // AcroForm
9860  	 	 if (!empty($this->form_obj_id)
9861  	 	 	 OR ($this->sign AND isset($this->signature_data['cert_type']))
9862  	 	 	 OR !empty($this->empty_signature_appearance)) {
9863  	 	 	 $out .= ' /AcroForm <<';
9864  	 	 	 $objrefs = '';
9865  	 	 	 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9866  	 	 	 	 // set reference for signature object
9867  	 	 	 	 $objrefs .= $this->sig_obj_id.' 0 R';
9868  	 	 	 }
9869  	 	 	 if (!empty($this->empty_signature_appearance)) {
9870  	 	 	 	 foreach ($this->empty_signature_appearance as $esa) {
9871  	 	 	 	 	 // set reference for empty signature objects
9872  	 	 	 	 	 $objrefs .= ' '.$esa['objid'].' 0 R';
9873  	 	 	 	 }
9874  	 	 	 }
9875  	 	 	 if (!empty($this->form_obj_id)) {
9876  	 	 	 	 foreach($this->form_obj_id as $objid) {
9877  	 	 	 	 	 $objrefs .= ' '.$objid.' 0 R';
9878  	 	 	 	 }
9879  	 	 	 }
9880  	 	 	 $out .= ' /Fields ['.$objrefs.']';
9881  	 	 	 // It's better to turn off this value and set the appearance stream for each annotation (/AP) to avoid conflicts with signature fields.
9882  	 	 	 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
9883  	 	 	 	 $out .= ' /NeedAppearances false';
9884  	 	 	 }
9885  	 	 	 if ($this->sign AND isset($this->signature_data['cert_type'])) {
9886  	 	 	 	 if ($this->signature_data['cert_type'] > 0) {
9887  	 	 	 	 	 $out .= ' /SigFlags 3';
9888  	 	 	 	 } else {
9889  	 	 	 	 	 $out .= ' /SigFlags 1';
9890  	 	 	 	 }
9891  	 	 	 }
9892  	 	 	 //$out .= ' /CO ';
9893  	 	 	 if (isset($this->annotation_fonts) AND !empty($this->annotation_fonts)) {
9894  	 	 	 	 $out .= ' /DR <<';
9895  	 	 	 	 $out .= ' /Font <<';
9896  	 	 	 	 foreach ($this->annotation_fonts as $fontkey => $fontid) {
9897  	 	 	 	 	 $out .= ' /F'.$fontid.' '.$this->font_obj_ids[$fontkey].' 0 R';
9898  	 	 	 	 }
9899  	 	 	 	 $out .= ' >> >>';
9900  	 	 	 }
9901  	 	 	 $font = $this->getFontBuffer('helvetica');
9902  	 	 	 $out .= ' /DA (/F'.$font['i'].' 0 Tf 0 g)';
9903  	 	 	 $out .= ' /Q '.(($this->rtl)?'2':'0');
9904  	 	 	 //$out .= ' /XFA ';
9905  	 	 	 $out .= ' >>';
9906  	 	 	 // signatures
9907  	 	 	 if ($this->sign AND isset($this->signature_data['cert_type'])
9908  	 	 	 	 AND (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A'))) {
9909  	 	 	 	 if ($this->signature_data['cert_type'] > 0) {
9910  	 	 	 	 	 $out .= ' /Perms << /DocMDP '.($this->sig_obj_id + 1).' 0 R >>';
9911  	 	 	 	 } else {
9912  	 	 	 	 	 $out .= ' /Perms << /UR3 '.($this->sig_obj_id + 1).' 0 R >>';
9913  	 	 	 	 }
9914  	 	 	 }
9915  	 	 }
9916  	 	 //$out .= ' /Legal <<>>';
9917  	 	 //$out .= ' /Requirements []';
9918  	 	 //$out .= ' /Collection <<>>';
9919  	 	 //$out .= ' /NeedsRendering true';
9920  	 	 $out .= ' >>';
9921  	 	 $out .= "\n".'endobj';
9922  	 	 $this->_out($out);
9923  	 	 return $oid;
9924  	 }
9925  
9926  	 /**
9927  	  * Output viewer preferences.
9928  	  * @return string for viewer preferences
9929  	  * @author Nicola asuni
9930  	  * @since 3.1.000 (2008-06-09)
9931  	  * @protected
9932  	  */
9933  	protected function _putviewerpreferences() {
9934  	 	 $vp = $this->viewer_preferences;
9935  	 	 $out = ' /ViewerPreferences <<';
9936  	 	 if ($this->rtl) {
9937  	 	 	 $out .= ' /Direction /R2L';
9938  	 	 } else {
9939  	 	 	 $out .= ' /Direction /L2R';
9940  	 	 }
9941  	 	 if (isset($vp['HideToolbar']) AND ($vp['HideToolbar'])) {
9942  	 	 	 $out .= ' /HideToolbar true';
9943  	 	 }
9944  	 	 if (isset($vp['HideMenubar']) AND ($vp['HideMenubar'])) {
9945  	 	 	 $out .= ' /HideMenubar true';
9946  	 	 }
9947  	 	 if (isset($vp['HideWindowUI']) AND ($vp['HideWindowUI'])) {
9948  	 	 	 $out .= ' /HideWindowUI true';
9949  	 	 }
9950  	 	 if (isset($vp['FitWindow']) AND ($vp['FitWindow'])) {
9951  	 	 	 $out .= ' /FitWindow true';
9952  	 	 }
9953  	 	 if (isset($vp['CenterWindow']) AND ($vp['CenterWindow'])) {
9954  	 	 	 $out .= ' /CenterWindow true';
9955  	 	 }
9956  	 	 if (isset($vp['DisplayDocTitle']) AND ($vp['DisplayDocTitle'])) {
9957  	 	 	 $out .= ' /DisplayDocTitle true';
9958  	 	 }
9959  	 	 if (isset($vp['NonFullScreenPageMode'])) {
9960  	 	 	 $out .= ' /NonFullScreenPageMode /'.$vp['NonFullScreenPageMode'];
9961  	 	 }
9962  	 	 if (isset($vp['ViewArea'])) {
9963  	 	 	 $out .= ' /ViewArea /'.$vp['ViewArea'];
9964  	 	 }
9965  	 	 if (isset($vp['ViewClip'])) {
9966  	 	 	 $out .= ' /ViewClip /'.$vp['ViewClip'];
9967  	 	 }
9968  	 	 if (isset($vp['PrintArea'])) {
9969  	 	 	 $out .= ' /PrintArea /'.$vp['PrintArea'];
9970  	 	 }
9971  	 	 if (isset($vp['PrintClip'])) {
9972  	 	 	 $out .= ' /PrintClip /'.$vp['PrintClip'];
9973  	 	 }
9974  	 	 if (isset($vp['PrintScaling'])) {
9975  	 	 	 $out .= ' /PrintScaling /'.$vp['PrintScaling'];
9976  	 	 }
9977  	 	 if (isset($vp['Duplex']) AND (!TCPDF_STATIC::empty_string($vp['Duplex']))) {
9978  	 	 	 $out .= ' /Duplex /'.$vp['Duplex'];
9979  	 	 }
9980  	 	 if (isset($vp['PickTrayByPDFSize'])) {
9981  	 	 	 if ($vp['PickTrayByPDFSize']) {
9982  	 	 	 	 $out .= ' /PickTrayByPDFSize true';
9983  	 	 	 } else {
9984  	 	 	 	 $out .= ' /PickTrayByPDFSize false';
9985  	 	 	 }
9986  	 	 }
9987  	 	 if (isset($vp['PrintPageRange'])) {
9988  	 	 	 $PrintPageRangeNum = '';
9989  	 	 	 foreach ($vp['PrintPageRange'] as $k => $v) {
9990  	 	 	 	 $PrintPageRangeNum .= ' '.($v - 1).'';
9991  	 	 	 }
9992  	 	 	 $out .= ' /PrintPageRange ['.substr($PrintPageRangeNum,1).']';
9993  	 	 }
9994  	 	 if (isset($vp['NumCopies'])) {
9995  	 	 	 $out .= ' /NumCopies '.intval($vp['NumCopies']);
9996  	 	 }
9997  	 	 $out .= ' >>';
9998  	 	 return $out;
9999  	 }
10000  
10001  	 /**
10002  	  * Output PDF File Header (7.5.2).
10003  	  * @protected
10004  	  */
10005  	protected function _putheader() {
10006  	 	 $this->_out('%PDF-'.$this->PDFVersion);
10007  	 	 $this->_out('%'.chr(0xe2).chr(0xe3).chr(0xcf).chr(0xd3));
10008  	 }
10009  
10010  	 /**
10011  	  * Output end of document (EOF).
10012  	  * @protected
10013  	  */
10014  	protected function _enddoc() {
10015  	 	 if (isset($this->CurrentFont['fontkey']) AND isset($this->CurrentFont['subsetchars'])) {
10016  	 	 	 // save subset chars of the previous font
10017  	 	 	 $this->setFontSubBuffer($this->CurrentFont['fontkey'], 'subsetchars', $this->CurrentFont['subsetchars']);
10018  	 	 }
10019  	 	 $this->state = 1;
10020  	 	 $this->_putheader();
10021  	 	 $this->_putpages();
10022  	 	 $this->_putresources();
10023  	 	 // empty signature fields
10024  	 	 if (!empty($this->empty_signature_appearance)) {
10025  	 	 	 foreach ($this->empty_signature_appearance as $key => $esa) {
10026  	 	 	 	 // widget annotation for empty signature
10027  	 	 	 	 $out = $this->_getobj($esa['objid'])."\n";
10028  	 	 	 	 $out .= '<< /Type /Annot';
10029  	 	 	 	 $out .= ' /Subtype /Widget';
10030  	 	 	 	 $out .= ' /Rect ['.$esa['rect'].']';
10031  	 	 	 	 $out .= ' /P '.$this->page_obj_id[($esa['page'])].' 0 R'; // link to signature appearance page
10032  	 	 	 	 $out .= ' /F 4';
10033  	 	 	 	 $out .= ' /FT /Sig';
10034  	 	 	 	 $signame = $esa['name'].sprintf(' [%03d]', ($key + 1));
10035  	 	 	 	 $out .= ' /T '.$this->_textstring($signame, $esa['objid']);
10036  	 	 	 	 $out .= ' /Ff 0';
10037  	 	 	 	 $out .= ' >>';
10038  	 	 	 	 $out .= "\n".'endobj';
10039  	 	 	 	 $this->_out($out);
10040  	 	 	 }
10041  	 	 }
10042  	 	 // Signature
10043  	 	 if ($this->sign AND isset($this->signature_data['cert_type'])) {
10044  	 	 	 // widget annotation for signature
10045  	 	 	 $out = $this->_getobj($this->sig_obj_id)."\n";
10046  	 	 	 $out .= '<< /Type /Annot';
10047  	 	 	 $out .= ' /Subtype /Widget';
10048  	 	 	 $out .= ' /Rect ['.$this->signature_appearance['rect'].']';
10049  	 	 	 $out .= ' /P '.$this->page_obj_id[($this->signature_appearance['page'])].' 0 R'; // link to signature appearance page
10050  	 	 	 $out .= ' /F 4';
10051  	 	 	 $out .= ' /FT /Sig';
10052  	 	 	 $out .= ' /T '.$this->_textstring($this->signature_appearance['name'], $this->sig_obj_id);
10053  	 	 	 $out .= ' /Ff 0';
10054  	 	 	 $out .= ' /V '.($this->sig_obj_id + 1).' 0 R';
10055  	 	 	 $out .= ' >>';
10056  	 	 	 $out .= "\n".'endobj';
10057  	 	 	 $this->_out($out);
10058  	 	 	 // signature
10059  	 	 	 $this->_putsignature();
10060  	 	 }
10061  	 	 // Info
10062  	 	 $objid_info = $this->_putinfo();
10063  	 	 // Catalog
10064  	 	 $objid_catalog = $this->_putcatalog();
10065  	 	 // Cross-ref
10066  	 	 $o = $this->bufferlen;
10067  	 	 // XREF section
10068  	 	 $this->_out('xref');
10069  	 	 $this->_out('0 '.($this->n + 1));
10070  	 	 $this->_out('0000000000 65535 f ');
10071  	 	 $freegen = ($this->n + 2);
10072  	 	 for ($i=1; $i <= $this->n; ++$i) {
10073  	 	 	 if (!isset($this->offsets[$i]) AND ($i > 1)) {
10074  	 	 	 	 $this->_out(sprintf('0000000000 %05d f ', $freegen));
10075  	 	 	 	 ++$freegen;
10076  	 	 	 } else {
10077  	 	 	 	 $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
10078  	 	 	 }
10079  	 	 }
10080  	 	 // TRAILER
10081  	 	 $out = 'trailer'."\n";
10082  	 	 $out .= '<<';
10083  	 	 $out .= ' /Size '.($this->n + 1);
10084  	 	 $out .= ' /Root '.$objid_catalog.' 0 R';
10085  	 	 $out .= ' /Info '.$objid_info.' 0 R';
10086  	 	 if ($this->encrypted) {
10087  	 	 	 $out .= ' /Encrypt '.$this->encryptdata['objid'].' 0 R';
10088  	 	 }
10089  	 	 $out .= ' /ID [ <'.$this->file_id.'> <'.$this->file_id.'> ]';
10090  	 	 $out .= ' >>';
10091  	 	 $this->_out($out);
10092  	 	 $this->_out('startxref');
10093  	 	 $this->_out($o);
10094  	 	 $this->_out('%%EOF');
10095  	 	 $this->state = 3; // end-of-doc
10096  	 }
10097  
10098  	 /**
10099  	  * Initialize a new page.
10100  	  * @param $orientation (string) page orientation. Possible values are (case insensitive):<ul><li>P or PORTRAIT (default)</li><li>L or LANDSCAPE</li></ul>
10101  	  * @param $format (mixed) The format used for pages. It can be either: one of the string values specified at getPageSizeFromFormat() or an array of parameters specified at setPageFormat().
10102  	  * @protected
10103  	  * @see getPageSizeFromFormat(), setPageFormat()
10104  	  */
10105  	protected function _beginpage($orientation='', $format='') {
10106  	 	 ++$this->page;
10107  	 	 $this->pageobjects[$this->page] = array();
10108  	 	 $this->setPageBuffer($this->page, '');
10109  	 	 // initialize array for graphics tranformation positions inside a page buffer
10110  	 	 $this->transfmrk[$this->page] = array();
10111  	 	 $this->state = 2;
10112  	 	 if (TCPDF_STATIC::empty_string($orientation)) {
10113  	 	 	 if (isset($this->CurOrientation)) {
10114  	 	 	 	 $orientation = $this->CurOrientation;
10115  	 	 	 } elseif ($this->fwPt > $this->fhPt) {
10116  	 	 	 	 // landscape
10117  	 	 	 	 $orientation = 'L';
10118  	 	 	 } else {
10119  	 	 	 	 // portrait
10120  	 	 	 	 $orientation = 'P';
10121  	 	 	 }
10122  	 	 }
10123  	 	 if (TCPDF_STATIC::empty_string($format)) {
10124  	 	 	 $this->pagedim[$this->page] = $this->pagedim[($this->page - 1)];
10125  	 	 	 $this->setPageOrientation($orientation);
10126  	 	 } else {
10127  	 	 	 $this->setPageFormat($format, $orientation);
10128  	 	 }
10129  	 	 if ($this->rtl) {
10130  	 	 	 $this->x = $this->w - $this->rMargin;
10131  	 	 } else {
10132  	 	 	 $this->x = $this->lMargin;
10133  	 	 }
10134  	 	 $this->y = $this->tMargin;
10135  	 	 if (isset($this->newpagegroup[$this->page])) {
10136  	 	 	 // start a new group
10137  	 	 	 $this->currpagegroup = $this->newpagegroup[$this->page];
10138  	 	 	 $this->pagegroups[$this->currpagegroup] = 1;
10139  	 	 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
10140  	 	 	 ++$this->pagegroups[$this->currpagegroup];
10141  	 	 }
10142  	 }
10143  
10144  	 /**
10145  	  * Mark end of page.
10146  	  * @protected
10147  	  */
10148  	protected function _endpage() {
10149  	 	 $this->setVisibility('all');
10150  	 	 $this->state = 1;
10151  	 }
10152  
10153  	 /**
10154  	  * Begin a new object and return the object number.
10155  	  * @return int object number
10156  	  * @protected
10157  	  */
10158  	protected function _newobj() {
10159  	 	 $this->_out($this->_getobj());
10160  	 	 return $this->n;
10161  	 }
10162  
10163  	 /**
10164  	  * Return the starting object string for the selected object ID.
10165  	  * @param $objid (int) Object ID (leave empty to get a new ID).
10166  	  * @return string the starting object string
10167  	  * @protected
10168  	  * @since 5.8.009 (2010-08-20)
10169  	  */
10170  	protected function _getobj($objid='') {
10171  	 	 if ($objid === '') {
10172  	 	 	 ++$this->n;
10173  	 	 	 $objid = $this->n;
10174  	 	 }
10175  	 	 $this->offsets[$objid] = $this->bufferlen;
10176  	 	 $this->pageobjects[$this->page][] = $objid;
10177  	 	 return $objid.' 0 obj';
10178  	 }
10179  
10180  	 /**
10181  	  * Underline text.
10182  	  * @param $x (int) X coordinate
10183  	  * @param $y (int) Y coordinate
10184  	  * @param $txt (string) text to underline
10185  	  * @protected
10186  	  */
10187  	protected function _dounderline($x, $y, $txt) {
10188  	 	 $w = $this->GetStringWidth($txt);
10189  	 	 return $this->_dounderlinew($x, $y, $w);
10190  	 }
10191  
10192  	 /**
10193  	  * Underline for rectangular text area.
10194  	  * @param $x (int) X coordinate
10195  	  * @param $y (int) Y coordinate
10196  	  * @param $w (int) width to underline
10197  	  * @protected
10198  	  * @since 4.8.008 (2009-09-29)
10199  	  */
10200  	protected function _dounderlinew($x, $y, $w) {
10201  	 	 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10202  	 	 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew), $w * $this->k, $linew);
10203  	 }
10204  
10205  	 /**
10206  	  * Line through text.
10207  	  * @param $x (int) X coordinate
10208  	  * @param $y (int) Y coordinate
10209  	  * @param $txt (string) text to linethrough
10210  	  * @protected
10211  	  */
10212  	protected function _dolinethrough($x, $y, $txt) {
10213  	 	 $w = $this->GetStringWidth($txt);
10214  	 	 return $this->_dolinethroughw($x, $y, $w);
10215  	 }
10216  
10217  	 /**
10218  	  * Line through for rectangular text area.
10219  	  * @param $x (int) X coordinate
10220  	  * @param $y (int) Y coordinate
10221  	  * @param $w (int) line length (width)
10222  	  * @protected
10223  	  * @since 4.9.008 (2009-09-29)
10224  	  */
10225  	protected function _dolinethroughw($x, $y, $w) {
10226  	 	 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10227  	 	 return sprintf('%F %F %F %F re f', $x * $this->k, ((($this->h - $y) * $this->k) + $linew + ($this->FontSizePt / 3)), $w * $this->k, $linew);
10228  	 }
10229  
10230  	 /**
10231  	  * Overline text.
10232  	  * @param $x (int) X coordinate
10233  	  * @param $y (int) Y coordinate
10234  	  * @param $txt (string) text to overline
10235  	  * @protected
10236  	  * @since 4.9.015 (2010-04-19)
10237  	  */
10238  	protected function _dooverline($x, $y, $txt) {
10239  	 	 $w = $this->GetStringWidth($txt);
10240  	 	 return $this->_dooverlinew($x, $y, $w);
10241  	 }
10242  
10243  	 /**
10244  	  * Overline for rectangular text area.
10245  	  * @param $x (int) X coordinate
10246  	  * @param $y (int) Y coordinate
10247  	  * @param $w (int) width to overline
10248  	  * @protected
10249  	  * @since 4.9.015 (2010-04-19)
10250  	  */
10251  	protected function _dooverlinew($x, $y, $w) {
10252  	 	 $linew = - $this->CurrentFont['ut'] / 1000 * $this->FontSizePt;
10253  	 	 return sprintf('%F %F %F %F re f', $x * $this->k, (($this->h - $y + $this->FontAscent) * $this->k) - $linew, $w * $this->k, $linew);
10254  
10255  	 }
10256  
10257  	 /**
10258  	  * Format a data string for meta information
10259  	  * @param $s (string) data string to escape.
10260  	  * @param $n (int) object ID
10261  	  * @return string escaped string.
10262  	  * @protected
10263  	  */
10264  	protected function _datastring($s, $n=0) {
10265  	 	 if ($n == 0) {
10266  	 	 	 $n = $this->n;
10267  	 	 }
10268  	 	 $s = $this->_encrypt_data($n, $s);
10269  	 	 return '('. TCPDF_STATIC::_escape($s).')';
10270  	 }
10271  
10272  	 /**
10273  	  * Set the document creation timestamp
10274  	  * @param $time (mixed) Document creation timestamp in seconds or date-time string.
10275  	  * @public
10276  	  * @since 5.9.152 (2012-03-23)
10277  	  */
10278  	public function setDocCreationTimestamp($time) {
10279  	 	 if (is_string($time)) {
10280  	 	 	 $time = TCPDF_STATIC::getTimestamp($time);
10281  	 	 }
10282  	 	 $this->doc_creation_timestamp = intval($time);
10283  	 }
10284  
10285  	 /**
10286  	  * Set the document modification timestamp
10287  	  * @param $time (mixed) Document modification timestamp in seconds or date-time string.
10288  	  * @public
10289  	  * @since 5.9.152 (2012-03-23)
10290  	  */
10291  	public function setDocModificationTimestamp($time) {
10292  	 	 if (is_string($time)) {
10293  	 	 	 $time = TCPDF_STATIC::getTimestamp($time);
10294  	 	 }
10295  	 	 $this->doc_modification_timestamp = intval($time);
10296  	 }
10297  
10298  	 /**
10299  	  * Returns document creation timestamp in seconds.
10300  	  * @return (int) Creation timestamp in seconds.
10301  	  * @public
10302  	  * @since 5.9.152 (2012-03-23)
10303  	  */
10304  	public function getDocCreationTimestamp() {
10305  	 	 return $this->doc_creation_timestamp;
10306  	 }
10307  
10308  	 /**
10309  	  * Returns document modification timestamp in seconds.
10310  	  * @return (int) Modfication timestamp in seconds.
10311  	  * @public
10312  	  * @since 5.9.152 (2012-03-23)
10313  	  */
10314  	public function getDocModificationTimestamp() {
10315  	 	 return $this->doc_modification_timestamp;
10316  	 }
10317  
10318  	 /**
10319  	  * Returns a formatted date for meta information
10320  	  * @param $n (int) Object ID.
10321  	  * @param $timestamp (int) Timestamp to convert.
10322  	  * @return string escaped date string.
10323  	  * @protected
10324  	  * @since 4.6.028 (2009-08-25)
10325  	  */
10326  	protected function _datestring($n=0, $timestamp=0) {
10327  	 	 if ((empty($timestamp)) OR ($timestamp < 0)) {
10328  	 	 	 $timestamp = $this->doc_creation_timestamp;
10329  	 	 }
10330  	 	 return $this->_datastring('D:'.TCPDF_STATIC::getFormattedDate($timestamp), $n);
10331  	 }
10332  
10333  	 /**
10334  	  * Format a text string for meta information
10335  	  * @param $s (string) string to escape.
10336  	  * @param $n (int) object ID
10337  	  * @return string escaped string.
10338  	  * @protected
10339  	  */
10340  	protected function _textstring($s, $n=0) {
10341  	 	 if ($this->isunicode) {
10342  	 	 	 //Convert string to UTF-16BE
10343  	 	 	 $s = TCPDF_FONTS::UTF8ToUTF16BE($s, true, $this->isunicode, $this->CurrentFont);
10344  	 	 }
10345  	 	 return $this->_datastring($s, $n);
10346  	 }
10347  
10348  	 /**
10349  	  * get raw output stream.
10350  	  * @param $s (string) string to output.
10351  	  * @param $n (int) object reference for encryption mode
10352  	  * @protected
10353  	  * @author Nicola Asuni
10354  	  * @since 5.5.000 (2010-06-22)
10355  	  */
10356  	protected function _getrawstream($s, $n=0) {
10357  	 	 if ($n <= 0) {
10358  	 	 	 // default to current object
10359  	 	 	 $n = $this->n;
10360  	 	 }
10361  	 	 return $this->_encrypt_data($n, $s);
10362  	 }
10363  
10364  	 /**
10365  	  * Output a string to the document.
10366  	  * @param $s (string) string to output.
10367  	  * @protected
10368  	  */
10369  	protected function _out($s) {
10370  	 	 if ($this->state == 2) {
10371  	 	 	 if ($this->inxobj) {
10372  	 	 	 	 // we are inside an XObject template
10373  	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] .= $s."\n";
10374  	 	 	 } elseif ((!$this->InFooter) AND isset($this->footerlen[$this->page]) AND ($this->footerlen[$this->page] > 0)) {
10375  	 	 	 	 // puts data before page footer
10376  	 	 	 	 $pagebuff = $this->getPageBuffer($this->page);
10377  	 	 	 	 $page = substr($pagebuff, 0, -$this->footerlen[$this->page]);
10378  	 	 	 	 $footer = substr($pagebuff, -$this->footerlen[$this->page]);
10379  	 	 	 	 $this->setPageBuffer($this->page, $page.$s."\n".$footer);
10380  	 	 	 	 // update footer position
10381  	 	 	 	 $this->footerpos[$this->page] += strlen($s."\n");
10382  	 	 	 } else {
10383  	 	 	 	 // set page data
10384  	 	 	 	 $this->setPageBuffer($this->page, $s."\n", true);
10385  	 	 	 }
10386  	 	 } elseif ($this->state > 0) {
10387  	 	 	 // set general data
10388  	 	 	 $this->setBuffer($s."\n");
10389  	 	 }
10390  	 }
10391  
10392  	 /**
10393  	  * Set header font.
10394  	  * @param $font (array) Array describing the basic font parameters: (family, style, size).
10395  	  * @public
10396  	  * @since 1.1
10397  	  */
10398  	public function setHeaderFont($font) {
10399  	 	 $this->header_font = $font;
10400  	 }
10401  
10402  	 /**
10403  	  * Get header font.
10404  	  * @return array() Array describing the basic font parameters: (family, style, size).
10405  	  * @public
10406  	  * @since 4.0.012 (2008-07-24)
10407  	  */
10408  	public function getHeaderFont() {
10409  	 	 return $this->header_font;
10410  	 }
10411  
10412  	 /**
10413  	  * Set footer font.
10414  	  * @param $font (array) Array describing the basic font parameters: (family, style, size).
10415  	  * @public
10416  	  * @since 1.1
10417  	  */
10418  	public function setFooterFont($font) {
10419  	 	 $this->footer_font = $font;
10420  	 }
10421  
10422  	 /**
10423  	  * Get Footer font.
10424  	  * @return array() Array describing the basic font parameters: (family, style, size).
10425  	  * @public
10426  	  * @since 4.0.012 (2008-07-24)
10427  	  */
10428  	public function getFooterFont() {
10429  	 	 return $this->footer_font;
10430  	 }
10431  
10432  	 /**
10433  	  * Set language array.
10434  	  * @param $language (array)
10435  	  * @public
10436  	  * @since 1.1
10437  	  */
10438  	public function setLanguageArray($language) {
10439  	 	 $this->l = $language;
10440  	 	 if (isset($this->l['a_meta_dir'])) {
10441  	 	 	 $this->rtl = $this->l['a_meta_dir']=='rtl' ? true : false;
10442  	 	 } else {
10443  	 	 	 $this->rtl = false;
10444  	 	 }
10445  	 }
10446  
10447  	 /**
10448  	  * Returns the PDF data.
10449  	  * @public
10450  	  */
10451  	public function getPDFData() {
10452  	 	 if ($this->state < 3) {
10453  	 	 	 $this->Close();
10454  	 	 }
10455  	 	 return $this->buffer;
10456  	 }
10457  
10458  	 /**
10459  	  * Output anchor link.
10460  	  * @param $url (string) link URL or internal link (i.e.: &lt;a href="#23,4.5"&gt;link to page 23 at 4.5 Y position&lt;/a&gt;)
10461  	  * @param $name (string) link name
10462  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
10463  	  * @param $firstline (boolean) if true prints only the first line and return the remaining string.
10464  	  * @param $color (array) array of RGB text color
10465  	  * @param $style (string) font style (U, D, B, I)
10466  	  * @param $firstblock (boolean) if true the string is the starting of a line.
10467  	  * @return the number of cells used or the remaining text if $firstline = true;
10468  	  * @public
10469  	  */
10470  	public function addHtmlLink($url, $name, $fill=false, $firstline=false, $color='', $style=-1, $firstblock=false) {
10471  	 	 if (isset($url[1]) AND ($url[0] == '#') AND is_numeric($url[1])) {
10472  	 	 	 // convert url to internal link
10473  	 	 	 $lnkdata = explode(',', $url);
10474  	 	 	 if (isset($lnkdata[0]) ) {
10475  	 	 	 	 $page = substr($lnkdata[0], 1);
10476  	 	 	 	 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
10477  	 	 	 	 	 $lnky = floatval($lnkdata[1]);
10478  	 	 	 	 } else {
10479  	 	 	 	 	 $lnky = 0;
10480  	 	 	 	 }
10481  	 	 	 	 $url = $this->AddLink();
10482  	 	 	 	 $this->SetLink($url, $lnky, $page);
10483  	 	 	 }
10484  	 	 }
10485  	 	 // store current settings
10486  	 	 $prevcolor = $this->fgcolor;
10487  	 	 $prevstyle = $this->FontStyle;
10488  	 	 if (empty($color)) {
10489  	 	 	 $this->SetTextColorArray($this->htmlLinkColorArray);
10490  	 	 } else {
10491  	 	 	 $this->SetTextColorArray($color);
10492  	 	 }
10493  	 	 if ($style == -1) {
10494  	 	 	 $this->SetFont('', $this->FontStyle.$this->htmlLinkFontStyle);
10495  	 	 } else {
10496  	 	 	 $this->SetFont('', $this->FontStyle.$style);
10497  	 	 }
10498  	 	 $ret = $this->Write($this->lasth, $name, $url, $fill, '', false, 0, $firstline, $firstblock, 0);
10499  	 	 // restore settings
10500  	 	 $this->SetFont('', $prevstyle);
10501  	 	 $this->SetTextColorArray($prevcolor);
10502  	 	 return $ret;
10503  	 }
10504  
10505  	 /**
10506  	  * Converts pixels to User's Units.
10507  	  * @param $px (int) pixels
10508  	  * @return float value in user's unit
10509  	  * @public
10510  	  * @see setImageScale(), getImageScale()
10511  	  */
10512  	public function pixelsToUnits($px) {
10513  	 	 return ($px / ($this->imgscale * $this->k));
10514  	 }
10515  
10516  	 /**
10517  	  * Reverse function for htmlentities.
10518  	  * Convert entities in UTF-8.
10519  	  * @param $text_to_convert (string) Text to convert.
10520  	  * @return string converted text string
10521  	  * @public
10522  	  */
10523  	public function unhtmlentities($text_to_convert) {
10524  	 	 return @html_entity_decode($text_to_convert, ENT_QUOTES, $this->encoding);
10525  	 }
10526  
10527  	 // ENCRYPTION METHODS ----------------------------------
10528  
10529  	 /**
10530  	  * Compute encryption key depending on object number where the encrypted data is stored.
10531  	  * This is used for all strings and streams without crypt filter specifier.
10532  	  * @param $n (int) object number
10533  	  * @return int object key
10534  	  * @protected
10535  	  * @author Nicola Asuni
10536  	  * @since 2.0.000 (2008-01-02)
10537  	  */
10538  	protected function _objectkey($n) {
10539  	 	 $objkey = $this->encryptdata['key'].pack('VXxx', $n);
10540  	 	 if ($this->encryptdata['mode'] == 2) { // AES-128
10541  	 	 	 // AES padding
10542  	 	 	 $objkey .= "\x73\x41\x6C\x54"; // sAlT
10543  	 	 }
10544  	 	 $objkey = substr(TCPDF_STATIC::_md5_16($objkey), 0, (($this->encryptdata['Length'] / 8) + 5));
10545  	 	 $objkey = substr($objkey, 0, 16);
10546  	 	 return $objkey;
10547  	 }
10548  
10549  	 /**
10550  	  * Encrypt the input string.
10551  	  * @param $n (int) object number
10552  	  * @param $s (string) data string to encrypt
10553  	  * @return encrypted string
10554  	  * @protected
10555  	  * @author Nicola Asuni
10556  	  * @since 5.0.005 (2010-05-11)
10557  	  */
10558  	protected function _encrypt_data($n, $s) {
10559  	 	 if (!$this->encrypted) {
10560  	 	 	 return $s;
10561  	 	 }
10562  	 	 switch ($this->encryptdata['mode']) {
10563  	 	 	 case 0:   // RC4-40
10564  	 	 	 case 1: { // RC4-128
10565  	 	 	 	 $s = TCPDF_STATIC::_RC4($this->_objectkey($n), $s, $this->last_enc_key, $this->last_enc_key_c);
10566  	 	 	 	 break;
10567  	 	 	 }
10568  	 	 	 case 2: { // AES-128
10569  	 	 	 	 $s = TCPDF_STATIC::_AES($this->_objectkey($n), $s);
10570  	 	 	 	 break;
10571  	 	 	 }
10572  	 	 	 case 3: { // AES-256
10573  	 	 	 	 $s = TCPDF_STATIC::_AES($this->encryptdata['key'], $s);
10574  	 	 	 	 break;
10575  	 	 	 }
10576  	 	 }
10577  	 	 return $s;
10578  	 }
10579  
10580  	 /**
10581  	  * Put encryption on PDF document.
10582  	  * @protected
10583  	  * @author Nicola Asuni
10584  	  * @since 2.0.000 (2008-01-02)
10585  	  */
10586  	protected function _putencryption() {
10587  	 	 if (!$this->encrypted) {
10588  	 	 	 return;
10589  	 	 }
10590  	 	 $this->encryptdata['objid'] = $this->_newobj();
10591  	 	 $out = '<<';
10592  	 	 if (!isset($this->encryptdata['Filter']) OR empty($this->encryptdata['Filter'])) {
10593  	 	 	 $this->encryptdata['Filter'] = 'Standard';
10594  	 	 }
10595  	 	 $out .= ' /Filter /'.$this->encryptdata['Filter'];
10596  	 	 if (isset($this->encryptdata['SubFilter']) AND !empty($this->encryptdata['SubFilter'])) {
10597  	 	 	 $out .= ' /SubFilter /'.$this->encryptdata['SubFilter'];
10598  	 	 }
10599  	 	 if (!isset($this->encryptdata['V']) OR empty($this->encryptdata['V'])) {
10600  	 	 	 $this->encryptdata['V'] = 1;
10601  	 	 }
10602  	 	 // V is a code specifying the algorithm to be used in encrypting and decrypting the document
10603  	 	 $out .= ' /V '.$this->encryptdata['V'];
10604  	 	 if (isset($this->encryptdata['Length']) AND !empty($this->encryptdata['Length'])) {
10605  	 	 	 // The length of the encryption key, in bits. The value shall be a multiple of 8, in the range 40 to 256
10606  	 	 	 $out .= ' /Length '.$this->encryptdata['Length'];
10607  	 	 } else {
10608  	 	 	 $out .= ' /Length 40';
10609  	 	 }
10610  	 	 if ($this->encryptdata['V'] >= 4) {
10611  	 	 	 if (!isset($this->encryptdata['StmF']) OR empty($this->encryptdata['StmF'])) {
10612  	 	 	 	 $this->encryptdata['StmF'] = 'Identity';
10613  	 	 	 }
10614  	 	 	 if (!isset($this->encryptdata['StrF']) OR empty($this->encryptdata['StrF'])) {
10615  	 	 	 	 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10616  	 	 	 	 $this->encryptdata['StrF'] = 'Identity';
10617  	 	 	 }
10618  	 	 	 // A dictionary whose keys shall be crypt filter names and whose values shall be the corresponding crypt filter dictionaries.
10619  	 	 	 if (isset($this->encryptdata['CF']) AND !empty($this->encryptdata['CF'])) {
10620  	 	 	 	 $out .= ' /CF <<';
10621  	 	 	 	 $out .= ' /'.$this->encryptdata['StmF'].' <<';
10622  	 	 	 	 $out .= ' /Type /CryptFilter';
10623  	 	 	 	 if (isset($this->encryptdata['CF']['CFM']) AND !empty($this->encryptdata['CF']['CFM'])) {
10624  	 	 	 	 	 // The method used
10625  	 	 	 	 	 $out .= ' /CFM /'.$this->encryptdata['CF']['CFM'];
10626  	 	 	 	 	 if ($this->encryptdata['pubkey']) {
10627  	 	 	 	 	 	 $out .= ' /Recipients [';
10628  	 	 	 	 	 	 foreach ($this->encryptdata['Recipients'] as $rec) {
10629  	 	 	 	 	 	 	 $out .= ' <'.$rec.'>';
10630  	 	 	 	 	 	 }
10631  	 	 	 	 	 	 $out .= ' ]';
10632  	 	 	 	 	 	 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) {
10633  	 	 	 	 	 	 	 $out .= ' /EncryptMetadata false';
10634  	 	 	 	 	 	 } else {
10635  	 	 	 	 	 	 	 $out .= ' /EncryptMetadata true';
10636  	 	 	 	 	 	 }
10637  	 	 	 	 	 }
10638  	 	 	 	 } else {
10639  	 	 	 	 	 $out .= ' /CFM /None';
10640  	 	 	 	 }
10641  	 	 	 	 if (isset($this->encryptdata['CF']['AuthEvent']) AND !empty($this->encryptdata['CF']['AuthEvent'])) {
10642  	 	 	 	 	 // The event to be used to trigger the authorization that is required to access encryption keys used by this filter.
10643  	 	 	 	 	 $out .= ' /AuthEvent /'.$this->encryptdata['CF']['AuthEvent'];
10644  	 	 	 	 } else {
10645  	 	 	 	 	 $out .= ' /AuthEvent /DocOpen';
10646  	 	 	 	 }
10647  	 	 	 	 if (isset($this->encryptdata['CF']['Length']) AND !empty($this->encryptdata['CF']['Length'])) {
10648  	 	 	 	 	 // The bit length of the encryption key.
10649  	 	 	 	 	 $out .= ' /Length '.$this->encryptdata['CF']['Length'];
10650  	 	 	 	 }
10651  	 	 	 	 $out .= ' >> >>';
10652  	 	 	 }
10653  	 	 	 // The name of the crypt filter that shall be used by default when decrypting streams.
10654  	 	 	 $out .= ' /StmF /'.$this->encryptdata['StmF'];
10655  	 	 	 // The name of the crypt filter that shall be used when decrypting all strings in the document.
10656  	 	 	 $out .= ' /StrF /'.$this->encryptdata['StrF'];
10657  	 	 	 if (isset($this->encryptdata['EFF']) AND !empty($this->encryptdata['EFF'])) {
10658  	 	 	 	 // The name of the crypt filter that shall be used when encrypting embedded file streams that do not have their own crypt filter specifier.
10659  	 	 	 	 $out .= ' /EFF /'.$this->encryptdata[''];
10660  	 	 	 }
10661  	 	 }
10662  	 	 // Additional encryption dictionary entries for the standard security handler
10663  	 	 if ($this->encryptdata['pubkey']) {
10664  	 	 	 if (($this->encryptdata['V'] < 4) AND isset($this->encryptdata['Recipients']) AND !empty($this->encryptdata['Recipients'])) {
10665  	 	 	 	 $out .= ' /Recipients [';
10666  	 	 	 	 foreach ($this->encryptdata['Recipients'] as $rec) {
10667  	 	 	 	 	 $out .= ' <'.$rec.'>';
10668  	 	 	 	 }
10669  	 	 	 	 $out .= ' ]';
10670  	 	 	 }
10671  	 	 } else {
10672  	 	 	 $out .= ' /R';
10673  	 	 	 if ($this->encryptdata['V'] == 5) { // AES-256
10674  	 	 	 	 $out .= ' 5';
10675  	 	 	 	 $out .= ' /OE ('.TCPDF_STATIC::_escape($this->encryptdata['OE']).')';
10676  	 	 	 	 $out .= ' /UE ('.TCPDF_STATIC::_escape($this->encryptdata['UE']).')';
10677  	 	 	 	 $out .= ' /Perms ('.TCPDF_STATIC::_escape($this->encryptdata['perms']).')';
10678  	 	 	 } elseif ($this->encryptdata['V'] == 4) { // AES-128
10679  	 	 	 	 $out .= ' 4';
10680  	 	 	 } elseif ($this->encryptdata['V'] < 2) { // RC-40
10681  	 	 	 	 $out .= ' 2';
10682  	 	 	 } else { // RC-128
10683  	 	 	 	 $out .= ' 3';
10684  	 	 	 }
10685  	 	 	 $out .= ' /O ('.TCPDF_STATIC::_escape($this->encryptdata['O']).')';
10686  	 	 	 $out .= ' /U ('.TCPDF_STATIC::_escape($this->encryptdata['U']).')';
10687  	 	 	 $out .= ' /P '.$this->encryptdata['P'];
10688  	 	 	 if (isset($this->encryptdata['EncryptMetadata']) AND (!$this->encryptdata['EncryptMetadata'])) {
10689  	 	 	 	 $out .= ' /EncryptMetadata false';
10690  	 	 	 } else {
10691  	 	 	 	 $out .= ' /EncryptMetadata true';
10692  	 	 	 }
10693  	 	 }
10694  	 	 $out .= ' >>';
10695  	 	 $out .= "\n".'endobj';
10696  	 	 $this->_out($out);
10697  	 }
10698  
10699  	 /**
10700  	  * Compute U value (used for encryption)
10701  	  * @return string U value
10702  	  * @protected
10703  	  * @since 2.0.000 (2008-01-02)
10704  	  * @author Nicola Asuni
10705  	  */
10706  	protected function _Uvalue() {
10707  	 	 if ($this->encryptdata['mode'] == 0) { // RC4-40
10708  	 	 	 return TCPDF_STATIC::_RC4($this->encryptdata['key'], TCPDF_STATIC::$enc_padding, $this->last_enc_key, $this->last_enc_key_c);
10709  	 	 } elseif ($this->encryptdata['mode'] < 3) { // RC4-128, AES-128
10710  	 	 	 $tmp = TCPDF_STATIC::_md5_16(TCPDF_STATIC::$enc_padding.$this->encryptdata['fileid']);
10711  	 	 	 $enc = TCPDF_STATIC::_RC4($this->encryptdata['key'], $tmp, $this->last_enc_key, $this->last_enc_key_c);
10712  	 	 	 $len = strlen($tmp);
10713  	 	 	 for ($i = 1; $i <= 19; ++$i) {
10714  	 	 	 	 $ek = '';
10715  	 	 	 	 for ($j = 0; $j < $len; ++$j) {
10716  	 	 	 	 	 $ek .= chr(ord($this->encryptdata['key'][$j]) ^ $i);
10717  	 	 	 	 }
10718  	 	 	 	 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10719  	 	 	 }
10720  	 	 	 $enc .= str_repeat("\x00", 16);
10721  	 	 	 return substr($enc, 0, 32);
10722  	 	 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10723  	 	 	 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10724  	 	 	 // User Validation Salt
10725  	 	 	 $this->encryptdata['UVS'] = substr($seed, 0, 8);
10726  	 	 	 // User Key Salt
10727  	 	 	 $this->encryptdata['UKS'] = substr($seed, 8, 16);
10728  	 	 	 return hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UVS'], true).$this->encryptdata['UVS'].$this->encryptdata['UKS'];
10729  	 	 }
10730  	 }
10731  
10732  	 /**
10733  	  * Compute UE value (used for encryption)
10734  	  * @return string UE value
10735  	  * @protected
10736  	  * @since 5.9.006 (2010-10-19)
10737  	  * @author Nicola Asuni
10738  	  */
10739  	protected function _UEvalue() {
10740  	 	 $hashkey = hash('sha256', $this->encryptdata['user_password'].$this->encryptdata['UKS'], true);
10741  	 	 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10742  	 }
10743  
10744  	 /**
10745  	  * Compute O value (used for encryption)
10746  	  * @return string O value
10747  	  * @protected
10748  	  * @since 2.0.000 (2008-01-02)
10749  	  * @author Nicola Asuni
10750  	  */
10751  	protected function _Ovalue() {
10752  	 	 if ($this->encryptdata['mode'] < 3) { // RC4-40, RC4-128, AES-128
10753  	 	 	 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['owner_password']);
10754  	 	 	 if ($this->encryptdata['mode'] > 0) {
10755  	 	 	 	 for ($i = 0; $i < 50; ++$i) {
10756  	 	 	 	 	 $tmp = TCPDF_STATIC::_md5_16($tmp);
10757  	 	 	 	 }
10758  	 	 	 }
10759  	 	 	 $owner_key = substr($tmp, 0, ($this->encryptdata['Length'] / 8));
10760  	 	 	 $enc = TCPDF_STATIC::_RC4($owner_key, $this->encryptdata['user_password'], $this->last_enc_key, $this->last_enc_key_c);
10761  	 	 	 if ($this->encryptdata['mode'] > 0) {
10762  	 	 	 	 $len = strlen($owner_key);
10763  	 	 	 	 for ($i = 1; $i <= 19; ++$i) {
10764  	 	 	 	 	 $ek = '';
10765  	 	 	 	 	 for ($j = 0; $j < $len; ++$j) {
10766  	 	 	 	 	 	 $ek .= chr(ord($owner_key[$j]) ^ $i);
10767  	 	 	 	 	 }
10768  	 	 	 	 	 $enc = TCPDF_STATIC::_RC4($ek, $enc, $this->last_enc_key, $this->last_enc_key_c);
10769  	 	 	 	 }
10770  	 	 	 }
10771  	 	 	 return $enc;
10772  	 	 } elseif ($this->encryptdata['mode'] == 3) { // AES-256
10773  	 	 	 $seed = TCPDF_STATIC::_md5_16(TCPDF_STATIC::getRandomSeed());
10774  	 	 	 // Owner Validation Salt
10775  	 	 	 $this->encryptdata['OVS'] = substr($seed, 0, 8);
10776  	 	 	 // Owner Key Salt
10777  	 	 	 $this->encryptdata['OKS'] = substr($seed, 8, 16);
10778  	 	 	 return hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OVS'].$this->encryptdata['U'], true).$this->encryptdata['OVS'].$this->encryptdata['OKS'];
10779  	 	 }
10780  	 }
10781  
10782  	 /**
10783  	  * Compute OE value (used for encryption)
10784  	  * @return string OE value
10785  	  * @protected
10786  	  * @since 5.9.006 (2010-10-19)
10787  	  * @author Nicola Asuni
10788  	  */
10789  	protected function _OEvalue() {
10790  	 	 $hashkey = hash('sha256', $this->encryptdata['owner_password'].$this->encryptdata['OKS'].$this->encryptdata['U'], true);
10791  	 	 return TCPDF_STATIC::_AESnopad($hashkey, $this->encryptdata['key']);
10792  	 }
10793  
10794  	 /**
10795  	  * Convert password for AES-256 encryption mode
10796  	  * @param $password (string) password
10797  	  * @return string password
10798  	  * @protected
10799  	  * @since 5.9.006 (2010-10-19)
10800  	  * @author Nicola Asuni
10801  	  */
10802  	protected function _fixAES256Password($password) {
10803  	 	 $psw = ''; // password to be returned
10804  	 	 $psw_array = TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($password, $this->isunicode, $this->CurrentFont), $password, $this->rtl, $this->isunicode, $this->CurrentFont);
10805  	 	 foreach ($psw_array as $c) {
10806  	 	 	 $psw .= TCPDF_FONTS::unichr($c, $this->isunicode);
10807  	 	 }
10808  	 	 return substr($psw, 0, 127);
10809  	 }
10810  
10811  	 /**
10812  	  * Compute encryption key
10813  	  * @protected
10814  	  * @since 2.0.000 (2008-01-02)
10815  	  * @author Nicola Asuni
10816  	  */
10817  	protected function _generateencryptionkey() {
10818  	 	 $keybytelen = ($this->encryptdata['Length'] / 8);
10819  	 	 if (!$this->encryptdata['pubkey']) { // standard mode
10820  	 	 	 if ($this->encryptdata['mode'] == 3) { // AES-256
10821  	 	 	 	 // generate 256 bit random key
10822  	 	 	 	 $this->encryptdata['key'] = substr(hash('sha256', TCPDF_STATIC::getRandomSeed(), true), 0, $keybytelen);
10823  	 	 	 	 // truncate passwords
10824  	 	 	 	 $this->encryptdata['user_password'] = $this->_fixAES256Password($this->encryptdata['user_password']);
10825  	 	 	 	 $this->encryptdata['owner_password'] = $this->_fixAES256Password($this->encryptdata['owner_password']);
10826  	 	 	 	 // Compute U value
10827  	 	 	 	 $this->encryptdata['U'] = $this->_Uvalue();
10828  	 	 	 	 // Compute UE value
10829  	 	 	 	 $this->encryptdata['UE'] = $this->_UEvalue();
10830  	 	 	 	 // Compute O value
10831  	 	 	 	 $this->encryptdata['O'] = $this->_Ovalue();
10832  	 	 	 	 // Compute OE value
10833  	 	 	 	 $this->encryptdata['OE'] = $this->_OEvalue();
10834  	 	 	 	 // Compute P value
10835  	 	 	 	 $this->encryptdata['P'] = $this->encryptdata['protection'];
10836  	 	 	 	 // Computing the encryption dictionary's Perms (permissions) value
10837  	 	 	 	 $perms = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']); // bytes 0-3
10838  	 	 	 	 $perms .= chr(255).chr(255).chr(255).chr(255); // bytes 4-7
10839  	 	 	 	 if (isset($this->encryptdata['CF']['EncryptMetadata']) AND (!$this->encryptdata['CF']['EncryptMetadata'])) { // byte 8
10840  	 	 	 	 	 $perms .= 'F';
10841  	 	 	 	 } else {
10842  	 	 	 	 	 $perms .= 'T';
10843  	 	 	 	 }
10844  	 	 	 	 $perms .= 'adb'; // bytes 9-11
10845  	 	 	 	 $perms .= 'nick'; // bytes 12-15
10846  	 	 	 	 $this->encryptdata['perms'] = TCPDF_STATIC::_AESnopad($this->encryptdata['key'], $perms);
10847  	 	 	 } else { // RC4-40, RC4-128, AES-128
10848  	 	 	 	 // Pad passwords
10849  	 	 	 	 $this->encryptdata['user_password'] = substr($this->encryptdata['user_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10850  	 	 	 	 $this->encryptdata['owner_password'] = substr($this->encryptdata['owner_password'].TCPDF_STATIC::$enc_padding, 0, 32);
10851  	 	 	 	 // Compute O value
10852  	 	 	 	 $this->encryptdata['O'] = $this->_Ovalue();
10853  	 	 	 	 // get default permissions (reverse byte order)
10854  	 	 	 	 $permissions = TCPDF_STATIC::getEncPermissionsString($this->encryptdata['protection']);
10855  	 	 	 	 // Compute encryption key
10856  	 	 	 	 $tmp = TCPDF_STATIC::_md5_16($this->encryptdata['user_password'].$this->encryptdata['O'].$permissions.$this->encryptdata['fileid']);
10857  	 	 	 	 if ($this->encryptdata['mode'] > 0) {
10858  	 	 	 	 	 for ($i = 0; $i < 50; ++$i) {
10859  	 	 	 	 	 	 $tmp = TCPDF_STATIC::_md5_16(substr($tmp, 0, $keybytelen));
10860  	 	 	 	 	 }
10861  	 	 	 	 }
10862  	 	 	 	 $this->encryptdata['key'] = substr($tmp, 0, $keybytelen);
10863  	 	 	 	 // Compute U value
10864  	 	 	 	 $this->encryptdata['U'] = $this->_Uvalue();
10865  	 	 	 	 // Compute P value
10866  	 	 	 	 $this->encryptdata['P'] = $this->encryptdata['protection'];
10867  	 	 	 }
10868  	 	 } else { // Public-Key mode
10869  	 	 	 // random 20-byte seed
10870  	 	 	 $seed = sha1(TCPDF_STATIC::getRandomSeed(), true);
10871  	 	 	 $recipient_bytes = '';
10872  	 	 	 foreach ($this->encryptdata['pubkeys'] as $pubkey) {
10873  	 	 	 	 // for each public certificate
10874  	 	 	 	 if (isset($pubkey['p'])) {
10875  	 	 	 	 	 $pkprotection = TCPDF_STATIC::getUserPermissionCode($pubkey['p'], $this->encryptdata['mode']);
10876  	 	 	 	 } else {
10877  	 	 	 	 	 $pkprotection = $this->encryptdata['protection'];
10878  	 	 	 	 }
10879  	 	 	 	 // get default permissions (reverse byte order)
10880  	 	 	 	 $pkpermissions = TCPDF_STATIC::getEncPermissionsString($pkprotection);
10881  	 	 	 	 // envelope data
10882  	 	 	 	 $envelope = $seed.$pkpermissions;
10883  	 	 	 	 // write the envelope data to a temporary file
10884  	 	 	 	 $tempkeyfile = TCPDF_STATIC::getObjFilename('key', $this->file_id);
10885  	 	 	 	 $f = TCPDF_STATIC::fopenLocal($tempkeyfile, 'wb');
10886  	 	 	 	 if (!$f) {
10887  	 	 	 	 	 $this->Error('Unable to create temporary key file: '.$tempkeyfile);
10888  	 	 	 	 }
10889  	 	 	 	 $envelope_length = strlen($envelope);
10890  	 	 	 	 fwrite($f, $envelope, $envelope_length);
10891  	 	 	 	 fclose($f);
10892  	 	 	 	 $tempencfile = TCPDF_STATIC::getObjFilename('enc', $this->file_id);
10893  	 	 	 	 if (!openssl_pkcs7_encrypt($tempkeyfile, $tempencfile, $pubkey['c'], array(), PKCS7_BINARY | PKCS7_DETACHED)) {
10894  	 	 	 	 	 $this->Error('Unable to encrypt the file: '.$tempkeyfile);
10895  	 	 	 	 }
10896  	 	 	 	 // read encryption signature
10897  	 	 	 	 $signature = file_get_contents($tempencfile, false, null, $envelope_length);
10898  	 	 	 	 // extract signature
10899  	 	 	 	 $signature = substr($signature, strpos($signature, 'Content-Disposition'));
10900  	 	 	 	 $tmparr = explode("\n\n", $signature);
10901  	 	 	 	 $signature = trim($tmparr[1]);
10902  	 	 	 	 unset($tmparr);
10903  	 	 	 	 // decode signature
10904  	 	 	 	 $signature = base64_decode($signature);
10905  	 	 	 	 // convert signature to hex
10906  	 	 	 	 $hexsignature = current(unpack('H*', $signature));
10907  	 	 	 	 // store signature on recipients array
10908  	 	 	 	 $this->encryptdata['Recipients'][] = $hexsignature;
10909  	 	 	 	 // The bytes of each item in the Recipients array of PKCS#7 objects in the order in which they appear in the array
10910  	 	 	 	 $recipient_bytes .= $signature;
10911  	 	 	 }
10912  	 	 	 // calculate encryption key
10913  	 	 	 if ($this->encryptdata['mode'] == 3) { // AES-256
10914  	 	 	 	 $this->encryptdata['key'] = substr(hash('sha256', $seed.$recipient_bytes, true), 0, $keybytelen);
10915  	 	 	 } else { // RC4-40, RC4-128, AES-128
10916  	 	 	 	 $this->encryptdata['key'] = substr(sha1($seed.$recipient_bytes, true), 0, $keybytelen);
10917  	 	 	 }
10918  	 	 }
10919  	 }
10920  
10921  	 /**
10922  	  * Set document protection
10923  	  * Remark: the protection against modification is for people who have the full Acrobat product.
10924  	  * 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.
10925  	  * 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.
10926  	  * @param $permissions (Array) the set of permissions (specify the ones you want to block):<ul><li>print : Print the document;</li><li>modify : Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';</li><li>copy : Copy or otherwise extract text and graphics from the document;</li><li>annot-forms : Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);</li><li>fill-forms : Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;</li><li>extract : Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);</li><li>assemble : Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;</li><li>print-high : Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.</li><li>owner : (inverted logic - only for public-key) when set permits change of encryption and enables all other permissions.</li></ul>
10927  	  * @param $user_pass (String) user password. Empty by default.
10928  	  * @param $owner_pass (String) owner password. If not specified, a random value is used.
10929  	  * @param $mode (int) encryption strength: 0 = RC4 40 bit; 1 = RC4 128 bit; 2 = AES 128 bit; 3 = AES 256 bit.
10930  	  * @param $pubkeys (String) array of recipients containing public-key certificates ('c') and permissions ('p'). For example: array(array('c' => 'file://../examples/data/cert/tcpdf.crt', 'p' => array('print')))
10931  	  * @public
10932  	  * @since 2.0.000 (2008-01-02)
10933  	  * @author Nicola Asuni
10934  	  */
10935  	public function SetProtection($permissions=array('print', 'modify', 'copy', 'annot-forms', 'fill-forms', 'extract', 'assemble', 'print-high'), $user_pass='', $owner_pass=null, $mode=0, $pubkeys=null) {
10936  	 	 if ($this->pdfa_mode) {
10937  	 	 	 // encryption is not allowed in PDF/A mode
10938  	 	 	 return;
10939  	 	 }
10940  	 	 $this->encryptdata['protection'] = TCPDF_STATIC::getUserPermissionCode($permissions, $mode);
10941  	 	 if (($pubkeys !== null) AND (is_array($pubkeys))) {
10942  	 	 	 // public-key mode
10943  	 	 	 $this->encryptdata['pubkeys'] = $pubkeys;
10944  	 	 	 if ($mode == 0) {
10945  	 	 	 	 // public-Key Security requires at least 128 bit
10946  	 	 	 	 $mode = 1;
10947  	 	 	 }
10948  	 	 	 if (!function_exists('openssl_pkcs7_encrypt')) {
10949  	 	 	 	 $this->Error('Public-Key Security requires openssl library.');
10950  	 	 	 }
10951  	 	 	 // Set Public-Key filter (available are: Entrust.PPKEF, Adobe.PPKLite, Adobe.PubSec)
10952  	 	 	 $this->encryptdata['pubkey'] = true;
10953  	 	 	 $this->encryptdata['Filter'] = 'Adobe.PubSec';
10954  	 	 	 $this->encryptdata['StmF'] = 'DefaultCryptFilter';
10955  	 	 	 $this->encryptdata['StrF'] = 'DefaultCryptFilter';
10956  	 	 } else {
10957  	 	 	 // standard mode (password mode)
10958  	 	 	 $this->encryptdata['pubkey'] = false;
10959  	 	 	 $this->encryptdata['Filter'] = 'Standard';
10960  	 	 	 $this->encryptdata['StmF'] = 'StdCF';
10961  	 	 	 $this->encryptdata['StrF'] = 'StdCF';
10962  	 	 }
10963  	 	 if ($mode > 1) { // AES
10964  	 	 	 if (!extension_loaded('openssl') && !extension_loaded('mcrypt')) {
10965  	 	 	 	 $this->Error('AES encryption requires openssl or mcrypt extension (http://www.php.net/manual/en/mcrypt.requirements.php).');
10966  	 	 	 }
10967  	 	 	 if (extension_loaded('openssl') && !in_array('aes-256-cbc', openssl_get_cipher_methods())) {
10968  	 	 	 	 $this->Error('AES encryption requires openssl/aes-256-cbc cypher.');
10969  	 	 	 }
10970  	 	 	 if (extension_loaded('mcrypt') && mcrypt_get_cipher_name(MCRYPT_RIJNDAEL_128) === false) {
10971  	 	 	 	 $this->Error('AES encryption requires MCRYPT_RIJNDAEL_128 cypher.');
10972  	 	 	 }
10973  	 	 	 if (($mode == 3) AND !function_exists('hash')) {
10974  	 	 	 	 // the Hash extension requires no external libraries and is enabled by default as of PHP 5.1.2.
10975  	 	 	 	 $this->Error('AES 256 encryption requires HASH Message Digest Framework (http://www.php.net/manual/en/book.hash.php).');
10976  	 	 	 }
10977  	 	 }
10978  	 	 if ($owner_pass === null) {
10979  	 	 	 $owner_pass = md5(TCPDF_STATIC::getRandomSeed());
10980  	 	 }
10981  	 	 $this->encryptdata['user_password'] = $user_pass;
10982  	 	 $this->encryptdata['owner_password'] = $owner_pass;
10983  	 	 $this->encryptdata['mode'] = $mode;
10984  	 	 switch ($mode) {
10985  	 	 	 case 0: { // RC4 40 bit
10986  	 	 	 	 $this->encryptdata['V'] = 1;
10987  	 	 	 	 $this->encryptdata['Length'] = 40;
10988  	 	 	 	 $this->encryptdata['CF']['CFM'] = 'V2';
10989  	 	 	 	 break;
10990  	 	 	 }
10991  	 	 	 case 1: { // RC4 128 bit
10992  	 	 	 	 $this->encryptdata['V'] = 2;
10993  	 	 	 	 $this->encryptdata['Length'] = 128;
10994  	 	 	 	 $this->encryptdata['CF']['CFM'] = 'V2';
10995  	 	 	 	 if ($this->encryptdata['pubkey']) {
10996  	 	 	 	 	 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s4';
10997  	 	 	 	 	 $this->encryptdata['Recipients'] = array();
10998  	 	 	 	 }
10999  	 	 	 	 break;
11000  	 	 	 }
11001  	 	 	 case 2: { // AES 128 bit
11002  	 	 	 	 $this->encryptdata['V'] = 4;
11003  	 	 	 	 $this->encryptdata['Length'] = 128;
11004  	 	 	 	 $this->encryptdata['CF']['CFM'] = 'AESV2';
11005  	 	 	 	 $this->encryptdata['CF']['Length'] = 128;
11006  	 	 	 	 if ($this->encryptdata['pubkey']) {
11007  	 	 	 	 	 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
11008  	 	 	 	 	 $this->encryptdata['Recipients'] = array();
11009  	 	 	 	 }
11010  	 	 	 	 break;
11011  	 	 	 }
11012  	 	 	 case 3: { // AES 256 bit
11013  	 	 	 	 $this->encryptdata['V'] = 5;
11014  	 	 	 	 $this->encryptdata['Length'] = 256;
11015  	 	 	 	 $this->encryptdata['CF']['CFM'] = 'AESV3';
11016  	 	 	 	 $this->encryptdata['CF']['Length'] = 256;
11017  	 	 	 	 if ($this->encryptdata['pubkey']) {
11018  	 	 	 	 	 $this->encryptdata['SubFilter'] = 'adbe.pkcs7.s5';
11019  	 	 	 	 	 $this->encryptdata['Recipients'] = array();
11020  	 	 	 	 }
11021  	 	 	 	 break;
11022  	 	 	 }
11023  	 	 }
11024  	 	 $this->encrypted = true;
11025  	 	 $this->encryptdata['fileid'] = TCPDF_STATIC::convertHexStringToString($this->file_id);
11026  	 	 $this->_generateencryptionkey();
11027  	 }
11028  
11029  	 // END OF ENCRYPTION FUNCTIONS -------------------------
11030  
11031  	 // START TRANSFORMATIONS SECTION -----------------------
11032  
11033  	 /**
11034  	  * Starts a 2D tranformation saving current graphic state.
11035  	  * This function must be called before scaling, mirroring, translation, rotation and skewing.
11036  	  * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11037  	  * @public
11038  	  * @since 2.1.000 (2008-01-07)
11039  	  * @see StartTransform(), StopTransform()
11040  	  */
11041  	public function StartTransform() {
11042  	 	 if ($this->state != 2) {
11043  	 	 	 return;
11044  	 	 }
11045  	 	 $this->_outSaveGraphicsState();
11046  	 	 if ($this->inxobj) {
11047  	 	 	 // we are inside an XObject template
11048  	 	 	 $this->xobjects[$this->xobjid]['transfmrk'][] = strlen($this->xobjects[$this->xobjid]['outdata']);
11049  	 	 } else {
11050  	 	 	 $this->transfmrk[$this->page][] = $this->pagelen[$this->page];
11051  	 	 }
11052  	 	 ++$this->transfmatrix_key;
11053  	 	 $this->transfmatrix[$this->transfmatrix_key] = array();
11054  	 }
11055  
11056  	 /**
11057  	  * Stops a 2D tranformation restoring previous graphic state.
11058  	  * This function must be called after scaling, mirroring, translation, rotation and skewing.
11059  	  * Use StartTransform() before, and StopTransform() after the transformations to restore the normal behavior.
11060  	  * @public
11061  	  * @since 2.1.000 (2008-01-07)
11062  	  * @see StartTransform(), StopTransform()
11063  	  */
11064  	public function StopTransform() {
11065  	 	 if ($this->state != 2) {
11066  	 	 	 return;
11067  	 	 }
11068  	 	 $this->_outRestoreGraphicsState();
11069  	 	 if (isset($this->transfmatrix[$this->transfmatrix_key])) {
11070  	 	 	 array_pop($this->transfmatrix[$this->transfmatrix_key]);
11071  	 	 	 --$this->transfmatrix_key;
11072  	 	 }
11073  	 	 if ($this->inxobj) {
11074  	 	 	 // we are inside an XObject template
11075  	 	 	 array_pop($this->xobjects[$this->xobjid]['transfmrk']);
11076  	 	 } else {
11077  	 	 	 array_pop($this->transfmrk[$this->page]);
11078  	 	 }
11079  	 }
11080  	 /**
11081  	  * Horizontal Scaling.
11082  	  * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11083  	  * @param $x (int) abscissa of the scaling center. Default is current x position
11084  	  * @param $y (int) ordinate of the scaling center. Default is current y position
11085  	  * @public
11086  	  * @since 2.1.000 (2008-01-07)
11087  	  * @see StartTransform(), StopTransform()
11088  	  */
11089  	public function ScaleX($s_x, $x='', $y='') {
11090  	 	 $this->Scale($s_x, 100, $x, $y);
11091  	 }
11092  
11093  	 /**
11094  	  * Vertical Scaling.
11095  	  * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11096  	  * @param $x (int) abscissa of the scaling center. Default is current x position
11097  	  * @param $y (int) ordinate of the scaling center. Default is current y position
11098  	  * @public
11099  	  * @since 2.1.000 (2008-01-07)
11100  	  * @see StartTransform(), StopTransform()
11101  	  */
11102  	public function ScaleY($s_y, $x='', $y='') {
11103  	 	 $this->Scale(100, $s_y, $x, $y);
11104  	 }
11105  
11106  	 /**
11107  	  * Vertical and horizontal proportional Scaling.
11108  	  * @param $s (float) scaling factor for width and height as percent. 0 is not allowed.
11109  	  * @param $x (int) abscissa of the scaling center. Default is current x position
11110  	  * @param $y (int) ordinate of the scaling center. Default is current y position
11111  	  * @public
11112  	  * @since 2.1.000 (2008-01-07)
11113  	  * @see StartTransform(), StopTransform()
11114  	  */
11115  	public function ScaleXY($s, $x='', $y='') {
11116  	 	 $this->Scale($s, $s, $x, $y);
11117  	 }
11118  
11119  	 /**
11120  	  * Vertical and horizontal non-proportional Scaling.
11121  	  * @param $s_x (float) scaling factor for width as percent. 0 is not allowed.
11122  	  * @param $s_y (float) scaling factor for height as percent. 0 is not allowed.
11123  	  * @param $x (int) abscissa of the scaling center. Default is current x position
11124  	  * @param $y (int) ordinate of the scaling center. Default is current y position
11125  	  * @public
11126  	  * @since 2.1.000 (2008-01-07)
11127  	  * @see StartTransform(), StopTransform()
11128  	  */
11129  	public function Scale($s_x, $s_y, $x='', $y='') {
11130  	 	 if ($x === '') {
11131  	 	 	 $x = $this->x;
11132  	 	 }
11133  	 	 if ($y === '') {
11134  	 	 	 $y = $this->y;
11135  	 	 }
11136  	 	 if (($s_x == 0) OR ($s_y == 0)) {
11137  	 	 	 $this->Error('Please do not use values equal to zero for scaling');
11138  	 	 }
11139  	 	 $y = ($this->h - $y) * $this->k;
11140  	 	 $x *= $this->k;
11141  	 	 //calculate elements of transformation matrix
11142  	 	 $s_x /= 100;
11143  	 	 $s_y /= 100;
11144  	 	 $tm = array();
11145  	 	 $tm[0] = $s_x;
11146  	 	 $tm[1] = 0;
11147  	 	 $tm[2] = 0;
11148  	 	 $tm[3] = $s_y;
11149  	 	 $tm[4] = $x * (1 - $s_x);
11150  	 	 $tm[5] = $y * (1 - $s_y);
11151  	 	 //scale the coordinate system
11152  	 	 $this->Transform($tm);
11153  	 }
11154  
11155  	 /**
11156  	  * Horizontal Mirroring.
11157  	  * @param $x (int) abscissa of the point. Default is current x position
11158  	  * @public
11159  	  * @since 2.1.000 (2008-01-07)
11160  	  * @see StartTransform(), StopTransform()
11161  	  */
11162  	public function MirrorH($x='') {
11163  	 	 $this->Scale(-100, 100, $x);
11164  	 }
11165  
11166  	 /**
11167  	  * Verical Mirroring.
11168  	  * @param $y (int) ordinate of the point. Default is current y position
11169  	  * @public
11170  	  * @since 2.1.000 (2008-01-07)
11171  	  * @see StartTransform(), StopTransform()
11172  	  */
11173  	public function MirrorV($y='') {
11174  	 	 $this->Scale(100, -100, '', $y);
11175  	 }
11176  
11177  	 /**
11178  	  * Point reflection mirroring.
11179  	  * @param $x (int) abscissa of the point. Default is current x position
11180  	  * @param $y (int) ordinate of the point. Default is current y position
11181  	  * @public
11182  	  * @since 2.1.000 (2008-01-07)
11183  	  * @see StartTransform(), StopTransform()
11184  	  */
11185  	public function MirrorP($x='',$y='') {
11186  	 	 $this->Scale(-100, -100, $x, $y);
11187  	 }
11188  
11189  	 /**
11190  	  * Reflection against a straight line through point (x, y) with the gradient angle (angle).
11191  	  * @param $angle (float) gradient angle of the straight line. Default is 0 (horizontal line).
11192  	  * @param $x (int) abscissa of the point. Default is current x position
11193  	  * @param $y (int) ordinate of the point. Default is current y position
11194  	  * @public
11195  	  * @since 2.1.000 (2008-01-07)
11196  	  * @see StartTransform(), StopTransform()
11197  	  */
11198  	public function MirrorL($angle=0, $x='',$y='') {
11199  	 	 $this->Scale(-100, 100, $x, $y);
11200  	 	 $this->Rotate(-2*($angle-90), $x, $y);
11201  	 }
11202  
11203  	 /**
11204  	  * Translate graphic object horizontally.
11205  	  * @param $t_x (int) movement to the right (or left for RTL)
11206  	  * @public
11207  	  * @since 2.1.000 (2008-01-07)
11208  	  * @see StartTransform(), StopTransform()
11209  	  */
11210  	public function TranslateX($t_x) {
11211  	 	 $this->Translate($t_x, 0);
11212  	 }
11213  
11214  	 /**
11215  	  * Translate graphic object vertically.
11216  	  * @param $t_y (int) movement to the bottom
11217  	  * @public
11218  	  * @since 2.1.000 (2008-01-07)
11219  	  * @see StartTransform(), StopTransform()
11220  	  */
11221  	public function TranslateY($t_y) {
11222  	 	 $this->Translate(0, $t_y);
11223  	 }
11224  
11225  	 /**
11226  	  * Translate graphic object horizontally and vertically.
11227  	  * @param $t_x (int) movement to the right
11228  	  * @param $t_y (int) movement to the bottom
11229  	  * @public
11230  	  * @since 2.1.000 (2008-01-07)
11231  	  * @see StartTransform(), StopTransform()
11232  	  */
11233  	public function Translate($t_x, $t_y) {
11234  	 	 //calculate elements of transformation matrix
11235  	 	 $tm = array();
11236  	 	 $tm[0] = 1;
11237  	 	 $tm[1] = 0;
11238  	 	 $tm[2] = 0;
11239  	 	 $tm[3] = 1;
11240  	 	 $tm[4] = $t_x * $this->k;
11241  	 	 $tm[5] = -$t_y * $this->k;
11242  	 	 //translate the coordinate system
11243  	 	 $this->Transform($tm);
11244  	 }
11245  
11246  	 /**
11247  	  * Rotate object.
11248  	  * @param $angle (float) angle in degrees for counter-clockwise rotation
11249  	  * @param $x (int) abscissa of the rotation center. Default is current x position
11250  	  * @param $y (int) ordinate of the rotation center. Default is current y position
11251  	  * @public
11252  	  * @since 2.1.000 (2008-01-07)
11253  	  * @see StartTransform(), StopTransform()
11254  	  */
11255  	public function Rotate($angle, $x='', $y='') {
11256  	 	 if ($x === '') {
11257  	 	 	 $x = $this->x;
11258  	 	 }
11259  	 	 if ($y === '') {
11260  	 	 	 $y = $this->y;
11261  	 	 }
11262  	 	 $y = ($this->h - $y) * $this->k;
11263  	 	 $x *= $this->k;
11264  	 	 //calculate elements of transformation matrix
11265  	 	 $tm = array();
11266  	 	 $tm[0] = cos(deg2rad($angle));
11267  	 	 $tm[1] = sin(deg2rad($angle));
11268  	 	 $tm[2] = -$tm[1];
11269  	 	 $tm[3] = $tm[0];
11270  	 	 $tm[4] = $x + ($tm[1] * $y) - ($tm[0] * $x);
11271  	 	 $tm[5] = $y - ($tm[0] * $y) - ($tm[1] * $x);
11272  	 	 //rotate the coordinate system around ($x,$y)
11273  	 	 $this->Transform($tm);
11274  	 }
11275  
11276  	 /**
11277  	  * Skew horizontally.
11278  	  * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11279  	  * @param $x (int) abscissa of the skewing center. default is current x position
11280  	  * @param $y (int) ordinate of the skewing center. default is current y position
11281  	  * @public
11282  	  * @since 2.1.000 (2008-01-07)
11283  	  * @see StartTransform(), StopTransform()
11284  	  */
11285  	public function SkewX($angle_x, $x='', $y='') {
11286  	 	 $this->Skew($angle_x, 0, $x, $y);
11287  	 }
11288  
11289  	 /**
11290  	  * Skew vertically.
11291  	  * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11292  	  * @param $x (int) abscissa of the skewing center. default is current x position
11293  	  * @param $y (int) ordinate of the skewing center. default is current y position
11294  	  * @public
11295  	  * @since 2.1.000 (2008-01-07)
11296  	  * @see StartTransform(), StopTransform()
11297  	  */
11298  	public function SkewY($angle_y, $x='', $y='') {
11299  	 	 $this->Skew(0, $angle_y, $x, $y);
11300  	 }
11301  
11302  	 /**
11303  	  * Skew.
11304  	  * @param $angle_x (float) angle in degrees between -90 (skew to the left) and 90 (skew to the right)
11305  	  * @param $angle_y (float) angle in degrees between -90 (skew to the bottom) and 90 (skew to the top)
11306  	  * @param $x (int) abscissa of the skewing center. default is current x position
11307  	  * @param $y (int) ordinate of the skewing center. default is current y position
11308  	  * @public
11309  	  * @since 2.1.000 (2008-01-07)
11310  	  * @see StartTransform(), StopTransform()
11311  	  */
11312  	public function Skew($angle_x, $angle_y, $x='', $y='') {
11313  	 	 if ($x === '') {
11314  	 	 	 $x = $this->x;
11315  	 	 }
11316  	 	 if ($y === '') {
11317  	 	 	 $y = $this->y;
11318  	 	 }
11319  	 	 if (($angle_x <= -90) OR ($angle_x >= 90) OR ($angle_y <= -90) OR ($angle_y >= 90)) {
11320  	 	 	 $this->Error('Please use values between -90 and +90 degrees for Skewing.');
11321  	 	 }
11322  	 	 $x *= $this->k;
11323  	 	 $y = ($this->h - $y) * $this->k;
11324  	 	 //calculate elements of transformation matrix
11325  	 	 $tm = array();
11326  	 	 $tm[0] = 1;
11327  	 	 $tm[1] = tan(deg2rad($angle_y));
11328  	 	 $tm[2] = tan(deg2rad($angle_x));
11329  	 	 $tm[3] = 1;
11330  	 	 $tm[4] = -$tm[2] * $y;
11331  	 	 $tm[5] = -$tm[1] * $x;
11332  	 	 //skew the coordinate system
11333  	 	 $this->Transform($tm);
11334  	 }
11335  
11336  	 /**
11337  	  * Apply graphic transformations.
11338  	  * @param $tm (array) transformation matrix
11339  	  * @protected
11340  	  * @since 2.1.000 (2008-01-07)
11341  	  * @see StartTransform(), StopTransform()
11342  	  */
11343  	protected function Transform($tm) {
11344  	 	 if ($this->state != 2) {
11345  	 	 	 return;
11346  	 	 }
11347  	 	 $this->_out(sprintf('%F %F %F %F %F %F cm', $tm[0], $tm[1], $tm[2], $tm[3], $tm[4], $tm[5]));
11348  	 	 // add tranformation matrix
11349  	 	 $this->transfmatrix[$this->transfmatrix_key][] = array('a' => $tm[0], 'b' => $tm[1], 'c' => $tm[2], 'd' => $tm[3], 'e' => $tm[4], 'f' => $tm[5]);
11350  	 	 // update transformation mark
11351  	 	 if ($this->inxobj) {
11352  	 	 	 // we are inside an XObject template
11353  	 	 	 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
11354  	 	 	 	 $key = key($this->xobjects[$this->xobjid]['transfmrk']);
11355  	 	 	 	 $this->xobjects[$this->xobjid]['transfmrk'][$key] = strlen($this->xobjects[$this->xobjid]['outdata']);
11356  	 	 	 }
11357  	 	 } elseif (end($this->transfmrk[$this->page]) !== false) {
11358  	 	 	 $key = key($this->transfmrk[$this->page]);
11359  	 	 	 $this->transfmrk[$this->page][$key] = $this->pagelen[$this->page];
11360  	 	 }
11361  	 }
11362  
11363  	 // END TRANSFORMATIONS SECTION -------------------------
11364  
11365  	 // START GRAPHIC FUNCTIONS SECTION ---------------------
11366  	 // The following section is based on the code provided by David Hernandez Sanz
11367  
11368  	 /**
11369  	  * 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.
11370  	  * @param $width (float) The width.
11371  	  * @public
11372  	  * @since 1.0
11373  	  * @see Line(), Rect(), Cell(), MultiCell()
11374  	  */
11375  	public function SetLineWidth($width) {
11376  	 	 //Set line width
11377  	 	 $this->LineWidth = $width;
11378  	 	 $this->linestyleWidth = sprintf('%F w', ($width * $this->k));
11379  	 	 if ($this->state == 2) {
11380  	 	 	 $this->_out($this->linestyleWidth);
11381  	 	 }
11382  	 }
11383  
11384  	 /**
11385  	  * Returns the current the line width.
11386  	  * @return int Line width
11387  	  * @public
11388  	  * @since 2.1.000 (2008-01-07)
11389  	  * @see Line(), SetLineWidth()
11390  	  */
11391  	public function GetLineWidth() {
11392  	 	 return $this->LineWidth;
11393  	 }
11394  
11395  	 /**
11396  	  * Set line style.
11397  	  * @param $style (array) Line style. Array with keys among the following:
11398  	  * <ul>
11399  	  *	  <li>width (float): Width of the line in user units.</li>
11400  	  *	  <li>cap (string): Type of cap to put on the line. Possible values are:
11401  	  * butt, round, square. The difference between "square" and "butt" is that
11402  	  * "square" projects a flat end past the end of the line.</li>
11403  	  *	  <li>join (string): Type of join. Possible values are: miter, round,
11404  	  * bevel.</li>
11405  	  *	  <li>dash (mixed): Dash pattern. Is 0 (without dash) or string with
11406  	  * series of length values, which are the lengths of the on and off dashes.
11407  	  * For example: "2" represents 2 on, 2 off, 2 on, 2 off, ...; "2,1" is 2 on,
11408  	  * 1 off, 2 on, 1 off, ...</li>
11409  	  *	  <li>phase (integer): Modifier on the dash pattern which is used to shift
11410  	  * the point at which the pattern starts.</li>
11411  	  *	  <li>color (array): Draw color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName).</li>
11412  	  * </ul>
11413  	  * @param $ret (boolean) if true do not send the command.
11414  	  * @return string the PDF command
11415  	  * @public
11416  	  * @since 2.1.000 (2008-01-08)
11417  	  */
11418  	public function SetLineStyle($style, $ret=false) {
11419  	 	 $s = ''; // string to be returned
11420  	 	 if (!is_array($style)) {
11421  	 	 	 return;
11422  	 	 }
11423  	 	 if (isset($style['width'])) {
11424  	 	 	 $this->LineWidth = $style['width'];
11425  	 	 	 $this->linestyleWidth = sprintf('%F w', ($style['width'] * $this->k));
11426  	 	 	 $s .= $this->linestyleWidth.' ';
11427  	 	 }
11428  	 	 if (isset($style['cap'])) {
11429  	 	 	 $ca = array('butt' => 0, 'round'=> 1, 'square' => 2);
11430  	 	 	 if (isset($ca[$style['cap']])) {
11431  	 	 	 	 $this->linestyleCap = $ca[$style['cap']].' J';
11432  	 	 	 	 $s .= $this->linestyleCap.' ';
11433  	 	 	 }
11434  	 	 }
11435  	 	 if (isset($style['join'])) {
11436  	 	 	 $ja = array('miter' => 0, 'round' => 1, 'bevel' => 2);
11437  	 	 	 if (isset($ja[$style['join']])) {
11438  	 	 	 	 $this->linestyleJoin = $ja[$style['join']].' j';
11439  	 	 	 	 $s .= $this->linestyleJoin.' ';
11440  	 	 	 }
11441  	 	 }
11442  	 	 if (isset($style['dash'])) {
11443  	 	 	 $dash_string = '';
11444  	 	 	 if ($style['dash']) {
11445  	 	 	 	 if (preg_match('/^.+,/', $style['dash']) > 0) {
11446  	 	 	 	 	 $tab = explode(',', $style['dash']);
11447  	 	 	 	 } else {
11448  	 	 	 	 	 $tab = array($style['dash']);
11449  	 	 	 	 }
11450  	 	 	 	 $dash_string = '';
11451  	 	 	 	 foreach ($tab as $i => $v) {
11452  	 	 	 	 	 if ($i) {
11453  	 	 	 	 	 	 $dash_string .= ' ';
11454  	 	 	 	 	 }
11455  	 	 	 	 	 $dash_string .= sprintf('%F', $v);
11456  	 	 	 	 }
11457  	 	 	 }
11458  	 	 	 if (!isset($style['phase']) OR !$style['dash']) {
11459  	 	 	 	 $style['phase'] = 0;
11460  	 	 	 }
11461  	 	 	 $this->linestyleDash = sprintf('[%s] %F d', $dash_string, $style['phase']);
11462  	 	 	 $s .= $this->linestyleDash.' ';
11463  	 	 }
11464  	 	 if (isset($style['color'])) {
11465  	 	 	 $s .= $this->SetDrawColorArray($style['color'], true).' ';
11466  	 	 }
11467  	 	 if (!$ret AND ($this->state == 2)) {
11468  	 	 	 $this->_out($s);
11469  	 	 }
11470  	 	 return $s;
11471  	 }
11472  
11473  	 /**
11474  	  * Begin a new subpath by moving the current point to coordinates (x, y), omitting any connecting line segment.
11475  	  * @param $x (float) Abscissa of point.
11476  	  * @param $y (float) Ordinate of point.
11477  	  * @protected
11478  	  * @since 2.1.000 (2008-01-08)
11479  	  */
11480  	protected function _outPoint($x, $y) {
11481  	 	 if ($this->state == 2) {
11482  	 	 	 $this->_out(sprintf('%F %F m', ($x * $this->k), (($this->h - $y) * $this->k)));
11483  	 	 }
11484  	 }
11485  
11486  	 /**
11487  	  * Append a straight line segment from the current point to the point (x, y).
11488  	  * The new current point shall be (x, y).
11489  	  * @param $x (float) Abscissa of end point.
11490  	  * @param $y (float) Ordinate of end point.
11491  	  * @protected
11492  	  * @since 2.1.000 (2008-01-08)
11493  	  */
11494  	protected function _outLine($x, $y) {
11495  	 	 if ($this->state == 2) {
11496  	 	 	 $this->_out(sprintf('%F %F l', ($x * $this->k), (($this->h - $y) * $this->k)));
11497  	 	 }
11498  	 }
11499  
11500  	 /**
11501  	  * Append a rectangle to the current path as a complete subpath, with lower-left corner (x, y) and dimensions widthand height in user space.
11502  	  * @param $x (float) Abscissa of upper-left corner.
11503  	  * @param $y (float) Ordinate of upper-left corner.
11504  	  * @param $w (float) Width.
11505  	  * @param $h (float) Height.
11506  	  * @param $op (string) options
11507  	  * @protected
11508  	  * @since 2.1.000 (2008-01-08)
11509  	  */
11510  	protected function _outRect($x, $y, $w, $h, $op) {
11511  	 	 if ($this->state == 2) {
11512  	 	 	 $this->_out(sprintf('%F %F %F %F re %s', ($x * $this->k), (($this->h - $y) * $this->k), ($w * $this->k), (-$h * $this->k), $op));
11513  	 	 }
11514  	 }
11515  
11516  	 /**
11517  	  * Append a cubic Bezier 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 Bezier control points.
11518  	  * The new current point shall be (x3, y3).
11519  	  * @param $x1 (float) Abscissa of control point 1.
11520  	  * @param $y1 (float) Ordinate of control point 1.
11521  	  * @param $x2 (float) Abscissa of control point 2.
11522  	  * @param $y2 (float) Ordinate of control point 2.
11523  	  * @param $x3 (float) Abscissa of end point.
11524  	  * @param $y3 (float) Ordinate of end point.
11525  	  * @protected
11526  	  * @since 2.1.000 (2008-01-08)
11527  	  */
11528  	protected function _outCurve($x1, $y1, $x2, $y2, $x3, $y3) {
11529  	 	 if ($this->state == 2) {
11530  	 	 	 $this->_out(sprintf('%F %F %F %F %F %F 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)));
11531  	 	 }
11532  	 }
11533  
11534  	 /**
11535  	  * Append a cubic Bezier 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 Bezier control points.
11536  	  * The new current point shall be (x3, y3).
11537  	  * @param $x2 (float) Abscissa of control point 2.
11538  	  * @param $y2 (float) Ordinate of control point 2.
11539  	  * @param $x3 (float) Abscissa of end point.
11540  	  * @param $y3 (float) Ordinate of end point.
11541  	  * @protected
11542  	  * @since 4.9.019 (2010-04-26)
11543  	  */
11544  	protected function _outCurveV($x2, $y2, $x3, $y3) {
11545  	 	 if ($this->state == 2) {
11546  	 	 	 $this->_out(sprintf('%F %F %F %F v', ($x2 * $this->k), (($this->h - $y2) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11547  	 	 }
11548  	 }
11549  
11550  	 /**
11551  	  * Append a cubic Bezier 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 Bezier control points.
11552  	  * The new current point shall be (x3, y3).
11553  	  * @param $x1 (float) Abscissa of control point 1.
11554  	  * @param $y1 (float) Ordinate of control point 1.
11555  	  * @param $x3 (float) Abscissa of end point.
11556  	  * @param $y3 (float) Ordinate of end point.
11557  	  * @protected
11558  	  * @since 2.1.000 (2008-01-08)
11559  	  */
11560  	protected function _outCurveY($x1, $y1, $x3, $y3) {
11561  	 	 if ($this->state == 2) {
11562  	 	 	 $this->_out(sprintf('%F %F %F %F y', ($x1 * $this->k), (($this->h - $y1) * $this->k), ($x3 * $this->k), (($this->h - $y3) * $this->k)));
11563  	 	 }
11564  	 }
11565  
11566  	 /**
11567  	  * Draws a line between two points.
11568  	  * @param $x1 (float) Abscissa of first point.
11569  	  * @param $y1 (float) Ordinate of first point.
11570  	  * @param $x2 (float) Abscissa of second point.
11571  	  * @param $y2 (float) Ordinate of second point.
11572  	  * @param $style (array) Line style. Array like for SetLineStyle(). Default value: default line style (empty array).
11573  	  * @public
11574  	  * @since 1.0
11575  	  * @see SetLineWidth(), SetDrawColor(), SetLineStyle()
11576  	  */
11577  	public function Line($x1, $y1, $x2, $y2, $style=array()) {
11578  	 	 if ($this->state != 2) {
11579  	 	 	 return;
11580  	 	 }
11581  	 	 if (is_array($style)) {
11582  	 	 	 $this->SetLineStyle($style);
11583  	 	 }
11584  	 	 $this->_outPoint($x1, $y1);
11585  	 	 $this->_outLine($x2, $y2);
11586  	 	 $this->_out('S');
11587  	 }
11588  
11589  	 /**
11590  	  * Draws a rectangle.
11591  	  * @param $x (float) Abscissa of upper-left corner.
11592  	  * @param $y (float) Ordinate of upper-left corner.
11593  	  * @param $w (float) Width.
11594  	  * @param $h (float) Height.
11595  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11596  	  * @param $border_style (array) Border style of rectangle. Array with keys among the following:
11597  	  * <ul>
11598  	  *	  <li>all: Line style of all borders. Array like for SetLineStyle().</li>
11599  	  *	  <li>L, T, R, B or combinations: Line style of left, top, right or bottom border. Array like for SetLineStyle().</li>
11600  	  * </ul>
11601  	  * If a key is not present or is null, the correspondent border is not drawn. Default value: default line style (empty array).
11602  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11603  	  * @public
11604  	  * @since 1.0
11605  	  * @see SetLineStyle()
11606  	  */
11607  	public function Rect($x, $y, $w, $h, $style='', $border_style=array(), $fill_color=array()) {
11608  	 	 if ($this->state != 2) {
11609  	 	 	 return;
11610  	 	 }
11611  	 	 if (empty($style)) {
11612  	 	 	 $style = 'S';
11613  	 	 }
11614  	 	 if (!(strpos($style, 'F') === false) AND !empty($fill_color)) {
11615  	 	 	 // set background color
11616  	 	 	 $this->SetFillColorArray($fill_color);
11617  	 	 }
11618  	 	 if (!empty($border_style)) {
11619  	 	 	 if (isset($border_style['all']) AND !empty($border_style['all'])) {
11620  	 	 	 	 //set global style for border
11621  	 	 	 	 $this->SetLineStyle($border_style['all']);
11622  	 	 	 	 $border_style = array();
11623  	 	 	 } else {
11624  	 	 	 	 // remove stroke operator from style
11625  	 	 	 	 $opnostroke = array('S' => '', 'D' => '', 's' => '', 'd' => '', 'B' => 'F', 'FD' => 'F', 'DF' => 'F', 'B*' => 'F*', 'F*D' => 'F*', 'DF*' => 'F*', 'b' => 'f', 'fd' => 'f', 'df' => 'f', 'b*' => 'f*', 'f*d' => 'f*', 'df*' => 'f*' );
11626  	 	 	 	 if (isset($opnostroke[$style])) {
11627  	 	 	 	 	 $style = $opnostroke[$style];
11628  	 	 	 	 }
11629  	 	 	 }
11630  	 	 }
11631  	 	 if (!empty($style)) {
11632  	 	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
11633  	 	 	 $this->_outRect($x, $y, $w, $h, $op);
11634  	 	 }
11635  	 	 if (!empty($border_style)) {
11636  	 	 	 $border_style2 = array();
11637  	 	 	 foreach ($border_style as $line => $value) {
11638  	 	 	 	 $length = strlen($line);
11639  	 	 	 	 for ($i = 0; $i < $length; ++$i) {
11640  	 	 	 	 	 $border_style2[$line[$i]] = $value;
11641  	 	 	 	 }
11642  	 	 	 }
11643  	 	 	 $border_style = $border_style2;
11644  	 	 	 if (isset($border_style['L']) AND $border_style['L']) {
11645  	 	 	 	 $this->Line($x, $y, $x, $y + $h, $border_style['L']);
11646  	 	 	 }
11647  	 	 	 if (isset($border_style['T']) AND $border_style['T']) {
11648  	 	 	 	 $this->Line($x, $y, $x + $w, $y, $border_style['T']);
11649  	 	 	 }
11650  	 	 	 if (isset($border_style['R']) AND $border_style['R']) {
11651  	 	 	 	 $this->Line($x + $w, $y, $x + $w, $y + $h, $border_style['R']);
11652  	 	 	 }
11653  	 	 	 if (isset($border_style['B']) AND $border_style['B']) {
11654  	 	 	 	 $this->Line($x, $y + $h, $x + $w, $y + $h, $border_style['B']);
11655  	 	 	 }
11656  	 	 }
11657  	 }
11658  
11659  	 /**
11660  	  * Draws a Bezier curve.
11661  	  * The Bezier curve is a tangent to the line between the control points at
11662  	  * either end of the curve.
11663  	  * @param $x0 (float) Abscissa of start point.
11664  	  * @param $y0 (float) Ordinate of start point.
11665  	  * @param $x1 (float) Abscissa of control point 1.
11666  	  * @param $y1 (float) Ordinate of control point 1.
11667  	  * @param $x2 (float) Abscissa of control point 2.
11668  	  * @param $y2 (float) Ordinate of control point 2.
11669  	  * @param $x3 (float) Abscissa of end point.
11670  	  * @param $y3 (float) Ordinate of end point.
11671  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11672  	  * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11673  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11674  	  * @public
11675  	  * @see SetLineStyle()
11676  	  * @since 2.1.000 (2008-01-08)
11677  	  */
11678  	public function Curve($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3, $style='', $line_style=array(), $fill_color=array()) {
11679  	 	 if ($this->state != 2) {
11680  	 	 	 return;
11681  	 	 }
11682  	 	 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11683  	 	 	 $this->SetFillColorArray($fill_color);
11684  	 	 }
11685  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
11686  	 	 if ($line_style) {
11687  	 	 	 $this->SetLineStyle($line_style);
11688  	 	 }
11689  	 	 $this->_outPoint($x0, $y0);
11690  	 	 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11691  	 	 $this->_out($op);
11692  	 }
11693  
11694  	 /**
11695  	  * Draws a poly-Bezier curve.
11696  	  * Each Bezier curve segment is a tangent to the line between the control points at
11697  	  * either end of the curve.
11698  	  * @param $x0 (float) Abscissa of start point.
11699  	  * @param $y0 (float) Ordinate of start point.
11700  	  * @param $segments (float) An array of bezier descriptions. Format: array(x1, y1, x2, y2, x3, y3).
11701  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11702  	  * @param $line_style (array) Line style of curve. Array like for SetLineStyle(). Default value: default line style (empty array).
11703  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11704  	  * @public
11705  	  * @see SetLineStyle()
11706  	  * @since 3.0008 (2008-05-12)
11707  	  */
11708  	public function Polycurve($x0, $y0, $segments, $style='', $line_style=array(), $fill_color=array()) {
11709  	 	 if ($this->state != 2) {
11710  	 	 	 return;
11711  	 	 }
11712  	 	 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11713  	 	 	 $this->SetFillColorArray($fill_color);
11714  	 	 }
11715  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
11716  	 	 if ($op == 'f') {
11717  	 	 	 $line_style = array();
11718  	 	 }
11719  	 	 if ($line_style) {
11720  	 	 	 $this->SetLineStyle($line_style);
11721  	 	 }
11722  	 	 $this->_outPoint($x0, $y0);
11723  	 	 foreach ($segments as $segment) {
11724  	 	 	 list($x1, $y1, $x2, $y2, $x3, $y3) = $segment;
11725  	 	 	 $this->_outCurve($x1, $y1, $x2, $y2, $x3, $y3);
11726  	 	 }
11727  	 	 $this->_out($op);
11728  	 }
11729  
11730  	 /**
11731  	  * Draws an ellipse.
11732  	  * An ellipse is formed from n Bezier curves.
11733  	  * @param $x0 (float) Abscissa of center point.
11734  	  * @param $y0 (float) Ordinate of center point.
11735  	  * @param $rx (float) Horizontal radius.
11736  	  * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11737  	  * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
11738  	  * @param $astart: (float) Angle start of draw line. Default value: 0.
11739  	  * @param $afinish: (float) Angle finish of draw line. Default value: 360.
11740  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11741  	  * @param $line_style (array) Line style of ellipse. Array like for SetLineStyle(). Default value: default line style (empty array).
11742  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11743  	  * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11744  	  * @author Nicola Asuni
11745  	  * @public
11746  	  * @since 2.1.000 (2008-01-08)
11747  	  */
11748  	public function Ellipse($x0, $y0, $rx, $ry='', $angle=0, $astart=0, $afinish=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11749  	 	 if ($this->state != 2) {
11750  	 	 	 return;
11751  	 	 }
11752  	 	 if (TCPDF_STATIC::empty_string($ry) OR ($ry == 0)) {
11753  	 	 	 $ry = $rx;
11754  	 	 }
11755  	 	 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11756  	 	 	 $this->SetFillColorArray($fill_color);
11757  	 	 }
11758  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
11759  	 	 if ($op == 'f') {
11760  	 	 	 $line_style = array();
11761  	 	 }
11762  	 	 if ($line_style) {
11763  	 	 	 $this->SetLineStyle($line_style);
11764  	 	 }
11765  	 	 $this->_outellipticalarc($x0, $y0, $rx, $ry, $angle, $astart, $afinish, false, $nc, true, true, false);
11766  	 	 $this->_out($op);
11767  	 }
11768  
11769  	 /**
11770  	  * Append an elliptical arc to the current path.
11771  	  * An ellipse is formed from n Bezier curves.
11772  	  * @param $xc (float) Abscissa of center point.
11773  	  * @param $yc (float) Ordinate of center point.
11774  	  * @param $rx (float) Horizontal radius.
11775  	  * @param $ry (float) Vertical radius (if ry = 0 then is a circle, see Circle()). Default value: 0.
11776  	  * @param $xang: (float) Angle between the X-axis and the major axis of the ellipse. Default value: 0.
11777  	  * @param $angs: (float) Angle start of draw line. Default value: 0.
11778  	  * @param $angf: (float) Angle finish of draw line. Default value: 360.
11779  	  * @param $pie (boolean) if true do not mark the border point (used to draw pie sectors).
11780  	  * @param $nc (integer) Number of curves used to draw a 90 degrees portion of ellipse.
11781  	  * @param $startpoint (boolean) if true output a starting point.
11782  	  * @param $ccw (boolean) if true draws in counter-clockwise.
11783  	  * @param $svg (boolean) if true the angles are in svg mode (already calculated).
11784  	  * @return array bounding box coordinates (x min, y min, x max, y max)
11785  	  * @author Nicola Asuni
11786  	  * @protected
11787  	  * @since 4.9.019 (2010-04-26)
11788  	  */
11789  	protected function _outellipticalarc($xc, $yc, $rx, $ry, $xang=0, $angs=0, $angf=360, $pie=false, $nc=2, $startpoint=true, $ccw=true, $svg=false) {
11790  	 	 if (($rx <= 0) OR ($ry < 0)) {
11791  	 	 	 return;
11792  	 	 }
11793  	 	 $k = $this->k;
11794  	 	 if ($nc < 2) {
11795  	 	 	 $nc = 2;
11796  	 	 }
11797  	 	 $xmin = 2147483647;
11798  	 	 $ymin = 2147483647;
11799  	 	 $xmax = 0;
11800  	 	 $ymax = 0;
11801  	 	 if ($pie) {
11802  	 	 	 // center of the arc
11803  	 	 	 $this->_outPoint($xc, $yc);
11804  	 	 }
11805  	 	 $xang = deg2rad((float) $xang);
11806  	 	 $angs = deg2rad((float) $angs);
11807  	 	 $angf = deg2rad((float) $angf);
11808  	 	 if ($svg) {
11809  	 	 	 $as = $angs;
11810  	 	 	 $af = $angf;
11811  	 	 } else {
11812  	 	 	 $as = atan2((sin($angs) / $ry), (cos($angs) / $rx));
11813  	 	 	 $af = atan2((sin($angf) / $ry), (cos($angf) / $rx));
11814  	 	 }
11815  	 	 if ($as < 0) {
11816  	 	 	 $as += (2 * M_PI);
11817  	 	 }
11818  	 	 if ($af < 0) {
11819  	 	 	 $af += (2 * M_PI);
11820  	 	 }
11821  	 	 if ($ccw AND ($as > $af)) {
11822  	 	 	 // reverse rotation
11823  	 	 	 $as -= (2 * M_PI);
11824  	 	 } elseif (!$ccw AND ($as < $af)) {
11825  	 	 	 // reverse rotation
11826  	 	 	 $af -= (2 * M_PI);
11827  	 	 }
11828  	 	 $total_angle = ($af - $as);
11829  	 	 if ($nc < 2) {
11830  	 	 	 $nc = 2;
11831  	 	 }
11832  	 	 // total arcs to draw
11833  	 	 $nc *= (2 * abs($total_angle) / M_PI);
11834  	 	 $nc = round($nc) + 1;
11835  	 	 // angle of each arc
11836  	 	 $arcang = ($total_angle / $nc);
11837  	 	 // center point in PDF coordinates
11838  	 	 $x0 = $xc;
11839  	 	 $y0 = ($this->h - $yc);
11840  	 	 // starting angle
11841  	 	 $ang = $as;
11842  	 	 $alpha = sin($arcang) * ((sqrt(4 + (3 * pow(tan(($arcang) / 2), 2))) - 1) / 3);
11843  	 	 $cos_xang = cos($xang);
11844  	 	 $sin_xang = sin($xang);
11845  	 	 $cos_ang = cos($ang);
11846  	 	 $sin_ang = sin($ang);
11847  	 	 // first arc point
11848  	 	 $px1 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11849  	 	 $py1 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11850  	 	 // first Bezier control point
11851  	 	 $qx1 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11852  	 	 $qy1 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11853  	 	 if ($pie) {
11854  	 	 	 // line from center to arc starting point
11855  	 	 	 $this->_outLine($px1, $this->h - $py1);
11856  	 	 } elseif ($startpoint) {
11857  	 	 	 // arc starting point
11858  	 	 	 $this->_outPoint($px1, $this->h - $py1);
11859  	 	 }
11860  	 	 // draw arcs
11861  	 	 for ($i = 1; $i <= $nc; ++$i) {
11862  	 	 	 // starting angle
11863  	 	 	 $ang = $as + ($i * $arcang);
11864  	 	 	 if ($i == $nc) {
11865  	 	 	 	 $ang = $af;
11866  	 	 	 }
11867  	 	 	 $cos_ang = cos($ang);
11868  	 	 	 $sin_ang = sin($ang);
11869  	 	 	 // second arc point
11870  	 	 	 $px2 = $x0 + ($rx * $cos_xang * $cos_ang) - ($ry * $sin_xang * $sin_ang);
11871  	 	 	 $py2 = $y0 + ($rx * $sin_xang * $cos_ang) + ($ry * $cos_xang * $sin_ang);
11872  	 	 	 // second Bezier control point
11873  	 	 	 $qx2 = ($alpha * ((-$rx * $cos_xang * $sin_ang) - ($ry * $sin_xang * $cos_ang)));
11874  	 	 	 $qy2 = ($alpha * ((-$rx * $sin_xang * $sin_ang) + ($ry * $cos_xang * $cos_ang)));
11875  	 	 	 // draw arc
11876  	 	 	 $cx1 = ($px1 + $qx1);
11877  	 	 	 $cy1 = ($this->h - ($py1 + $qy1));
11878  	 	 	 $cx2 = ($px2 - $qx2);
11879  	 	 	 $cy2 = ($this->h - ($py2 - $qy2));
11880  	 	 	 $cx3 = $px2;
11881  	 	 	 $cy3 = ($this->h - $py2);
11882  	 	 	 $this->_outCurve($cx1, $cy1, $cx2, $cy2, $cx3, $cy3);
11883  	 	 	 // get bounding box coordinates
11884  	 	 	 $xmin = min($xmin, $cx1, $cx2, $cx3);
11885  	 	 	 $ymin = min($ymin, $cy1, $cy2, $cy3);
11886  	 	 	 $xmax = max($xmax, $cx1, $cx2, $cx3);
11887  	 	 	 $ymax = max($ymax, $cy1, $cy2, $cy3);
11888  	 	 	 // move to next point
11889  	 	 	 $px1 = $px2;
11890  	 	 	 $py1 = $py2;
11891  	 	 	 $qx1 = $qx2;
11892  	 	 	 $qy1 = $qy2;
11893  	 	 }
11894  	 	 if ($pie) {
11895  	 	 	 $this->_outLine($xc, $yc);
11896  	 	 	 // get bounding box coordinates
11897  	 	 	 $xmin = min($xmin, $xc);
11898  	 	 	 $ymin = min($ymin, $yc);
11899  	 	 	 $xmax = max($xmax, $xc);
11900  	 	 	 $ymax = max($ymax, $yc);
11901  	 	 }
11902  	 	 return array($xmin, $ymin, $xmax, $ymax);
11903  	 }
11904  
11905  	 /**
11906  	  * Draws a circle.
11907  	  * A circle is formed from n Bezier curves.
11908  	  * @param $x0 (float) Abscissa of center point.
11909  	  * @param $y0 (float) Ordinate of center point.
11910  	  * @param $r (float) Radius.
11911  	  * @param $angstr: (float) Angle start of draw line. Default value: 0.
11912  	  * @param $angend: (float) Angle finish of draw line. Default value: 360.
11913  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11914  	  * @param $line_style (array) Line style of circle. Array like for SetLineStyle(). Default value: default line style (empty array).
11915  	  * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
11916  	  * @param $nc (integer) Number of curves used to draw a 90 degrees portion of circle.
11917  	  * @public
11918  	  * @since 2.1.000 (2008-01-08)
11919  	  */
11920  	public function Circle($x0, $y0, $r, $angstr=0, $angend=360, $style='', $line_style=array(), $fill_color=array(), $nc=2) {
11921  	 	 $this->Ellipse($x0, $y0, $r, $r, 0, $angstr, $angend, $style, $line_style, $fill_color, $nc);
11922  	 }
11923  
11924  	 /**
11925  	  * Draws a polygonal line
11926  	  * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11927  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11928  	  * @param $line_style (array) Line style of polygon. Array with keys among the following:
11929  	  * <ul>
11930  	  *	  <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11931  	  *	  <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11932  	  * </ul>
11933  	  * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11934  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11935  	  * @since 4.8.003 (2009-09-15)
11936  	  * @public
11937  	  */
11938  	public function PolyLine($p, $style='', $line_style=array(), $fill_color=array()) {
11939  	 	 $this->Polygon($p, $style, $line_style, $fill_color, false);
11940  	 }
11941  
11942  	 /**
11943  	  * Draws a polygon.
11944  	  * @param $p (array) Points 0 to ($np - 1). Array with values (x0, y0, x1, y1,..., x(np-1), y(np - 1))
11945  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
11946  	  * @param $line_style (array) Line style of polygon. Array with keys among the following:
11947  	  * <ul>
11948  	  *	  <li>all: Line style of all lines. Array like for SetLineStyle().</li>
11949  	  *	  <li>0 to ($np - 1): Line style of each line. Array like for SetLineStyle().</li>
11950  	  * </ul>
11951  	  * If a key is not present or is null, not draws the line. Default value is default line style (empty array).
11952  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
11953  	  * @param $closed (boolean) if true the polygon is closes, otherwise will remain open
11954  	  * @public
11955  	  * @since 2.1.000 (2008-01-08)
11956  	  */
11957  	public function Polygon($p, $style='', $line_style=array(), $fill_color=array(), $closed=true) {
11958  	 	 if ($this->state != 2) {
11959  	 	 	 return;
11960  	 	 }
11961  	 	 $nc = count($p); // number of coordinates
11962  	 	 $np = $nc / 2; // number of points
11963  	 	 if ($closed) {
11964  	 	 	 // close polygon by adding the first 2 points at the end (one line)
11965  	 	 	 for ($i = 0; $i < 4; ++$i) {
11966  	 	 	 	 $p[$nc + $i] = $p[$i];
11967  	 	 	 }
11968  	 	 	 // copy style for the last added line
11969  	 	 	 if (isset($line_style[0])) {
11970  	 	 	 	 $line_style[$np] = $line_style[0];
11971  	 	 	 }
11972  	 	 	 $nc += 4;
11973  	 	 }
11974  	 	 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
11975  	 	 	 $this->SetFillColorArray($fill_color);
11976  	 	 }
11977  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
11978  	 	 if ($op == 'f') {
11979  	 	 	 $line_style = array();
11980  	 	 }
11981  	 	 $draw = true;
11982  	 	 if ($line_style) {
11983  	 	 	 if (isset($line_style['all'])) {
11984  	 	 	 	 $this->SetLineStyle($line_style['all']);
11985  	 	 	 } else {
11986  	 	 	 	 $draw = false;
11987  	 	 	 	 if ($op == 'B') {
11988  	 	 	 	 	 // draw fill
11989  	 	 	 	 	 $op = 'f';
11990  	 	 	 	 	 $this->_outPoint($p[0], $p[1]);
11991  	 	 	 	 	 for ($i = 2; $i < $nc; $i = $i + 2) {
11992  	 	 	 	 	 	 $this->_outLine($p[$i], $p[$i + 1]);
11993  	 	 	 	 	 }
11994  	 	 	 	 	 $this->_out($op);
11995  	 	 	 	 }
11996  	 	 	 	 // draw outline
11997  	 	 	 	 $this->_outPoint($p[0], $p[1]);
11998  	 	 	 	 for ($i = 2; $i < $nc; $i = $i + 2) {
11999  	 	 	 	 	 $line_num = ($i / 2) - 1;
12000  	 	 	 	 	 if (isset($line_style[$line_num])) {
12001  	 	 	 	 	 	 if ($line_style[$line_num] != 0) {
12002  	 	 	 	 	 	 	 if (is_array($line_style[$line_num])) {
12003  	 	 	 	 	 	 	 	 $this->_out('S');
12004  	 	 	 	 	 	 	 	 $this->SetLineStyle($line_style[$line_num]);
12005  	 	 	 	 	 	 	 	 $this->_outPoint($p[$i - 2], $p[$i - 1]);
12006  	 	 	 	 	 	 	 	 $this->_outLine($p[$i], $p[$i + 1]);
12007  	 	 	 	 	 	 	 	 $this->_out('S');
12008  	 	 	 	 	 	 	 	 $this->_outPoint($p[$i], $p[$i + 1]);
12009  	 	 	 	 	 	 	 } else {
12010  	 	 	 	 	 	 	 	 $this->_outLine($p[$i], $p[$i + 1]);
12011  	 	 	 	 	 	 	 }
12012  	 	 	 	 	 	 }
12013  	 	 	 	 	 } else {
12014  	 	 	 	 	 	 $this->_outLine($p[$i], $p[$i + 1]);
12015  	 	 	 	 	 }
12016  	 	 	 	 }
12017  	 	 	 	 $this->_out($op);
12018  	 	 	 }
12019  	 	 }
12020  	 	 if ($draw) {
12021  	 	 	 $this->_outPoint($p[0], $p[1]);
12022  	 	 	 for ($i = 2; $i < $nc; $i = $i + 2) {
12023  	 	 	 	 $this->_outLine($p[$i], $p[$i + 1]);
12024  	 	 	 }
12025  	 	 	 $this->_out($op);
12026  	 	 }
12027  	 }
12028  
12029  	 /**
12030  	  * Draws a regular polygon.
12031  	  * @param $x0 (float) Abscissa of center point.
12032  	  * @param $y0 (float) Ordinate of center point.
12033  	  * @param $r: (float) Radius of inscribed circle.
12034  	  * @param $ns (integer) Number of sides.
12035  	  * @param $angle (float) Angle oriented (anti-clockwise). Default value: 0.
12036  	  * @param $draw_circle (boolean) Draw inscribed circle or not. Default value: false.
12037  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12038  	  * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12039  	  * <ul>
12040  	  *	  <li>all: Line style of all sides. Array like for SetLineStyle().</li>
12041  	  *	  <li>0 to ($ns - 1): Line style of each side. Array like for SetLineStyle().</li>
12042  	  * </ul>
12043  	  * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12044  	  * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12045  	  * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12046  	  * <ul>
12047  	  *	  <li>D or empty string: Draw (default).</li>
12048  	  *	  <li>F: Fill.</li>
12049  	  *	  <li>DF or FD: Draw and fill.</li>
12050  	  *	  <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12051  	  *	  <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12052  	  * </ul>
12053  	  * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12054  	  * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12055  	  * @public
12056  	  * @since 2.1.000 (2008-01-08)
12057  	  */
12058  	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()) {
12059  	 	 if (3 > $ns) {
12060  	 	 	 $ns = 3;
12061  	 	 }
12062  	 	 if ($draw_circle) {
12063  	 	 	 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12064  	 	 }
12065  	 	 $p = array();
12066  	 	 for ($i = 0; $i < $ns; ++$i) {
12067  	 	 	 $a = $angle + ($i * 360 / $ns);
12068  	 	 	 $a_rad = deg2rad((float) $a);
12069  	 	 	 $p[] = $x0 + ($r * sin($a_rad));
12070  	 	 	 $p[] = $y0 + ($r * cos($a_rad));
12071  	 	 }
12072  	 	 $this->Polygon($p, $style, $line_style, $fill_color);
12073  	 }
12074  
12075  	 /**
12076  	  * Draws a star polygon
12077  	  * @param $x0 (float) Abscissa of center point.
12078  	  * @param $y0 (float) Ordinate of center point.
12079  	  * @param $r (float) Radius of inscribed circle.
12080  	  * @param $nv (integer) Number of vertices.
12081  	  * @param $ng (integer) Number of gap (if ($ng % $nv = 1) then is a regular polygon).
12082  	  * @param $angle: (float) Angle oriented (anti-clockwise). Default value: 0.
12083  	  * @param $draw_circle: (boolean) Draw inscribed circle or not. Default value is false.
12084  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12085  	  * @param $line_style (array) Line style of polygon sides. Array with keys among the following:
12086  	  * <ul>
12087  	  *	  <li>all: Line style of all sides. Array like for
12088  	  * SetLineStyle().</li>
12089  	  *	  <li>0 to (n - 1): Line style of each side. Array like for SetLineStyle().</li>
12090  	  * </ul>
12091  	  * If a key is not present or is null, not draws the side. Default value is default line style (empty array).
12092  	  * @param $fill_color (array) Fill color. Format: array(red, green, blue). Default value: default color (empty array).
12093  	  * @param $circle_style (string) Style of rendering of inscribed circle (if draws). Possible values are:
12094  	  * <ul>
12095  	  *	  <li>D or empty string: Draw (default).</li>
12096  	  *	  <li>F: Fill.</li>
12097  	  *	  <li>DF or FD: Draw and fill.</li>
12098  	  *	  <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
12099  	  *	  <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
12100  	  * </ul>
12101  	  * @param $circle_outLine_style (array) Line style of inscribed circle (if draws). Array like for SetLineStyle(). Default value: default line style (empty array).
12102  	  * @param $circle_fill_color (array) Fill color of inscribed circle (if draws). Format: array(red, green, blue). Default value: default color (empty array).
12103  	  * @public
12104  	  * @since 2.1.000 (2008-01-08)
12105  	  */
12106  	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()) {
12107  	 	 if ($nv < 2) {
12108  	 	 	 $nv = 2;
12109  	 	 }
12110  	 	 if ($draw_circle) {
12111  	 	 	 $this->Circle($x0, $y0, $r, 0, 360, $circle_style, $circle_outLine_style, $circle_fill_color);
12112  	 	 }
12113  	 	 $p2 = array();
12114  	 	 $visited = array();
12115  	 	 for ($i = 0; $i < $nv; ++$i) {
12116  	 	 	 $a = $angle + ($i * 360 / $nv);
12117  	 	 	 $a_rad = deg2rad((float) $a);
12118  	 	 	 $p2[] = $x0 + ($r * sin($a_rad));
12119  	 	 	 $p2[] = $y0 + ($r * cos($a_rad));
12120  	 	 	 $visited[] = false;
12121  	 	 }
12122  	 	 $p = array();
12123  	 	 $i = 0;
12124  	 	 do {
12125  	 	 	 $p[] = $p2[$i * 2];
12126  	 	 	 $p[] = $p2[($i * 2) + 1];
12127  	 	 	 $visited[$i] = true;
12128  	 	 	 $i += $ng;
12129  	 	 	 $i %= $nv;
12130  	 	 } while (!$visited[$i]);
12131  	 	 $this->Polygon($p, $style, $line_style, $fill_color);
12132  	 }
12133  
12134  	 /**
12135  	  * Draws a rounded rectangle.
12136  	  * @param $x (float) Abscissa of upper-left corner.
12137  	  * @param $y (float) Ordinate of upper-left corner.
12138  	  * @param $w (float) Width.
12139  	  * @param $h (float) Height.
12140  	  * @param $r (float) the radius of the circle used to round off the corners of the rectangle.
12141  	  * @param $round_corner (string) 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 right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12142  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12143  	  * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12144  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12145  	  * @public
12146  	  * @since 2.1.000 (2008-01-08)
12147  	  */
12148  	public function RoundedRect($x, $y, $w, $h, $r, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12149  	 	 $this->RoundedRectXY($x, $y, $w, $h, $r, $r, $round_corner, $style, $border_style, $fill_color);
12150  	 }
12151  
12152  	 /**
12153  	  * Draws a rounded rectangle.
12154  	  * @param $x (float) Abscissa of upper-left corner.
12155  	  * @param $y (float) Ordinate of upper-left corner.
12156  	  * @param $w (float) Width.
12157  	  * @param $h (float) Height.
12158  	  * @param $rx (float) the x-axis radius of the ellipse used to round off the corners of the rectangle.
12159  	  * @param $ry (float) the y-axis radius of the ellipse used to round off the corners of the rectangle.
12160  	  * @param $round_corner (string) 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 right, bottom right, bottom left and top left. Default value: all rounded corner ("1111").
12161  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
12162  	  * @param $border_style (array) Border style of rectangle. Array like for SetLineStyle(). Default value: default line style (empty array).
12163  	  * @param $fill_color (array) Fill color. Format: array(GREY) or array(R,G,B) or array(C,M,Y,K) or array(C,M,Y,K,SpotColorName). Default value: default color (empty array).
12164  	  * @public
12165  	  * @since 4.9.019 (2010-04-22)
12166  	  */
12167  	public function RoundedRectXY($x, $y, $w, $h, $rx, $ry, $round_corner='1111', $style='', $border_style=array(), $fill_color=array()) {
12168  	 	 if ($this->state != 2) {
12169  	 	 	 return;
12170  	 	 }
12171  	 	 if (($round_corner == '0000') OR (($rx == $ry) AND ($rx == 0))) {
12172  	 	 	 // Not rounded
12173  	 	 	 $this->Rect($x, $y, $w, $h, $style, $border_style, $fill_color);
12174  	 	 	 return;
12175  	 	 }
12176  	 	 // Rounded
12177  	 	 if (!(false === strpos($style, 'F')) AND isset($fill_color)) {
12178  	 	 	 $this->SetFillColorArray($fill_color);
12179  	 	 }
12180  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
12181  	 	 if ($op == 'f') {
12182  	 	 	 $border_style = array();
12183  	 	 }
12184  	 	 if ($border_style) {
12185  	 	 	 $this->SetLineStyle($border_style);
12186  	 	 }
12187  	 	 $MyArc = 4 / 3 * (sqrt(2) - 1);
12188  	 	 $this->_outPoint($x + $rx, $y);
12189  	 	 $xc = $x + $w - $rx;
12190  	 	 $yc = $y + $ry;
12191  	 	 $this->_outLine($xc, $y);
12192  	 	 if ($round_corner[0]) {
12193  	 	 	 $this->_outCurve($xc + ($rx * $MyArc), $yc - $ry, $xc + $rx, $yc - ($ry * $MyArc), $xc + $rx, $yc);
12194  	 	 } else {
12195  	 	 	 $this->_outLine($x + $w, $y);
12196  	 	 }
12197  	 	 $xc = $x + $w - $rx;
12198  	 	 $yc = $y + $h - $ry;
12199  	 	 $this->_outLine($x + $w, $yc);
12200  	 	 if ($round_corner[1]) {
12201  	 	 	 $this->_outCurve($xc + $rx, $yc + ($ry * $MyArc), $xc + ($rx * $MyArc), $yc + $ry, $xc, $yc + $ry);
12202  	 	 } else {
12203  	 	 	 $this->_outLine($x + $w, $y + $h);
12204  	 	 }
12205  	 	 $xc = $x + $rx;
12206  	 	 $yc = $y + $h - $ry;
12207  	 	 $this->_outLine($xc, $y + $h);
12208  	 	 if ($round_corner[2]) {
12209  	 	 	 $this->_outCurve($xc - ($rx * $MyArc), $yc + $ry, $xc - $rx, $yc + ($ry * $MyArc), $xc - $rx, $yc);
12210  	 	 } else {
12211  	 	 	 $this->_outLine($x, $y + $h);
12212  	 	 }
12213  	 	 $xc = $x + $rx;
12214  	 	 $yc = $y + $ry;
12215  	 	 $this->_outLine($x, $yc);
12216  	 	 if ($round_corner[3]) {
12217  	 	 	 $this->_outCurve($xc - $rx, $yc - ($ry * $MyArc), $xc - ($rx * $MyArc), $yc - $ry, $xc, $yc - $ry);
12218  	 	 } else {
12219  	 	 	 $this->_outLine($x, $y);
12220  	 	 	 $this->_outLine($x + $rx, $y);
12221  	 	 }
12222  	 	 $this->_out($op);
12223  	 }
12224  
12225  	 /**
12226  	  * Draws a grahic arrow.
12227  	  * @param $x0 (float) Abscissa of first point.
12228  	  * @param $y0 (float) Ordinate of first point.
12229  	  * @param $x1 (float) Abscissa of second point.
12230  	  * @param $y1 (float) Ordinate of second point.
12231  	  * @param $head_style (int) (0 = draw only arrowhead arms, 1 = draw closed arrowhead, but no fill, 2 = closed and filled arrowhead, 3 = filled arrowhead)
12232  	  * @param $arm_size (float) length of arrowhead arms
12233  	  * @param $arm_angle (int) angle between an arm and the shaft
12234  	  * @author Piotr Galecki, Nicola Asuni, Andy Meier
12235  	  * @since 4.6.018 (2009-07-10)
12236  	  */
12237  	public function Arrow($x0, $y0, $x1, $y1, $head_style=0, $arm_size=5, $arm_angle=15) {
12238  	 	 // getting arrow direction angle
12239  	 	 // 0 deg angle is when both arms go along X axis. angle grows clockwise.
12240  	 	 $dir_angle = atan2(($y0 - $y1), ($x0 - $x1));
12241  	 	 if ($dir_angle < 0) {
12242  	 	 	 $dir_angle += (2 * M_PI);
12243  	 	 }
12244  	 	 $arm_angle = deg2rad($arm_angle);
12245  	 	 $sx1 = $x1;
12246  	 	 $sy1 = $y1;
12247  	 	 if ($head_style > 0) {
12248  	 	 	 // calculate the stopping point for the arrow shaft
12249  	 	 	 $sx1 = $x1 + (($arm_size - $this->LineWidth) * cos($dir_angle));
12250  	 	 	 $sy1 = $y1 + (($arm_size - $this->LineWidth) * sin($dir_angle));
12251  	 	 }
12252  	 	 // main arrow line / shaft
12253  	 	 $this->Line($x0, $y0, $sx1, $sy1);
12254  	 	 // left arrowhead arm tip
12255  	 	 $x2L = $x1 + ($arm_size * cos($dir_angle + $arm_angle));
12256  	 	 $y2L = $y1 + ($arm_size * sin($dir_angle + $arm_angle));
12257  	 	 // right arrowhead arm tip
12258  	 	 $x2R = $x1 + ($arm_size * cos($dir_angle - $arm_angle));
12259  	 	 $y2R = $y1 + ($arm_size * sin($dir_angle - $arm_angle));
12260  	 	 $mode = 'D';
12261  	 	 $style = array();
12262  	 	 switch ($head_style) {
12263  	 	 	 case 0: {
12264  	 	 	 	 // draw only arrowhead arms
12265  	 	 	 	 $mode = 'D';
12266  	 	 	 	 $style = array(1, 1, 0);
12267  	 	 	 	 break;
12268  	 	 	 }
12269  	 	 	 case 1: {
12270  	 	 	 	 // draw closed arrowhead, but no fill
12271  	 	 	 	 $mode = 'D';
12272  	 	 	 	 break;
12273  	 	 	 }
12274  	 	 	 case 2: {
12275  	 	 	 	 // closed and filled arrowhead
12276  	 	 	 	 $mode = 'DF';
12277  	 	 	 	 break;
12278  	 	 	 }
12279  	 	 	 case 3: {
12280  	 	 	 	 // filled arrowhead
12281  	 	 	 	 $mode = 'F';
12282  	 	 	 	 break;
12283  	 	 	 }
12284  	 	 }
12285  	 	 $this->Polygon(array($x2L, $y2L, $x1, $y1, $x2R, $y2R), $mode, $style, array());
12286  	 }
12287  
12288  	 // END GRAPHIC FUNCTIONS SECTION -----------------------
12289  
12290  	 /**
12291  	  * Add a Named Destination.
12292  	  * NOTE: destination names are unique, so only last entry will be saved.
12293  	  * @param $name (string) Destination name.
12294  	  * @param $y (float) Y position in user units of the destiantion on the selected page (default = -1 = current position; 0 = page start;).
12295  	  * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12296  	  * @param $x (float) X position in user units of the destiantion on the selected page (default = -1 = current position;).
12297  	  * @return (string) Stripped named destination identifier or false in case of error.
12298  	  * @public
12299  	  * @author Christian Deligant, Nicola Asuni
12300  	  * @since 5.9.097 (2011-06-23)
12301  	  */
12302  	public function setDestination($name, $y=-1, $page='', $x=-1) {
12303  	 	 // remove unsupported characters
12304  	 	 $name = TCPDF_STATIC::encodeNameObject($name);
12305  	 	 if (TCPDF_STATIC::empty_string($name)) {
12306  	 	 	 return false;
12307  	 	 }
12308  	 	 if ($y == -1) {
12309  	 	 	 $y = $this->GetY();
12310  	 	 } elseif ($y < 0) {
12311  	 	 	 $y = 0;
12312  	 	 } elseif ($y > $this->h) {
12313  	 	 	 $y = $this->h;
12314  	 	 }
12315  	 	 if ($x == -1) {
12316  	 	 	 $x = $this->GetX();
12317  	 	 } elseif ($x < 0) {
12318  	 	 	 $x = 0;
12319  	 	 } elseif ($x > $this->w) {
12320  	 	 	 $x = $this->w;
12321  	 	 }
12322  	 	 $fixed = false;
12323  	 	 if (!empty($page) AND (substr($page, 0, 1) == '*')) {
12324  	 	 	 $page = intval(substr($page, 1));
12325  	 	 	 // this page number will not be changed when moving/add/deleting pages
12326  	 	 	 $fixed = true;
12327  	 	 }
12328  	 	 if (empty($page)) {
12329  	 	 	 $page = $this->PageNo();
12330  	 	 	 if (empty($page)) {
12331  	 	 	 	 return;
12332  	 	 	 }
12333  	 	 }
12334  	 	 $this->dests[$name] = array('x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed);
12335  	 	 return $name;
12336  	 }
12337  
12338  	 /**
12339  	  * Return the Named Destination array.
12340  	  * @return (array) Named Destination array.
12341  	  * @public
12342  	  * @author Nicola Asuni
12343  	  * @since 5.9.097 (2011-06-23)
12344  	  */
12345  	public function getDestination() {
12346  	 	 return $this->dests;
12347  	 }
12348  
12349  	 /**
12350  	  * Insert Named Destinations.
12351  	  * @protected
12352  	  * @author Johannes G\FCntert, Nicola Asuni
12353  	  * @since 5.9.098 (2011-06-23)
12354  	  */
12355  	protected function _putdests() {
12356  	 	 if (empty($this->dests)) {
12357  	 	 	 return;
12358  	 	 }
12359  	 	 $this->n_dests = $this->_newobj();
12360  	 	 $out = ' <<';
12361  	 	 foreach($this->dests as $name => $o) {
12362  	 	 	 $out .= ' /'.$name.' '.sprintf('[%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12363  	 	 }
12364  	 	 $out .= ' >>';
12365  	 	 $out .= "\n".'endobj';
12366  	 	 $this->_out($out);
12367  	 }
12368  
12369  	 /**
12370  	  * Adds a bookmark - alias for Bookmark().
12371  	  * @param $txt (string) Bookmark description.
12372  	  * @param $level (int) Bookmark level (minimum value is 0).
12373  	  * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12374  	  * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12375  	  * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12376  	  * @param $color (array) RGB color array (values from 0 to 255).
12377  	  * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12378  	  * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12379  	  * @public
12380  	  */
12381  	public function setBookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12382  	 	 $this->Bookmark($txt, $level, $y, $page, $style, $color, $x, $link);
12383  	 }
12384  
12385  	 /**
12386  	  * Adds a bookmark.
12387  	  * @param $txt (string) Bookmark description.
12388  	  * @param $level (int) Bookmark level (minimum value is 0).
12389  	  * @param $y (float) Y position in user units of the bookmark on the selected page (default = -1 = current position; 0 = page start;).
12390  	  * @param $page (int|string) Target page number (leave empty for current page). If you prefix a page number with the * character, then this page will not be changed when adding/deleting/moving pages.
12391  	  * @param $style (string) Font style: B = Bold, I = Italic, BI = Bold + Italic.
12392  	  * @param $color (array) RGB color array (values from 0 to 255).
12393  	  * @param $x (float) X position in user units of the bookmark on the selected page (default = -1 = current position;).
12394  	  * @param $link (mixed) URL, or numerical link ID, or named destination (# character followed by the destination name), or embedded file (* character followed by the file name).
12395  	  * @public
12396  	  * @since 2.1.002 (2008-02-12)
12397  	  */
12398  	public function Bookmark($txt, $level=0, $y=-1, $page='', $style='', $color=array(0,0,0), $x=-1, $link='') {
12399  	 	 if ($level < 0) {
12400  	 	 	 $level = 0;
12401  	 	 }
12402  	 	 if (isset($this->outlines[0])) {
12403  	 	 	 $lastoutline = end($this->outlines);
12404  	 	 	 $maxlevel = $lastoutline['l'] + 1;
12405  	 	 } else {
12406  	 	 	 $maxlevel = 0;
12407  	 	 }
12408  	 	 if ($level > $maxlevel) {
12409  	 	 	 $level = $maxlevel;
12410  	 	 }
12411  	 	 if ($y == -1) {
12412  	 	 	 $y = $this->GetY();
12413  	 	 } elseif ($y < 0) {
12414  	 	 	 $y = 0;
12415  	 	 } elseif ($y > $this->h) {
12416  	 	 	 $y = $this->h;
12417  	 	 }
12418  	 	 if ($x == -1) {
12419  	 	 	 $x = $this->GetX();
12420  	 	 } elseif ($x < 0) {
12421  	 	 	 $x = 0;
12422  	 	 } elseif ($x > $this->w) {
12423  	 	 	 $x = $this->w;
12424  	 	 }
12425  	 	 $fixed = false;
12426  	 	 $pageAsString = (string) $page;
12427  	 	 if ($pageAsString && $pageAsString[0] == '*') {
12428  	 	 	 $page = intval(substr($page, 1));
12429  	 	 	 // this page number will not be changed when moving/add/deleting pages
12430  	 	 	 $fixed = true;
12431  	 	 }
12432  	 	 if (empty($page)) {
12433  	 	 	 $page = $this->PageNo();
12434  	 	 	 if (empty($page)) {
12435  	 	 	 	 return;
12436  	 	 	 }
12437  	 	 }
12438  	 	 $this->outlines[] = array('t' => $txt, 'l' => $level, 'x' => $x, 'y' => $y, 'p' => $page, 'f' => $fixed, 's' => strtoupper($style), 'c' => $color, 'u' => $link);
12439  	 }
12440  
12441  	 /**
12442  	  * Sort bookmarks for page and key.
12443  	  * @protected
12444  	  * @since 5.9.119 (2011-09-19)
12445  	  */
12446  	protected function sortBookmarks() {
12447  	 	 // get sorting columns
12448  	 	 $outline_p = array();
12449  	 	 $outline_y = array();
12450  	 	 foreach ($this->outlines as $key => $row) {
12451  	 	 	 $outline_p[$key] = $row['p'];
12452  	 	 	 $outline_k[$key] = $key;
12453  	 	 }
12454  	 	 // sort outlines by page and original position
12455  	 	 array_multisort($outline_p, SORT_NUMERIC, SORT_ASC, $outline_k, SORT_NUMERIC, SORT_ASC, $this->outlines);
12456  	 }
12457  
12458  	 /**
12459  	  * Create a bookmark PDF string.
12460  	  * @protected
12461  	  * @author Olivier Plathey, Nicola Asuni
12462  	  * @since 2.1.002 (2008-02-12)
12463  	  */
12464  	protected function _putbookmarks() {
12465  	 	 $nb = count($this->outlines);
12466  	 	 if ($nb == 0) {
12467  	 	 	 return;
12468  	 	 }
12469  	 	 // sort bookmarks
12470  	 	 $this->sortBookmarks();
12471  	 	 $lru = array();
12472  	 	 $level = 0;
12473  	 	 foreach ($this->outlines as $i => $o) {
12474  	 	 	 if ($o['l'] > 0) {
12475  	 	 	 	 $parent = $lru[($o['l'] - 1)];
12476  	 	 	 	 //Set parent and last pointers
12477  	 	 	 	 $this->outlines[$i]['parent'] = $parent;
12478  	 	 	 	 $this->outlines[$parent]['last'] = $i;
12479  	 	 	 	 if ($o['l'] > $level) {
12480  	 	 	 	 	 //Level increasing: set first pointer
12481  	 	 	 	 	 $this->outlines[$parent]['first'] = $i;
12482  	 	 	 	 }
12483  	 	 	 } else {
12484  	 	 	 	 $this->outlines[$i]['parent'] = $nb;
12485  	 	 	 }
12486  	 	 	 if (($o['l'] <= $level) AND ($i > 0)) {
12487  	 	 	 	 //Set prev and next pointers
12488  	 	 	 	 $prev = $lru[$o['l']];
12489  	 	 	 	 $this->outlines[$prev]['next'] = $i;
12490  	 	 	 	 $this->outlines[$i]['prev'] = $prev;
12491  	 	 	 }
12492  	 	 	 $lru[$o['l']] = $i;
12493  	 	 	 $level = $o['l'];
12494  	 	 }
12495  	 	 //Outline items
12496  	 	 $n = $this->n + 1;
12497  	 	 $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';
12498  	 	 foreach ($this->outlines as $i => $o) {
12499  	 	 	 $oid = $this->_newobj();
12500  	 	 	 // covert HTML title to string
12501  	 	 	 $title = preg_replace($nltags, "\n", $o['t']);
12502  	 	 	 $title = preg_replace("/[\r]+/si", '', $title);
12503  	 	 	 $title = preg_replace("/[\n]+/si", "\n", $title);
12504  	 	 	 $title = strip_tags($title);
12505  	 	 	 $title = $this->stringTrim($title);
12506  	 	 	 $out = '<</Title '.$this->_textstring($title, $oid);
12507  	 	 	 $out .= ' /Parent '.($n + $o['parent']).' 0 R';
12508  	 	 	 if (isset($o['prev'])) {
12509  	 	 	 	 $out .= ' /Prev '.($n + $o['prev']).' 0 R';
12510  	 	 	 }
12511  	 	 	 if (isset($o['next'])) {
12512  	 	 	 	 $out .= ' /Next '.($n + $o['next']).' 0 R';
12513  	 	 	 }
12514  	 	 	 if (isset($o['first'])) {
12515  	 	 	 	 $out .= ' /First '.($n + $o['first']).' 0 R';
12516  	 	 	 }
12517  	 	 	 if (isset($o['last'])) {
12518  	 	 	 	 $out .= ' /Last '.($n + $o['last']).' 0 R';
12519  	 	 	 }
12520  	 	 	 if (isset($o['u']) AND !empty($o['u'])) {
12521  	 	 	 	 // link
12522  	 	 	 	 if (is_string($o['u'])) {
12523  	 	 	 	 	 if ($o['u'][0] == '#') {
12524  	 	 	 	 	 	 // internal destination
12525  	 	 	 	 	 	 $out .= ' /Dest /'.TCPDF_STATIC::encodeNameObject(substr($o['u'], 1));
12526  	 	 	 	 	 } elseif ($o['u'][0] == '%') {
12527  	 	 	 	 	 	 // embedded PDF file
12528  	 	 	 	 	 	 $filename = basename(substr($o['u'], 1));
12529  	 	 	 	 	 	 $out .= ' /A <</S /GoToE /D [0 /Fit] /NewWindow true /T << /R /C /P '.($o['p'] - 1).' /A '.$this->embeddedfiles[$filename]['a'].' >> >>';
12530  	 	 	 	 	 } elseif ($o['u'][0] == '*') {
12531  	 	 	 	 	 	 // embedded generic file
12532  	 	 	 	 	 	 $filename = basename(substr($o['u'], 1));
12533  	 	 	 	 	 	 $jsa = 'var D=event.target.doc;var MyData=D.dataObjects;for (var i in MyData) if (MyData[i].path=="'.$filename.'") D.exportDataObject( { cName : MyData[i].name, nLaunch : 2});';
12534  	 	 	 	 	 	 $out .= ' /A <</S /JavaScript /JS '.$this->_textstring($jsa, $oid).'>>';
12535  	 	 	 	 	 } else {
12536  	 	 	 	 	 	 // external URI link
12537  	 	 	 	 	 	 $out .= ' /A <</S /URI /URI '.$this->_datastring($this->unhtmlentities($o['u']), $oid).'>>';
12538  	 	 	 	 	 }
12539  	 	 	 	 } elseif (isset($this->links[$o['u']])) {
12540  	 	 	 	 	 // internal link ID
12541  	 	 	 	 	 $l = $this->links[$o['u']];
12542  	 	 	 	 	 if (isset($this->page_obj_id[($l['p'])])) {
12543  	 	 	 	 	 	 $out .= sprintf(' /Dest [%u 0 R /XYZ 0 %F null]', $this->page_obj_id[($l['p'])], ($this->pagedim[$l['p']]['h'] - ($l['y'] * $this->k)));
12544  	 	 	 	 	 }
12545  	 	 	 	 }
12546  	 	 	 } elseif (isset($this->page_obj_id[($o['p'])])) {
12547  	 	 	 	 // link to a page
12548  	 	 	 	 $out .= ' '.sprintf('/Dest [%u 0 R /XYZ %F %F null]', $this->page_obj_id[($o['p'])], ($o['x'] * $this->k), ($this->pagedim[$o['p']]['h'] - ($o['y'] * $this->k)));
12549  	 	 	 }
12550  	 	 	 // set font style
12551  	 	 	 $style = 0;
12552  	 	 	 if (!empty($o['s'])) {
12553  	 	 	 	 // bold
12554  	 	 	 	 if (strpos($o['s'], 'B') !== false) {
12555  	 	 	 	 	 $style |= 2;
12556  	 	 	 	 }
12557  	 	 	 	 // oblique
12558  	 	 	 	 if (strpos($o['s'], 'I') !== false) {
12559  	 	 	 	 	 $style |= 1;
12560  	 	 	 	 }
12561  	 	 	 }
12562  	 	 	 $out .= sprintf(' /F %d', $style);
12563  	 	 	 // set bookmark color
12564  	 	 	 if (isset($o['c']) AND is_array($o['c']) AND (count($o['c']) == 3)) {
12565  	 	 	 	 $color = array_values($o['c']);
12566  	 	 	 	 $out .= sprintf(' /C [%F %F %F]', ($color[0] / 255), ($color[1] / 255), ($color[2] / 255));
12567  	 	 	 } else {
12568  	 	 	 	 // black
12569  	 	 	 	 $out .= ' /C [0.0 0.0 0.0]';
12570  	 	 	 }
12571  	 	 	 $out .= ' /Count 0'; // normally closed item
12572  	 	 	 $out .= ' >>';
12573  	 	 	 $out .= "\n".'endobj';
12574  	 	 	 $this->_out($out);
12575  	 	 }
12576  	 	 //Outline root
12577  	 	 $this->OutlineRoot = $this->_newobj();
12578  	 	 $this->_out('<< /Type /Outlines /First '.$n.' 0 R /Last '.($n + $lru[0]).' 0 R >>'."\n".'endobj');
12579  	 }
12580  
12581  	 // --- JAVASCRIPT ------------------------------------------------------
12582  
12583  	 /**
12584  	  * Adds a javascript
12585  	  * @param $script (string) Javascript code
12586  	  * @public
12587  	  * @author Johannes G\FCntert, Nicola Asuni
12588  	  * @since 2.1.002 (2008-02-12)
12589  	  */
12590  	public function IncludeJS($script) {
12591  	 	 $this->javascript .= $script;
12592  	 }
12593  
12594  	 /**
12595  	  * Adds a javascript object and return object ID
12596  	  * @param $script (string) Javascript code
12597  	  * @param $onload (boolean) if true executes this object when opening the document
12598  	  * @return int internal object ID
12599  	  * @public
12600  	  * @author Nicola Asuni
12601  	  * @since 4.8.000 (2009-09-07)
12602  	  */
12603  	public function addJavascriptObject($script, $onload=false) {
12604  	 	 if ($this->pdfa_mode) {
12605  	 	 	 // javascript is not allowed in PDF/A mode
12606  	 	 	 return false;
12607  	 	 }
12608  	 	 ++$this->n;
12609  	 	 $this->js_objects[$this->n] = array('n' => $this->n, 'js' => $script, 'onload' => $onload);
12610  	 	 return $this->n;
12611  	 }
12612  
12613  	 /**
12614  	  * Create a javascript PDF string.
12615  	  * @protected
12616  	  * @author Johannes G\FCntert, Nicola Asuni
12617  	  * @since 2.1.002 (2008-02-12)
12618  	  */
12619  	protected function _putjavascript() {
12620  	 	 if ($this->pdfa_mode OR (empty($this->javascript) AND empty($this->js_objects))) {
12621  	 	 	 return;
12622  	 	 }
12623  	 	 if (strpos($this->javascript, 'this.addField') > 0) {
12624  	 	 	 if (!$this->ur['enabled']) {
12625  	 	 	 	 //$this->setUserRights();
12626  	 	 	 }
12627  	 	 	 // the following two lines are used to avoid form fields duplication after saving
12628  	 	 	 // The addField method only works when releasing user rights (UR3)
12629  	 	 	 $jsa = sprintf("ftcpdfdocsaved=this.addField('%s','%s',%d,[%F,%F,%F,%F]);", 'tcpdfdocsaved', 'text', 0, 0, 1, 0, 1);
12630  	 	 	 $jsb = "getField('tcpdfdocsaved').value='saved';";
12631  	 	 	 $this->javascript = $jsa."\n".$this->javascript."\n".$jsb;
12632  	 	 }
12633  	 	 // name tree for javascript
12634  	 	 $this->n_js = '<< /Names [';
12635  	 	 if (!empty($this->javascript)) {
12636  	 	 	 $this->n_js .= ' (EmbeddedJS) '.($this->n + 1).' 0 R';
12637  	 	 }
12638  	 	 if (!empty($this->js_objects)) {
12639  	 	 	 foreach ($this->js_objects as $key => $val) {
12640  	 	 	 	 if ($val['onload']) {
12641  	 	 	 	 	 $this->n_js .= ' (JS'.$key.') '.$key.' 0 R';
12642  	 	 	 	 }
12643  	 	 	 }
12644  	 	 }
12645  	 	 $this->n_js .= ' ] >>';
12646  	 	 // default Javascript object
12647  	 	 if (!empty($this->javascript)) {
12648  	 	 	 $obj_id = $this->_newobj();
12649  	 	 	 $out = '<< /S /JavaScript';
12650  	 	 	 $out .= ' /JS '.$this->_textstring($this->javascript, $obj_id);
12651  	 	 	 $out .= ' >>';
12652  	 	 	 $out .= "\n".'endobj';
12653  	 	 	 $this->_out($out);
12654  	 	 }
12655  	 	 // additional Javascript objects
12656  	 	 if (!empty($this->js_objects)) {
12657  	 	 	 foreach ($this->js_objects as $key => $val) {
12658  	 	 	 	 $out = $this->_getobj($key)."\n".' << /S /JavaScript /JS '.$this->_textstring($val['js'], $key).' >>'."\n".'endobj';
12659  	 	 	 	 $this->_out($out);
12660  	 	 	 }
12661  	 	 }
12662  	 }
12663  
12664  	 /**
12665  	  * Adds a javascript form field.
12666  	  * @param $type (string) field type
12667  	  * @param $name (string) field name
12668  	  * @param $x (int) horizontal position
12669  	  * @param $y (int) vertical position
12670  	  * @param $w (int) width
12671  	  * @param $h (int) height
12672  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12673  	  * @protected
12674  	  * @author Denis Van Nuffelen, Nicola Asuni
12675  	  * @since 2.1.002 (2008-02-12)
12676  	  */
12677  	protected function _addfield($type, $name, $x, $y, $w, $h, $prop) {
12678  	 	 if ($this->rtl) {
12679  	 	 	 $x = $x - $w;
12680  	 	 }
12681  	 	 // the followind avoid fields duplication after saving the document
12682  	 	 $this->javascript .= "if (getField('tcpdfdocsaved').value != 'saved') {";
12683  	 	 $k = $this->k;
12684  	 	 $this->javascript .= sprintf("f".$name."=this.addField('%s','%s',%u,[%F,%F,%F,%F]);", $name, $type, $this->PageNo()-1, $x*$k, ($this->h-$y)*$k+1, ($x+$w)*$k, ($this->h-$y-$h)*$k+1)."\n";
12685  	 	 $this->javascript .= 'f'.$name.'.textSize='.$this->FontSizePt.";\n";
12686  	 	 foreach($prop as $key => $val) {
12687  	 	 	 if (strcmp(substr($key, -5), 'Color') == 0) {
12688  	 	 	 	 $val = TCPDF_COLORS::_JScolor($val);
12689  	 	 	 } else {
12690  	 	 	 	 $val = "'".$val."'";
12691  	 	 	 }
12692  	 	 	 $this->javascript .= 'f'.$name.'.'.$key.'='.$val.";\n";
12693  	 	 }
12694  	 	 if ($this->rtl) {
12695  	 	 	 $this->x -= $w;
12696  	 	 } else {
12697  	 	 	 $this->x += $w;
12698  	 	 }
12699  	 	 $this->javascript .= '}';
12700  	 }
12701  
12702  	 // --- FORM FIELDS -----------------------------------------------------
12703  
12704  
12705  
12706  	 /**
12707  	  * Set default properties for form fields.
12708  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12709  	  * @public
12710  	  * @author Nicola Asuni
12711  	  * @since 4.8.000 (2009-09-06)
12712  	  */
12713  	public function setFormDefaultProp($prop=array()) {
12714  	 	 $this->default_form_prop = $prop;
12715  	 }
12716  
12717  	 /**
12718  	  * Return the default properties for form fields.
12719  	  * @return array $prop javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12720  	  * @public
12721  	  * @author Nicola Asuni
12722  	  * @since 4.8.000 (2009-09-06)
12723  	  */
12724  	public function getFormDefaultProp() {
12725  	 	 return $this->default_form_prop;
12726  	 }
12727  
12728  	 /**
12729  	  * Creates a text field
12730  	  * @param $name (string) field name
12731  	  * @param $w (float) Width of the rectangle
12732  	  * @param $h (float) Height of the rectangle
12733  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12734  	  * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12735  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
12736  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
12737  	  * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12738  	  * @public
12739  	  * @author Nicola Asuni
12740  	  * @since 4.8.000 (2009-09-07)
12741  	  */
12742  	public function TextField($name, $w, $h, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12743  	 	 if ($x === '') {
12744  	 	 	 $x = $this->x;
12745  	 	 }
12746  	 	 if ($y === '') {
12747  	 	 	 $y = $this->y;
12748  	 	 }
12749  	 	 // check page for no-write regions and adapt page margins if necessary
12750  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12751  	 	 if ($js) {
12752  	 	 	 $this->_addfield('text', $name, $x, $y, $w, $h, $prop);
12753  	 	 	 return;
12754  	 	 }
12755  	 	 // get default style
12756  	 	 $prop = array_merge($this->getFormDefaultProp(), $prop);
12757  	 	 // get annotation data
12758  	 	 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12759  	 	 // set default appearance stream
12760  	 	 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12761  	 	 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12762  	 	 $popt['da'] = $fontstyle;
12763  	 	 // build appearance stream
12764  	 	 $popt['ap'] = array();
12765  	 	 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12766  	 	 $text = '';
12767  	 	 if (isset($prop['value']) AND !empty($prop['value'])) {
12768  	 	 	 $text = $prop['value'];
12769  	 	 } elseif (isset($opt['v']) AND !empty($opt['v'])) {
12770  	 	 	 $text = $opt['v'];
12771  	 	 }
12772  	 	 $tmpid = $this->startTemplate($w, $h, false);
12773  	 	 $align = '';
12774  	 	 if (isset($popt['q'])) {
12775  	 	 	 switch ($popt['q']) {
12776  	 	 	 	 case 0: {
12777  	 	 	 	 	 $align = 'L';
12778  	 	 	 	 	 break;
12779  	 	 	 	 }
12780  	 	 	 	 case 1: {
12781  	 	 	 	 	 $align = 'C';
12782  	 	 	 	 	 break;
12783  	 	 	 	 }
12784  	 	 	 	 case 2: {
12785  	 	 	 	 	 $align = 'R';
12786  	 	 	 	 	 break;
12787  	 	 	 	 }
12788  	 	 	 	 default: {
12789  	 	 	 	 	 $align = '';
12790  	 	 	 	 	 break;
12791  	 	 	 	 }
12792  	 	 	 }
12793  	 	 }
12794  	 	 $this->MultiCell($w, $h, $text, 0, $align, false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
12795  	 	 $this->endTemplate();
12796  	 	 --$this->n;
12797  	 	 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
12798  	 	 unset($this->xobjects[$tmpid]);
12799  	 	 $popt['ap']['n'] .= 'Q EMC';
12800  	 	 // merge options
12801  	 	 $opt = array_merge($popt, $opt);
12802  	 	 // remove some conflicting options
12803  	 	 unset($opt['bs']);
12804  	 	 // set remaining annotation data
12805  	 	 $opt['Subtype'] = 'Widget';
12806  	 	 $opt['ft'] = 'Tx';
12807  	 	 $opt['t'] = $name;
12808  	 	 // Additional annotation's parameters (check _putannotsobj() method):
12809  	 	 //$opt['f']
12810  	 	 //$opt['as']
12811  	 	 //$opt['bs']
12812  	 	 //$opt['be']
12813  	 	 //$opt['c']
12814  	 	 //$opt['border']
12815  	 	 //$opt['h']
12816  	 	 //$opt['mk'];
12817  	 	 //$opt['mk']['r']
12818  	 	 //$opt['mk']['bc'];
12819  	 	 //$opt['mk']['bg'];
12820  	 	 unset($opt['mk']['ca']);
12821  	 	 unset($opt['mk']['rc']);
12822  	 	 unset($opt['mk']['ac']);
12823  	 	 unset($opt['mk']['i']);
12824  	 	 unset($opt['mk']['ri']);
12825  	 	 unset($opt['mk']['ix']);
12826  	 	 unset($opt['mk']['if']);
12827  	 	 //$opt['mk']['if']['sw'];
12828  	 	 //$opt['mk']['if']['s'];
12829  	 	 //$opt['mk']['if']['a'];
12830  	 	 //$opt['mk']['if']['fb'];
12831  	 	 unset($opt['mk']['tp']);
12832  	 	 //$opt['tu']
12833  	 	 //$opt['tm']
12834  	 	 //$opt['ff']
12835  	 	 //$opt['v']
12836  	 	 //$opt['dv']
12837  	 	 //$opt['a']
12838  	 	 //$opt['aa']
12839  	 	 //$opt['q']
12840  	 	 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
12841  	 	 if ($this->rtl) {
12842  	 	 	 $this->x -= $w;
12843  	 	 } else {
12844  	 	 	 $this->x += $w;
12845  	 	 }
12846  	 }
12847  
12848  	 /**
12849  	  * Creates a RadioButton field.
12850  	  * @param $name (string) Field name.
12851  	  * @param $w (int) Width of the radio button.
12852  	  * @param $prop (array) Javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12853  	  * @param $opt (array) Annotation parameters. Possible values are described on official PDF32000_2008 reference.
12854  	  * @param $onvalue (string) Value to be returned if selected.
12855  	  * @param $checked (boolean) Define the initial state.
12856  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
12857  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
12858  	  * @param $js (boolean) If true put the field using JavaScript (requires Acrobat Writer to be rendered).
12859  	  * @public
12860  	  * @author Nicola Asuni
12861  	  * @since 4.8.000 (2009-09-07)
12862  	  */
12863  	public function RadioButton($name, $w, $prop=array(), $opt=array(), $onvalue='On', $checked=false, $x='', $y='', $js=false) {
12864  	 	 if ($x === '') {
12865  	 	 	 $x = $this->x;
12866  	 	 }
12867  	 	 if ($y === '') {
12868  	 	 	 $y = $this->y;
12869  	 	 }
12870  	 	 // check page for no-write regions and adapt page margins if necessary
12871  	 	 list($x, $y) = $this->checkPageRegions($w, $x, $y);
12872  	 	 if ($js) {
12873  	 	 	 $this->_addfield('radiobutton', $name, $x, $y, $w, $w, $prop);
12874  	 	 	 return;
12875  	 	 }
12876  	 	 if (TCPDF_STATIC::empty_string($onvalue)) {
12877  	 	 	 $onvalue = 'On';
12878  	 	 }
12879  	 	 if ($checked) {
12880  	 	 	 $defval = $onvalue;
12881  	 	 } else {
12882  	 	 	 $defval = 'Off';
12883  	 	 }
12884  	 	 // set font
12885  	 	 $font = 'zapfdingbats';
12886  	 	 if ($this->pdfa_mode) {
12887  	 	 	 // all fonts must be embedded
12888  	 	 	 $font = 'pdfa'.$font;
12889  	 	 }
12890  	 	 $this->AddFont($font);
12891  	 	 $tmpfont = $this->getFontBuffer($font);
12892  	 	 // set data for parent group
12893  	 	 if (!isset($this->radiobutton_groups[$this->page])) {
12894  	 	 	 $this->radiobutton_groups[$this->page] = array();
12895  	 	 }
12896  	 	 if (!isset($this->radiobutton_groups[$this->page][$name])) {
12897  	 	 	 $this->radiobutton_groups[$this->page][$name] = array();
12898  	 	 	 ++$this->n;
12899  	 	 	 $this->radiobutton_groups[$this->page][$name]['n'] = $this->n;
12900  	 	 	 $this->radio_groups[] = $this->n;
12901  	 	 }
12902  	 	 $kid = ($this->n + 1);
12903  	 	 // save object ID to be added on Kids entry on parent object
12904  	 	 $this->radiobutton_groups[$this->page][$name][] = array('kid' => $kid, 'def' => $defval);
12905  	 	 // get default style
12906  	 	 $prop = array_merge($this->getFormDefaultProp(), $prop);
12907  	 	 $prop['NoToggleToOff'] = 'true';
12908  	 	 $prop['Radio'] = 'true';
12909  	 	 $prop['borderStyle'] = 'inset';
12910  	 	 // get annotation data
12911  	 	 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12912  	 	 // set additional default options
12913  	 	 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
12914  	 	 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
12915  	 	 $popt['da'] = $fontstyle;
12916  	 	 // build appearance stream
12917  	 	 $popt['ap'] = array();
12918  	 	 $popt['ap']['n'] = array();
12919  	 	 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][108])) / 2) * $this->k);
12920  	 	 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
12921  	 	 $popt['ap']['n'][$onvalue] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(108).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12922  	 	 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(109).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
12923  	 	 if (!isset($popt['mk'])) {
12924  	 	 	 $popt['mk'] = array();
12925  	 	 }
12926  	 	 $popt['mk']['ca'] = '(l)';
12927  	 	 // merge options
12928  	 	 $opt = array_merge($popt, $opt);
12929  	 	 // set remaining annotation data
12930  	 	 $opt['Subtype'] = 'Widget';
12931  	 	 $opt['ft'] = 'Btn';
12932  	 	 if ($checked) {
12933  	 	 	 $opt['v'] = array('/'.$onvalue);
12934  	 	 	 $opt['as'] = $onvalue;
12935  	 	 } else {
12936  	 	 	 $opt['as'] = 'Off';
12937  	 	 }
12938  	 	 // store readonly flag
12939  	 	 if (!isset($this->radiobutton_groups[$this->page][$name]['#readonly#'])) {
12940  	 	 	 $this->radiobutton_groups[$this->page][$name]['#readonly#'] = false;
12941  	 	 }
12942  	 	 $this->radiobutton_groups[$this->page][$name]['#readonly#'] |= ($opt['f'] & 64);
12943  	 	 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
12944  	 	 if ($this->rtl) {
12945  	 	 	 $this->x -= $w;
12946  	 	 } else {
12947  	 	 	 $this->x += $w;
12948  	 	 }
12949  	 }
12950  
12951  	 /**
12952  	  * Creates a List-box field
12953  	  * @param $name (string) field name
12954  	  * @param $w (int) width
12955  	  * @param $h (int) height
12956  	  * @param $values (array) array containing the list of values.
12957  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
12958  	  * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
12959  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
12960  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
12961  	  * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
12962  	  * @public
12963  	  * @author Nicola Asuni
12964  	  * @since 4.8.000 (2009-09-07)
12965  	  */
12966  	public function ListBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
12967  	 	 if ($x === '') {
12968  	 	 	 $x = $this->x;
12969  	 	 }
12970  	 	 if ($y === '') {
12971  	 	 	 $y = $this->y;
12972  	 	 }
12973  	 	 // check page for no-write regions and adapt page margins if necessary
12974  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
12975  	 	 if ($js) {
12976  	 	 	 $this->_addfield('listbox', $name, $x, $y, $w, $h, $prop);
12977  	 	 	 $s = '';
12978  	 	 	 foreach ($values as $value) {
12979  	 	 	 	 if (is_array($value)) {
12980  	 	 	 	 	 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
12981  	 	 	 	 } else {
12982  	 	 	 	 	 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
12983  	 	 	 	 }
12984  	 	 	 }
12985  	 	 	 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
12986  	 	 	 return;
12987  	 	 }
12988  	 	 // get default style
12989  	 	 $prop = array_merge($this->getFormDefaultProp(), $prop);
12990  	 	 // get annotation data
12991  	 	 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
12992  	 	 // set additional default values
12993  	 	 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
12994  	 	 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
12995  	 	 $popt['da'] = $fontstyle;
12996  	 	 // build appearance stream
12997  	 	 $popt['ap'] = array();
12998  	 	 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
12999  	 	 $text = '';
13000  	 	 foreach($values as $item) {
13001  	 	 	 if (is_array($item)) {
13002  	 	 	 	 $text .= $item[1]."\n";
13003  	 	 	 } else {
13004  	 	 	 	 $text .= $item."\n";
13005  	 	 	 }
13006  	 	 }
13007  	 	 $tmpid = $this->startTemplate($w, $h, false);
13008  	 	 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13009  	 	 $this->endTemplate();
13010  	 	 --$this->n;
13011  	 	 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13012  	 	 unset($this->xobjects[$tmpid]);
13013  	 	 $popt['ap']['n'] .= 'Q EMC';
13014  	 	 // merge options
13015  	 	 $opt = array_merge($popt, $opt);
13016  	 	 // set remaining annotation data
13017  	 	 $opt['Subtype'] = 'Widget';
13018  	 	 $opt['ft'] = 'Ch';
13019  	 	 $opt['t'] = $name;
13020  	 	 $opt['opt'] = $values;
13021  	 	 unset($opt['mk']['ca']);
13022  	 	 unset($opt['mk']['rc']);
13023  	 	 unset($opt['mk']['ac']);
13024  	 	 unset($opt['mk']['i']);
13025  	 	 unset($opt['mk']['ri']);
13026  	 	 unset($opt['mk']['ix']);
13027  	 	 unset($opt['mk']['if']);
13028  	 	 unset($opt['mk']['tp']);
13029  	 	 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13030  	 	 if ($this->rtl) {
13031  	 	 	 $this->x -= $w;
13032  	 	 } else {
13033  	 	 	 $this->x += $w;
13034  	 	 }
13035  	 }
13036  
13037  	 /**
13038  	  * Creates a Combo-box field
13039  	  * @param $name (string) field name
13040  	  * @param $w (int) width
13041  	  * @param $h (int) height
13042  	  * @param $values (array) array containing the list of values.
13043  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13044  	  * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13045  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
13046  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
13047  	  * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13048  	  * @public
13049  	  * @author Nicola Asuni
13050  	  * @since 4.8.000 (2009-09-07)
13051  	  */
13052  	public function ComboBox($name, $w, $h, $values, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13053  	 	 if ($x === '') {
13054  	 	 	 $x = $this->x;
13055  	 	 }
13056  	 	 if ($y === '') {
13057  	 	 	 $y = $this->y;
13058  	 	 }
13059  	 	 // check page for no-write regions and adapt page margins if necessary
13060  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13061  	 	 if ($js) {
13062  	 	 	 $this->_addfield('combobox', $name, $x, $y, $w, $h, $prop);
13063  	 	 	 $s = '';
13064  	 	 	 foreach ($values as $value) {
13065  	 	 	 	 if (is_array($value)) {
13066  	 	 	 	 	 $s .= ',[\''.addslashes($value[1]).'\',\''.addslashes($value[0]).'\']';
13067  	 	 	 	 } else {
13068  	 	 	 	 	 $s .= ',[\''.addslashes($value).'\',\''.addslashes($value).'\']';
13069  	 	 	 	 }
13070  	 	 	 }
13071  	 	 	 $this->javascript .= 'f'.$name.'.setItems('.substr($s, 1).');'."\n";
13072  	 	 	 return;
13073  	 	 }
13074  	 	 // get default style
13075  	 	 $prop = array_merge($this->getFormDefaultProp(), $prop);
13076  	 	 $prop['Combo'] = true;
13077  	 	 // get annotation data
13078  	 	 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13079  	 	 // set additional default options
13080  	 	 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13081  	 	 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13082  	 	 $popt['da'] = $fontstyle;
13083  	 	 // build appearance stream
13084  	 	 $popt['ap'] = array();
13085  	 	 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13086  	 	 $text = '';
13087  	 	 foreach($values as $item) {
13088  	 	 	 if (is_array($item)) {
13089  	 	 	 	 $text .= $item[1]."\n";
13090  	 	 	 } else {
13091  	 	 	 	 $text .= $item."\n";
13092  	 	 	 }
13093  	 	 }
13094  	 	 $tmpid = $this->startTemplate($w, $h, false);
13095  	 	 $this->MultiCell($w, $h, $text, 0, '', false, 0, 0, 0, true, 0, false, true, 0, 'T', false);
13096  	 	 $this->endTemplate();
13097  	 	 --$this->n;
13098  	 	 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13099  	 	 unset($this->xobjects[$tmpid]);
13100  	 	 $popt['ap']['n'] .= 'Q EMC';
13101  	 	 // merge options
13102  	 	 $opt = array_merge($popt, $opt);
13103  	 	 // set remaining annotation data
13104  	 	 $opt['Subtype'] = 'Widget';
13105  	 	 $opt['ft'] = 'Ch';
13106  	 	 $opt['t'] = $name;
13107  	 	 $opt['opt'] = $values;
13108  	 	 unset($opt['mk']['ca']);
13109  	 	 unset($opt['mk']['rc']);
13110  	 	 unset($opt['mk']['ac']);
13111  	 	 unset($opt['mk']['i']);
13112  	 	 unset($opt['mk']['ri']);
13113  	 	 unset($opt['mk']['ix']);
13114  	 	 unset($opt['mk']['if']);
13115  	 	 unset($opt['mk']['tp']);
13116  	 	 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13117  	 	 if ($this->rtl) {
13118  	 	 	 $this->x -= $w;
13119  	 	 } else {
13120  	 	 	 $this->x += $w;
13121  	 	 }
13122  	 }
13123  
13124  	 /**
13125  	  * Creates a CheckBox field
13126  	  * @param $name (string) field name
13127  	  * @param $w (int) width
13128  	  * @param $checked (boolean) define the initial state.
13129  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13130  	  * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13131  	  * @param $onvalue (string) value to be returned if selected.
13132  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
13133  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
13134  	  * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13135  	  * @public
13136  	  * @author Nicola Asuni
13137  	  * @since 4.8.000 (2009-09-07)
13138  	  */
13139  	public function CheckBox($name, $w, $checked=false, $prop=array(), $opt=array(), $onvalue='Yes', $x='', $y='', $js=false) {
13140  	 	 if ($x === '') {
13141  	 	 	 $x = $this->x;
13142  	 	 }
13143  	 	 if ($y === '') {
13144  	 	 	 $y = $this->y;
13145  	 	 }
13146  	 	 // check page for no-write regions and adapt page margins if necessary
13147  	 	 list($x, $y) = $this->checkPageRegions($w, $x, $y);
13148  	 	 if ($js) {
13149  	 	 	 $this->_addfield('checkbox', $name, $x, $y, $w, $w, $prop);
13150  	 	 	 return;
13151  	 	 }
13152  	 	 if (!isset($prop['value'])) {
13153  	 	 	 $prop['value'] = array('Yes');
13154  	 	 }
13155  	 	 // get default style
13156  	 	 $prop = array_merge($this->getFormDefaultProp(), $prop);
13157  	 	 $prop['borderStyle'] = 'inset';
13158  	 	 // get annotation data
13159  	 	 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13160  	 	 // set additional default options
13161  	 	 $font = 'zapfdingbats';
13162  	 	 if ($this->pdfa_mode) {
13163  	 	 	 // all fonts must be embedded
13164  	 	 	 $font = 'pdfa'.$font;
13165  	 	 }
13166  	 	 $this->AddFont($font);
13167  	 	 $tmpfont = $this->getFontBuffer($font);
13168  	 	 $this->annotation_fonts[$tmpfont['fontkey']] = $tmpfont['i'];
13169  	 	 $fontstyle = sprintf('/F%d %F Tf %s', $tmpfont['i'], $this->FontSizePt, $this->TextColor);
13170  	 	 $popt['da'] = $fontstyle;
13171  	 	 // build appearance stream
13172  	 	 $popt['ap'] = array();
13173  	 	 $popt['ap']['n'] = array();
13174  	 	 $fx = ((($w - $this->getAbsFontMeasure($tmpfont['cw'][110])) / 2) * $this->k);
13175  	 	 $fy = (($w - ((($tmpfont['desc']['Ascent'] - $tmpfont['desc']['Descent']) * $this->FontSizePt / 1000) / $this->k)) * $this->k);
13176  	 	 $popt['ap']['n']['Yes'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(110).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13177  	 	 $popt['ap']['n']['Off'] = sprintf('q %s BT /F%d %F Tf %F %F Td ('.chr(111).') Tj ET Q', $this->TextColor, $tmpfont['i'], $this->FontSizePt, $fx, $fy);
13178  	 	 // merge options
13179  	 	 $opt = array_merge($popt, $opt);
13180  	 	 // set remaining annotation data
13181  	 	 $opt['Subtype'] = 'Widget';
13182  	 	 $opt['ft'] = 'Btn';
13183  	 	 $opt['t'] = $name;
13184  	 	 if (TCPDF_STATIC::empty_string($onvalue)) {
13185  	 	 	 $onvalue = 'Yes';
13186  	 	 }
13187  	 	 $opt['opt'] = array($onvalue);
13188  	 	 if ($checked) {
13189  	 	 	 $opt['v'] = array('/Yes');
13190  	 	 	 $opt['as'] = 'Yes';
13191  	 	 } else {
13192  	 	 	 $opt['v'] = array('/Off');
13193  	 	 	 $opt['as'] = 'Off';
13194  	 	 }
13195  	 	 $this->Annotation($x, $y, $w, $w, $name, $opt, 0);
13196  	 	 if ($this->rtl) {
13197  	 	 	 $this->x -= $w;
13198  	 	 } else {
13199  	 	 	 $this->x += $w;
13200  	 	 }
13201  	 }
13202  
13203  	 /**
13204  	  * Creates a button field
13205  	  * @param $name (string) field name
13206  	  * @param $w (int) width
13207  	  * @param $h (int) height
13208  	  * @param $caption (string) caption.
13209  	  * @param $action (mixed) 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.
13210  	  * @param $prop (array) javascript field properties. Possible values are described on official Javascript for Acrobat API reference.
13211  	  * @param $opt (array) annotation parameters. Possible values are described on official PDF32000_2008 reference.
13212  	  * @param $x (float) Abscissa of the upper-left corner of the rectangle
13213  	  * @param $y (float) Ordinate of the upper-left corner of the rectangle
13214  	  * @param $js (boolean) if true put the field using JavaScript (requires Acrobat Writer to be rendered).
13215  	  * @public
13216  	  * @author Nicola Asuni
13217  	  * @since 4.8.000 (2009-09-07)
13218  	  */
13219  	public function Button($name, $w, $h, $caption, $action, $prop=array(), $opt=array(), $x='', $y='', $js=false) {
13220  	 	 if ($x === '') {
13221  	 	 	 $x = $this->x;
13222  	 	 }
13223  	 	 if ($y === '') {
13224  	 	 	 $y = $this->y;
13225  	 	 }
13226  	 	 // check page for no-write regions and adapt page margins if necessary
13227  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
13228  	 	 if ($js) {
13229  	 	 	 $this->_addfield('button', $name, $this->x, $this->y, $w, $h, $prop);
13230  	 	 	 $this->javascript .= 'f'.$name.".buttonSetCaption('".addslashes($caption)."');\n";
13231  	 	 	 $this->javascript .= 'f'.$name.".setAction('MouseUp','".addslashes($action)."');\n";
13232  	 	 	 $this->javascript .= 'f'.$name.".highlight='push';\n";
13233  	 	 	 $this->javascript .= 'f'.$name.".print=false;\n";
13234  	 	 	 return;
13235  	 	 }
13236  	 	 // get default style
13237  	 	 $prop = array_merge($this->getFormDefaultProp(), $prop);
13238  	 	 $prop['Pushbutton'] = 'true';
13239  	 	 $prop['highlight'] = 'push';
13240  	 	 $prop['display'] = 'display.noPrint';
13241  	 	 // get annotation data
13242  	 	 $popt = TCPDF_STATIC::getAnnotOptFromJSProp($prop, $this->spot_colors, $this->rtl);
13243  	 	 $this->annotation_fonts[$this->CurrentFont['fontkey']] = $this->CurrentFont['i'];
13244  	 	 $fontstyle = sprintf('/F%d %F Tf %s', $this->CurrentFont['i'], $this->FontSizePt, $this->TextColor);
13245  	 	 $popt['da'] = $fontstyle;
13246  	 	 // build appearance stream
13247  	 	 $popt['ap'] = array();
13248  	 	 $popt['ap']['n'] = '/Tx BMC q '.$fontstyle.' ';
13249  	 	 $tmpid = $this->startTemplate($w, $h, false);
13250  	 	 $bw = (2 / $this->k); // border width
13251  	 	 $border = array(
13252  	 	 	 'L' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13253  	 	 	 'R' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)),
13254  	 	 	 'T' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(231)),
13255  	 	 	 'B' => array('width' => $bw, 'cap' => 'square', 'join' => 'miter', 'dash' => 0, 'color' => array(51)));
13256  	 	 $this->SetFillColor(204);
13257  	 	 $this->Cell($w, $h, $caption, $border, 0, 'C', true, '', 1, false, 'T', 'M');
13258  	 	 $this->endTemplate();
13259  	 	 --$this->n;
13260  	 	 $popt['ap']['n'] .= $this->xobjects[$tmpid]['outdata'];
13261  	 	 unset($this->xobjects[$tmpid]);
13262  	 	 $popt['ap']['n'] .= 'Q EMC';
13263  	 	 // set additional default options
13264  	 	 if (!isset($popt['mk'])) {
13265  	 	 	 $popt['mk'] = array();
13266  	 	 }
13267  	 	 $ann_obj_id = ($this->n + 1);
13268  	 	 if (!empty($action) AND !is_array($action)) {
13269  	 	 	 $ann_obj_id = ($this->n + 2);
13270  	 	 }
13271  	 	 $popt['mk']['ca'] = $this->_textstring($caption, $ann_obj_id);
13272  	 	 $popt['mk']['rc'] = $this->_textstring($caption, $ann_obj_id);
13273  	 	 $popt['mk']['ac'] = $this->_textstring($caption, $ann_obj_id);
13274  	 	 // merge options
13275  	 	 $opt = array_merge($popt, $opt);
13276  	 	 // set remaining annotation data
13277  	 	 $opt['Subtype'] = 'Widget';
13278  	 	 $opt['ft'] = 'Btn';
13279  	 	 $opt['t'] = $caption;
13280  	 	 $opt['v'] = $name;
13281  	 	 if (!empty($action)) {
13282  	 	 	 if (is_array($action)) {
13283  	 	 	 	 // form action options as on section 12.7.5 of PDF32000_2008.
13284  	 	 	 	 $opt['aa'] = '/D <<';
13285  	 	 	 	 $bmode = array('SubmitForm', 'ResetForm', 'ImportData');
13286  	 	 	 	 foreach ($action AS $key => $val) {
13287  	 	 	 	 	 if (($key == 'S') AND in_array($val, $bmode)) {
13288  	 	 	 	 	 	 $opt['aa'] .= ' /S /'.$val;
13289  	 	 	 	 	 } elseif (($key == 'F') AND (!empty($val))) {
13290  	 	 	 	 	 	 $opt['aa'] .= ' /F '.$this->_datastring($val, $ann_obj_id);
13291  	 	 	 	 	 } elseif (($key == 'Fields') AND is_array($val) AND !empty($val)) {
13292  	 	 	 	 	 	 $opt['aa'] .= ' /Fields [';
13293  	 	 	 	 	 	 foreach ($val AS $field) {
13294  	 	 	 	 	 	 	 $opt['aa'] .= ' '.$this->_textstring($field, $ann_obj_id);
13295  	 	 	 	 	 	 }
13296  	 	 	 	 	 	 $opt['aa'] .= ']';
13297  	 	 	 	 	 } elseif (($key == 'Flags')) {
13298  	 	 	 	 	 	 $ff = 0;
13299  	 	 	 	 	 	 if (is_array($val)) {
13300  	 	 	 	 	 	 	 foreach ($val AS $flag) {
13301  	 	 	 	 	 	 	 	 switch ($flag) {
13302  	 	 	 	 	 	 	 	 	 case 'Include/Exclude': {
13303  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 0;
13304  	 	 	 	 	 	 	 	 	 	 break;
13305  	 	 	 	 	 	 	 	 	 }
13306  	 	 	 	 	 	 	 	 	 case 'IncludeNoValueFields': {
13307  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 1;
13308  	 	 	 	 	 	 	 	 	 	 break;
13309  	 	 	 	 	 	 	 	 	 }
13310  	 	 	 	 	 	 	 	 	 case 'ExportFormat': {
13311  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 2;
13312  	 	 	 	 	 	 	 	 	 	 break;
13313  	 	 	 	 	 	 	 	 	 }
13314  	 	 	 	 	 	 	 	 	 case 'GetMethod': {
13315  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 3;
13316  	 	 	 	 	 	 	 	 	 	 break;
13317  	 	 	 	 	 	 	 	 	 }
13318  	 	 	 	 	 	 	 	 	 case 'SubmitCoordinates': {
13319  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 4;
13320  	 	 	 	 	 	 	 	 	 	 break;
13321  	 	 	 	 	 	 	 	 	 }
13322  	 	 	 	 	 	 	 	 	 case 'XFDF': {
13323  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 5;
13324  	 	 	 	 	 	 	 	 	 	 break;
13325  	 	 	 	 	 	 	 	 	 }
13326  	 	 	 	 	 	 	 	 	 case 'IncludeAppendSaves': {
13327  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 6;
13328  	 	 	 	 	 	 	 	 	 	 break;
13329  	 	 	 	 	 	 	 	 	 }
13330  	 	 	 	 	 	 	 	 	 case 'IncludeAnnotations': {
13331  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 7;
13332  	 	 	 	 	 	 	 	 	 	 break;
13333  	 	 	 	 	 	 	 	 	 }
13334  	 	 	 	 	 	 	 	 	 case 'SubmitPDF': {
13335  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 8;
13336  	 	 	 	 	 	 	 	 	 	 break;
13337  	 	 	 	 	 	 	 	 	 }
13338  	 	 	 	 	 	 	 	 	 case 'CanonicalFormat': {
13339  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 9;
13340  	 	 	 	 	 	 	 	 	 	 break;
13341  	 	 	 	 	 	 	 	 	 }
13342  	 	 	 	 	 	 	 	 	 case 'ExclNonUserAnnots': {
13343  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 10;
13344  	 	 	 	 	 	 	 	 	 	 break;
13345  	 	 	 	 	 	 	 	 	 }
13346  	 	 	 	 	 	 	 	 	 case 'ExclFKey': {
13347  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 11;
13348  	 	 	 	 	 	 	 	 	 	 break;
13349  	 	 	 	 	 	 	 	 	 }
13350  	 	 	 	 	 	 	 	 	 case 'EmbedForm': {
13351  	 	 	 	 	 	 	 	 	 	 $ff += 1 << 13;
13352  	 	 	 	 	 	 	 	 	 	 break;
13353  	 	 	 	 	 	 	 	 	 }
13354  	 	 	 	 	 	 	 	 }
13355  	 	 	 	 	 	 	 }
13356  	 	 	 	 	 	 } else {
13357  	 	 	 	 	 	 	 $ff = intval($val);
13358  	 	 	 	 	 	 }
13359  	 	 	 	 	 	 $opt['aa'] .= ' /Flags '.$ff;
13360  	 	 	 	 	 }
13361  	 	 	 	 }
13362  	 	 	 	 $opt['aa'] .= ' >>';
13363  	 	 	 } else {
13364  	 	 	 	 // Javascript action or raw action command
13365  	 	 	 	 $js_obj_id = $this->addJavascriptObject($action);
13366  	 	 	 	 $opt['aa'] = '/D '.$js_obj_id.' 0 R';
13367  	 	 	 }
13368  	 	 }
13369  	 	 $this->Annotation($x, $y, $w, $h, $name, $opt, 0);
13370  	 	 if ($this->rtl) {
13371  	 	 	 $this->x -= $w;
13372  	 	 } else {
13373  	 	 	 $this->x += $w;
13374  	 	 }
13375  	 }
13376  
13377  	 // --- END FORMS FIELDS ------------------------------------------------
13378  
13379  	 /**
13380  	  * Add certification signature (DocMDP or UR3)
13381  	  * You can set only one signature type
13382  	  * @protected
13383  	  * @author Nicola Asuni
13384  	  * @since 4.6.008 (2009-05-07)
13385  	  */
13386  	protected function _putsignature() {
13387  	 	 if ((!$this->sign) OR (!isset($this->signature_data['cert_type']))) {
13388  	 	 	 return;
13389  	 	 }
13390  	 	 $sigobjid = ($this->sig_obj_id + 1);
13391  	 	 $out = $this->_getobj($sigobjid)."\n";
13392  	 	 $out .= '<< /Type /Sig';
13393  	 	 $out .= ' /Filter /Adobe.PPKLite';
13394  	 	 $out .= ' /SubFilter /adbe.pkcs7.detached';
13395  	 	 $out .= ' '.TCPDF_STATIC::$byterange_string;
13396  	 	 $out .= ' /Contents<'.str_repeat('0', $this->signature_max_length).'>';
13397  	 	 if (empty($this->signature_data['approval']) OR ($this->signature_data['approval'] != 'A')) {
13398  	 	 	 $out .= ' /Reference ['; // array of signature reference dictionaries
13399  	 	 	 $out .= ' << /Type /SigRef';
13400  	 	 	 if ($this->signature_data['cert_type'] > 0) {
13401  	 	 	 	 $out .= ' /TransformMethod /DocMDP';
13402  	 	 	 	 $out .= ' /TransformParams <<';
13403  	 	 	 	 $out .= ' /Type /TransformParams';
13404  	 	 	 	 $out .= ' /P '.$this->signature_data['cert_type'];
13405  	 	 	 	 $out .= ' /V /1.2';
13406  	 	 	 } else {
13407  	 	 	 	 $out .= ' /TransformMethod /UR3';
13408  	 	 	 	 $out .= ' /TransformParams <<';
13409  	 	 	 	 $out .= ' /Type /TransformParams';
13410  	 	 	 	 $out .= ' /V /2.2';
13411  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->ur['document'])) {
13412  	 	 	 	 	 $out .= ' /Document['.$this->ur['document'].']';
13413  	 	 	 	 }
13414  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->ur['form'])) {
13415  	 	 	 	 	 $out .= ' /Form['.$this->ur['form'].']';
13416  	 	 	 	 }
13417  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->ur['signature'])) {
13418  	 	 	 	 	 $out .= ' /Signature['.$this->ur['signature'].']';
13419  	 	 	 	 }
13420  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->ur['annots'])) {
13421  	 	 	 	 	 $out .= ' /Annots['.$this->ur['annots'].']';
13422  	 	 	 	 }
13423  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->ur['ef'])) {
13424  	 	 	 	 	 $out .= ' /EF['.$this->ur['ef'].']';
13425  	 	 	 	 }
13426  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->ur['formex'])) {
13427  	 	 	 	 	 $out .= ' /FormEX['.$this->ur['formex'].']';
13428  	 	 	 	 }
13429  	 	 	 }
13430  	 	 	 $out .= ' >>'; // close TransformParams
13431  	 	 	 // optional digest data (values must be calculated and replaced later)
13432  	 	 	 //$out .= ' /Data ********** 0 R';
13433  	 	 	 //$out .= ' /DigestMethod/MD5';
13434  	 	 	 //$out .= ' /DigestLocation[********** 34]';
13435  	 	 	 //$out .= ' /DigestValue<********************************>';
13436  	 	 	 $out .= ' >>';
13437  	 	 	 $out .= ' ]'; // end of reference
13438  	 	 }
13439  	 	 if (isset($this->signature_data['info']['Name']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Name'])) {
13440  	 	 	 $out .= ' /Name '.$this->_textstring($this->signature_data['info']['Name'], $sigobjid);
13441  	 	 }
13442  	 	 if (isset($this->signature_data['info']['Location']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Location'])) {
13443  	 	 	 $out .= ' /Location '.$this->_textstring($this->signature_data['info']['Location'], $sigobjid);
13444  	 	 }
13445  	 	 if (isset($this->signature_data['info']['Reason']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['Reason'])) {
13446  	 	 	 $out .= ' /Reason '.$this->_textstring($this->signature_data['info']['Reason'], $sigobjid);
13447  	 	 }
13448  	 	 if (isset($this->signature_data['info']['ContactInfo']) AND !TCPDF_STATIC::empty_string($this->signature_data['info']['ContactInfo'])) {
13449  	 	 	 $out .= ' /ContactInfo '.$this->_textstring($this->signature_data['info']['ContactInfo'], $sigobjid);
13450  	 	 }
13451  	 	 $out .= ' /M '.$this->_datestring($sigobjid, $this->doc_modification_timestamp);
13452  	 	 $out .= ' >>';
13453  	 	 $out .= "\n".'endobj';
13454  	 	 $this->_out($out);
13455  	 }
13456  
13457  	 /**
13458  	  * Set User's Rights for PDF Reader
13459  	  * WARNING: This is experimental and currently do not work.
13460  	  * Check the PDF Reference 8.7.1 Transform Methods,
13461  	  * Table 8.105 Entries in the UR transform parameters dictionary
13462  	  * @param $enable (boolean) if true enable user's rights on PDF reader
13463  	  * @param $document (string) 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.
13464  	  * @param $annots (string) 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.
13465  	  * @param $form (string) Names specifying additional form-field-related usage rights for the document. Valid names are: /Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate
13466  	  * @param $signature (string) 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.
13467  	  * @param $ef (string) Names specifying additional usage rights for named embedded files in the document. Valid names are /Create/Delete/Modify/Import, which permit the user to perform the named operation on named embedded files
13468  	  Names specifying additional embedded-files-related usage rights for the document.
13469  	  * @param $formex (string) Names specifying additional form-field-related usage rights. The only valid name is BarcodePlaintext, which permits text form field data to be encoded as a plaintext two-dimensional barcode.
13470  	  * @public
13471  	  * @author Nicola Asuni
13472  	  * @since 2.9.000 (2008-03-26)
13473  	  */
13474  	public function setUserRights(
13475  	 	 	 $enable=true,
13476  	 	 	 $document='/FullSave',
13477  	 	 	 $annots='/Create/Delete/Modify/Copy/Import/Export',
13478  	 	 	 $form='/Add/Delete/FillIn/Import/Export/SubmitStandalone/SpawnTemplate',
13479  	 	 	 $signature='/Modify',
13480  	 	 	 $ef='/Create/Delete/Modify/Import',
13481  	 	 	 $formex='') {
13482  	 	 $this->ur['enabled'] = $enable;
13483  	 	 $this->ur['document'] = $document;
13484  	 	 $this->ur['annots'] = $annots;
13485  	 	 $this->ur['form'] = $form;
13486  	 	 $this->ur['signature'] = $signature;
13487  	 	 $this->ur['ef'] = $ef;
13488  	 	 $this->ur['formex'] = $formex;
13489  	 	 if (!$this->sign) {
13490  	 	 	 $this->setSignature('', '', '', '', 0, array());
13491  	 	 }
13492  	 }
13493  
13494  	 /**
13495  	  * Enable document signature (requires the OpenSSL Library).
13496  	  * The digital signature improve document authenticity and integrity and allows o enable extra features on Acrobat Reader.
13497  	  * To create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13498  	  * To export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13499  	  * To convert pfx certificate to pem: openssl pkcs12 -in tcpdf.pfx -out tcpdf.crt -nodes
13500  	  * @param $signing_cert (mixed) signing certificate (string or filename prefixed with 'file://')
13501  	  * @param $private_key (mixed) private key (string or filename prefixed with 'file://')
13502  	  * @param $private_key_password (string) password
13503  	  * @param $extracerts (string) 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.
13504  	  * @param $cert_type (int) 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.
13505  	  * @param $info (array) array of option information: Name, Location, Reason, ContactInfo.
13506  	  * @param $approval (string) Enable approval signature eg. for PDF incremental update
13507  	  * @public
13508  	  * @author Nicola Asuni
13509  	  * @since 4.6.005 (2009-04-24)
13510  	  */
13511  	public function setSignature($signing_cert='', $private_key='', $private_key_password='', $extracerts='', $cert_type=2, $info=array(), $approval='') {
13512  	 	 // to create self-signed signature: openssl req -x509 -nodes -days 365000 -newkey rsa:1024 -keyout tcpdf.crt -out tcpdf.crt
13513  	 	 // to export crt to p12: openssl pkcs12 -export -in tcpdf.crt -out tcpdf.p12
13514  	 	 // to convert pfx certificate to pem: openssl
13515  	 	 //     OpenSSL> pkcs12 -in <cert.pfx> -out <cert.crt> -nodes
13516  	 	 $this->sign = true;
13517  	 	 ++$this->n;
13518  	 	 $this->sig_obj_id = $this->n; // signature widget
13519  	 	 ++$this->n; // signature object ($this->sig_obj_id + 1)
13520  	 	 $this->signature_data = array();
13521  	 	 if (strlen($signing_cert) == 0) {
13522  	 	 	 $this->Error('Please provide a certificate file and password!');
13523  	 	 }
13524  	 	 if (strlen($private_key) == 0) {
13525  	 	 	 $private_key = $signing_cert;
13526  	 	 }
13527  	 	 $this->signature_data['signcert'] = $signing_cert;
13528  	 	 $this->signature_data['privkey'] = $private_key;
13529  	 	 $this->signature_data['password'] = $private_key_password;
13530  	 	 $this->signature_data['extracerts'] = $extracerts;
13531  	 	 $this->signature_data['cert_type'] = $cert_type;
13532  	 	 $this->signature_data['info'] = $info;
13533  	 	 $this->signature_data['approval'] = $approval;
13534  	 }
13535  
13536  	 /**
13537  	  * Set the digital signature appearance (a cliccable rectangle area to get signature properties)
13538  	  * @param $x (float) Abscissa of the upper-left corner.
13539  	  * @param $y (float) Ordinate of the upper-left corner.
13540  	  * @param $w (float) Width of the signature area.
13541  	  * @param $h (float) Height of the signature area.
13542  	  * @param $page (int) option page number (if < 0 the current page is used).
13543  	  * @param $name (string) Name of the signature.
13544  	  * @public
13545  	  * @author Nicola Asuni
13546  	  * @since 5.3.011 (2010-06-17)
13547  	  */
13548  	public function setSignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13549  	 	 $this->signature_appearance = $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13550  	 }
13551  
13552  	 /**
13553  	  * Add an empty digital signature appearance (a cliccable rectangle area to get signature properties)
13554  	  * @param $x (float) Abscissa of the upper-left corner.
13555  	  * @param $y (float) Ordinate of the upper-left corner.
13556  	  * @param $w (float) Width of the signature area.
13557  	  * @param $h (float) Height of the signature area.
13558  	  * @param $page (int) option page number (if < 0 the current page is used).
13559  	  * @param $name (string) Name of the signature.
13560  	  * @public
13561  	  * @author Nicola Asuni
13562  	  * @since 5.9.101 (2011-07-06)
13563  	  */
13564  	public function addEmptySignatureAppearance($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13565  	 	 ++$this->n;
13566  	 	 $this->empty_signature_appearance[] = array('objid' => $this->n) + $this->getSignatureAppearanceArray($x, $y, $w, $h, $page, $name);
13567  	 }
13568  
13569  	 /**
13570  	  * Get the array that defines the signature appearance (page and rectangle coordinates).
13571  	  * @param $x (float) Abscissa of the upper-left corner.
13572  	  * @param $y (float) Ordinate of the upper-left corner.
13573  	  * @param $w (float) Width of the signature area.
13574  	  * @param $h (float) Height of the signature area.
13575  	  * @param $page (int) option page number (if < 0 the current page is used).
13576  	  * @param $name (string) Name of the signature.
13577  	  * @return (array) Array defining page and rectangle coordinates of signature appearance.
13578  	  * @protected
13579  	  * @author Nicola Asuni
13580  	  * @since 5.9.101 (2011-07-06)
13581  	  */
13582  	protected function getSignatureAppearanceArray($x=0, $y=0, $w=0, $h=0, $page=-1, $name='') {
13583  	 	 $sigapp = array();
13584  	 	 if (($page < 1) OR ($page > $this->numpages)) {
13585  	 	 	 $sigapp['page'] = $this->page;
13586  	 	 } else {
13587  	 	 	 $sigapp['page'] = intval($page);
13588  	 	 }
13589  	 	 if (empty($name)) {
13590  	 	 	 $sigapp['name'] = 'Signature';
13591  	 	 } else {
13592  	 	 	 $sigapp['name'] = $name;
13593  	 	 }
13594  	 	 $a = $x * $this->k;
13595  	 	 $b = $this->pagedim[($sigapp['page'])]['h'] - (($y + $h) * $this->k);
13596  	 	 $c = $w * $this->k;
13597  	 	 $d = $h * $this->k;
13598  	 	 $sigapp['rect'] = sprintf('%F %F %F %F', $a, $b, ($a + $c), ($b + $d));
13599  	 	 return $sigapp;
13600  	 }
13601  
13602  	 /**
13603  	  * Enable document timestamping (requires the OpenSSL Library).
13604  	  * The trusted timestamping improve document security that means that no one should be able to change the document once it has been recorded.
13605  	  * Use with digital signature only!
13606  	  * @param $tsa_host (string) Time Stamping Authority (TSA) server (prefixed with 'https://')
13607  	  * @param $tsa_username (string) Specifies the username for TSA authorization (optional) OR specifies the TSA authorization PEM file (see: example_66.php, optional)
13608  	  * @param $tsa_password (string) Specifies the password for TSA authorization (optional)
13609  	  * @param $tsa_cert (string) Specifies the location of TSA certificate for authorization (optional for cURL)
13610  	  * @public
13611  	  * @author Richard Stockinger
13612  	  * @since 6.0.090 (2014-06-16)
13613  	  */
13614  	public function setTimeStamp($tsa_host='', $tsa_username='', $tsa_password='', $tsa_cert='') {
13615  	 	 $this->tsa_data = array();
13616  	 	 if (!function_exists('curl_init')) {
13617  	 	 	 $this->Error('Please enable cURL PHP extension!');
13618  	 	 }
13619  	 	 if (strlen($tsa_host) == 0) {
13620  	 	 	 $this->Error('Please specify the host of Time Stamping Authority (TSA)!');
13621  	 	 }
13622  	 	 $this->tsa_data['tsa_host'] = $tsa_host;
13623  	 	 if (is_file($tsa_username)) {
13624  	 	 	 $this->tsa_data['tsa_auth'] = $tsa_username;
13625  	 	 } else {
13626  	 	 	 $this->tsa_data['tsa_username'] = $tsa_username;
13627  	 	 }
13628  	 	 $this->tsa_data['tsa_password'] = $tsa_password;
13629  	 	 $this->tsa_data['tsa_cert'] = $tsa_cert;
13630  	 	 $this->tsa_timestamp = true;
13631  	 }
13632  
13633  	 /**
13634  	  * NOT YET IMPLEMENTED
13635  	  * Request TSA for a timestamp
13636  	  * @param $signature (string) Digital signature as binary string
13637  	  * @return (string) Timestamped digital signature
13638  	  * @protected
13639  	  * @author Richard Stockinger
13640  	  * @since 6.0.090 (2014-06-16)
13641  	  */
13642  	protected function applyTSA($signature) {
13643  	 	 if (!$this->tsa_timestamp) {
13644  	 	 	 return $signature;
13645  	 	 }
13646  	 	 //@TODO: implement this feature
13647  	 	 return $signature;
13648  	 }
13649  
13650  	 /**
13651  	  * Create a new page group.
13652  	  * NOTE: call this function before calling AddPage()
13653  	  * @param $page (int) starting group page (leave empty for next page).
13654  	  * @public
13655  	  * @since 3.0.000 (2008-03-27)
13656  	  */
13657  	public function startPageGroup($page='') {
13658  	 	 if (empty($page)) {
13659  	 	 	 $page = $this->page + 1;
13660  	 	 }
13661  	 	 $this->newpagegroup[$page] = sizeof($this->newpagegroup) + 1;
13662  	 }
13663  
13664  	 /**
13665  	  * Set the starting page number.
13666  	  * @param $num (int) Starting page number.
13667  	  * @since 5.9.093 (2011-06-16)
13668  	  * @public
13669  	  */
13670  	public function setStartingPageNumber($num=1) {
13671  	 	 $this->starting_page_number = max(0, intval($num));
13672  	 }
13673  
13674  	 /**
13675  	  * Returns the string alias used right align page numbers.
13676  	  * If the current font is unicode type, the returned string wil contain an additional open curly brace.
13677  	  * @return string
13678  	  * @since 5.9.099 (2011-06-27)
13679  	  * @public
13680  	  */
13681  	public function getAliasRightShift() {
13682  	 	 // calculate aproximatively the ratio between widths of aliases and replacements.
13683  	 	 $ref = '{'.TCPDF_STATIC::$alias_right_shift.'}{'.TCPDF_STATIC::$alias_tot_pages.'}{'.TCPDF_STATIC::$alias_num_page.'}';
13684  	 	 $rep = str_repeat(' ', $this->GetNumChars($ref));
13685  	 	 $wrep = $this->GetStringWidth($rep);
13686  	 	 if ($wrep > 0) {
13687  	 	 	 $wdiff = max(1, ($this->GetStringWidth($ref) / $wrep));
13688  	 	 } else {
13689  	 	 	 $wdiff = 1;
13690  	 	 }
13691  	 	 $sdiff = sprintf('%F', $wdiff);
13692  	 	 $alias = TCPDF_STATIC::$alias_right_shift.$sdiff.'}';
13693  	 	 if ($this->isUnicodeFont()) {
13694  	 	 	 $alias = '{'.$alias;
13695  	 	 }
13696  	 	 return $alias;
13697  	 }
13698  
13699  	 /**
13700  	  * Returns the string alias used for the total number of pages.
13701  	  * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13702  	  * This alias will be replaced by the total number of pages in the document.
13703  	  * @return string
13704  	  * @since 4.0.018 (2008-08-08)
13705  	  * @public
13706  	  */
13707  	public function getAliasNbPages() {
13708  	 	 if ($this->isUnicodeFont()) {
13709  	 	 	 return '{'.TCPDF_STATIC::$alias_tot_pages.'}';
13710  	 	 }
13711  	 	 return TCPDF_STATIC::$alias_tot_pages;
13712  	 }
13713  
13714  	 /**
13715  	  * Returns the string alias used for the page number.
13716  	  * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13717  	  * This alias will be replaced by the page number.
13718  	  * @return string
13719  	  * @since 4.5.000 (2009-01-02)
13720  	  * @public
13721  	  */
13722  	public function getAliasNumPage() {
13723  	 	 if ($this->isUnicodeFont()) {
13724  	 	 	 return '{'.TCPDF_STATIC::$alias_num_page.'}';
13725  	 	 }
13726  	 	 return TCPDF_STATIC::$alias_num_page;
13727  	 }
13728  
13729  	 /**
13730  	  * Return the alias for the total number of pages in the current page group.
13731  	  * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13732  	  * This alias will be replaced by the total number of pages in this group.
13733  	  * @return alias of the current page group
13734  	  * @public
13735  	  * @since 3.0.000 (2008-03-27)
13736  	  */
13737  	public function getPageGroupAlias() {
13738  	 	 if ($this->isUnicodeFont()) {
13739  	 	 	 return '{'.TCPDF_STATIC::$alias_group_tot_pages.'}';
13740  	 	 }
13741  	 	 return TCPDF_STATIC::$alias_group_tot_pages;
13742  	 }
13743  
13744  	 /**
13745  	  * Return the alias for the page number on the current page group.
13746  	  * If the current font is unicode type, the returned string is surrounded by additional curly braces.
13747  	  * This alias will be replaced by the page number (relative to the belonging group).
13748  	  * @return alias of the current page group
13749  	  * @public
13750  	  * @since 4.5.000 (2009-01-02)
13751  	  */
13752  	public function getPageNumGroupAlias() {
13753  	 	 if ($this->isUnicodeFont()) {
13754  	 	 	 return '{'.TCPDF_STATIC::$alias_group_num_page.'}';
13755  	 	 }
13756  	 	 return TCPDF_STATIC::$alias_group_num_page;
13757  	 }
13758  
13759  	 /**
13760  	  * Return the current page in the group.
13761  	  * @return current page in the group
13762  	  * @public
13763  	  * @since 3.0.000 (2008-03-27)
13764  	  */
13765  	public function getGroupPageNo() {
13766  	 	 return $this->pagegroups[$this->currpagegroup];
13767  	 }
13768  
13769  	 /**
13770  	  * Returns the current group page number formatted as a string.
13771  	  * @public
13772  	  * @since 4.3.003 (2008-11-18)
13773  	  * @see PaneNo(), formatPageNumber()
13774  	  */
13775  	public function getGroupPageNoFormatted() {
13776  	 	 return TCPDF_STATIC::formatPageNumber($this->getGroupPageNo());
13777  	 }
13778  
13779  	 /**
13780  	  * Returns the current page number formatted as a string.
13781  	  * @public
13782  	  * @since 4.2.005 (2008-11-06)
13783  	  * @see PaneNo(), formatPageNumber()
13784  	  */
13785  	public function PageNoFormatted() {
13786  	 	 return TCPDF_STATIC::formatPageNumber($this->PageNo());
13787  	 }
13788  
13789  	 /**
13790  	  * Put pdf layers.
13791  	  * @protected
13792  	  * @since 3.0.000 (2008-03-27)
13793  	  */
13794  	protected function _putocg() {
13795  	 	 if (empty($this->pdflayers)) {
13796  	 	 	 return;
13797  	 	 }
13798  	 	 foreach ($this->pdflayers as $key => $layer) {
13799  	 	 	  $this->pdflayers[$key]['objid'] = $this->_newobj();
13800  	 	 	  $out = '<< /Type /OCG';
13801  	 	 	  $out .= ' /Name '.$this->_textstring($layer['name'], $this->pdflayers[$key]['objid']);
13802  	 	 	  $out .= ' /Usage <<';
13803  	 	 	  if (isset($layer['print']) AND ($layer['print'] !== NULL)) {
13804  	 	 	 	 $out .= ' /Print <</PrintState /'.($layer['print']?'ON':'OFF').'>>';
13805  	 	 	  }
13806  	 	 	  $out .= ' /View <</ViewState /'.($layer['view']?'ON':'OFF').'>>';
13807  	 	 	  $out .= ' >> >>';
13808  	 	 	  $out .= "\n".'endobj';
13809  	 	 	  $this->_out($out);
13810  	 	 }
13811  	 }
13812  
13813  	 /**
13814  	  * Start a new pdf layer.
13815  	  * @param $name (string) Layer name (only a-z letters and numbers). Leave empty for automatic name.
13816  	  * @param $print (boolean|null) Set to TRUE to print this layer, FALSE to not print and NULL to not set this option
13817  	  * @param $view (boolean) Set to true to view this layer.
13818  	  * @param $lock (boolean) If true lock the layer
13819  	  * @public
13820  	  * @since 5.9.102 (2011-07-13)
13821  	  */
13822  	public function startLayer($name='', $print=true, $view=true, $lock=true) {
13823  	 	 if ($this->state != 2) {
13824  	 	 	 return;
13825  	 	 }
13826  	 	 $layer = sprintf('LYR%03d', (count($this->pdflayers) + 1));
13827  	 	 if (empty($name)) {
13828  	 	 	 $name = $layer;
13829  	 	 } else {
13830  	 	 	 $name = preg_replace('/[^a-zA-Z0-9_\-]/', '', $name);
13831  	 	 }
13832  	 	 $this->pdflayers[] = array('layer' => $layer, 'name' => $name, 'print' => $print, 'view' => $view, 'lock' => $lock);
13833  	 	 $this->openMarkedContent = true;
13834  	 	 $this->_out('/OC /'.$layer.' BDC');
13835  	 }
13836  
13837  	 /**
13838  	  * End the current PDF layer.
13839  	  * @public
13840  	  * @since 5.9.102 (2011-07-13)
13841  	  */
13842  	public function endLayer() {
13843  	 	 if ($this->state != 2) {
13844  	 	 	 return;
13845  	 	 }
13846  	 	 if ($this->openMarkedContent) {
13847  	 	 	 // close existing open marked-content layer
13848  	 	 	 $this->_out('EMC');
13849  	 	 	 $this->openMarkedContent = false;
13850  	 	 }
13851  	 }
13852  
13853  	 /**
13854  	  * Set the visibility of the successive elements.
13855  	  * This can be useful, for instance, to put a background
13856  	  * image or color that will show on screen but won't print.
13857  	  * @param $v (string) visibility mode. Legal values are: all, print, screen or view.
13858  	  * @public
13859  	  * @since 3.0.000 (2008-03-27)
13860  	  */
13861  	public function setVisibility($v) {
13862  	 	 if ($this->state != 2) {
13863  	 	 	 return;
13864  	 	 }
13865  	 	 $this->endLayer();
13866  	 	 switch($v) {
13867  	 	 	 case 'print': {
13868  	 	 	 	 $this->startLayer('Print', true, false);
13869  	 	 	 	 break;
13870  	 	 	 }
13871  	 	 	 case 'view':
13872  	 	 	 case 'screen': {
13873  	 	 	 	 $this->startLayer('View', false, true);
13874  	 	 	 	 break;
13875  	 	 	 }
13876  	 	 	 case 'all': {
13877  	 	 	 	 $this->_out('');
13878  	 	 	 	 break;
13879  	 	 	 }
13880  	 	 	 default: {
13881  	 	 	 	 $this->Error('Incorrect visibility: '.$v);
13882  	 	 	 	 break;
13883  	 	 	 }
13884  	 	 }
13885  	 }
13886  
13887  	 /**
13888  	  * Add transparency parameters to the current extgstate
13889  	  * @param $parms (array) parameters
13890  	  * @return the number of extgstates
13891  	  * @protected
13892  	  * @since 3.0.000 (2008-03-27)
13893  	  */
13894  	protected function addExtGState($parms) {
13895  	 	 if ($this->pdfa_mode) {
13896  	 	 	 // transparencies are not allowed in PDF/A mode
13897  	 	 	 return;
13898  	 	 }
13899  	 	 // check if this ExtGState already exist
13900  	 	 foreach ($this->extgstates as $i => $ext) {
13901  	 	 	 if ($ext['parms'] == $parms) {
13902  	 	 	 	 if ($this->inxobj) {
13903  	 	 	 	 	 // we are inside an XObject template
13904  	 	 	 	 	 $this->xobjects[$this->xobjid]['extgstates'][$i] = $ext;
13905  	 	 	 	 }
13906  	 	 	 	 // return reference to existing ExtGState
13907  	 	 	 	 return $i;
13908  	 	 	 }
13909  	 	 }
13910  	 	 $n = (count($this->extgstates) + 1);
13911  	 	 $this->extgstates[$n] = array('parms' => $parms);
13912  	 	 if ($this->inxobj) {
13913  	 	 	 // we are inside an XObject template
13914  	 	 	 $this->xobjects[$this->xobjid]['extgstates'][$n] = $this->extgstates[$n];
13915  	 	 }
13916  	 	 return $n;
13917  	 }
13918  
13919  	 /**
13920  	  * Add an extgstate
13921  	  * @param $gs (array) extgstate
13922  	  * @protected
13923  	  * @since 3.0.000 (2008-03-27)
13924  	  */
13925  	protected function setExtGState($gs) {
13926  	 	 if ($this->pdfa_mode OR ($this->state != 2)) {
13927  	 	 	 // transparency is not allowed in PDF/A mode
13928  	 	 	 return;
13929  	 	 }
13930  	 	 $this->_out(sprintf('/GS%d gs', $gs));
13931  	 }
13932  
13933  	 /**
13934  	  * Put extgstates for object transparency
13935  	  * @protected
13936  	  * @since 3.0.000 (2008-03-27)
13937  	  */
13938  	protected function _putextgstates() {
13939  	 	 foreach ($this->extgstates as $i => $ext) {
13940  	 	 	 $this->extgstates[$i]['n'] = $this->_newobj();
13941  	 	 	 $out = '<< /Type /ExtGState';
13942  	 	 	 foreach ($ext['parms'] as $k => $v) {
13943  	 	 	 	 if (is_float($v)) {
13944  	 	 	 	 	 $v = sprintf('%F', $v);
13945  	 	 	 	 } elseif ($v === true) {
13946  	 	 	 	 	 $v = 'true';
13947  	 	 	 	 } elseif ($v === false) {
13948  	 	 	 	 	 $v = 'false';
13949  	 	 	 	 }
13950  	 	 	 	 $out .= ' /'.$k.' '.$v;
13951  	 	 	 }
13952  	 	 	 $out .= ' >>';
13953  	 	 	 $out .= "\n".'endobj';
13954  	 	 	 $this->_out($out);
13955  	 	 }
13956  	 }
13957  
13958  	 /**
13959  	  * Set overprint mode for stroking (OP) and non-stroking (op) painting operations.
13960  	  * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13961  	  * @param $stroking (boolean) If true apply overprint for stroking operations.
13962  	  * @param $nonstroking (boolean) If true apply overprint for painting operations other than stroking.
13963  	  * @param $mode (integer) Overprint mode: (0 = each source colour component value replaces the value previously painted for the corresponding device colorant; 1 = a tint value of 0.0 for a source colour component shall leave the corresponding component of the previously painted colour unchanged).
13964  	  * @public
13965  	  * @since 5.9.152 (2012-03-23)
13966  	  */
13967  	public function setOverprint($stroking=true, $nonstroking='', $mode=0) {
13968  	 	 if ($this->state != 2) {
13969  	 	 	 return;
13970  	 	 }
13971  	 	 $stroking = $stroking ? true : false;
13972  	 	 if (TCPDF_STATIC::empty_string($nonstroking)) {
13973  	 	 	 // default value if not set
13974  	 	 	 $nonstroking = $stroking;
13975  	 	 } else {
13976  	 	 	 $nonstroking = $nonstroking ? true : false;
13977  	 	 }
13978  	 	 if (($mode != 0) AND ($mode != 1)) {
13979  	 	 	 $mode = 0;
13980  	 	 }
13981  	 	 $this->overprint = array('OP' => $stroking, 'op' => $nonstroking, 'OPM' => $mode);
13982  	 	 $gs = $this->addExtGState($this->overprint);
13983  	 	 $this->setExtGState($gs);
13984  	 }
13985  
13986  	 /**
13987  	  * Get the overprint mode array (OP, op, OPM).
13988  	  * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
13989  	  * @return array.
13990  	  * @public
13991  	  * @since 5.9.152 (2012-03-23)
13992  	  */
13993  	public function getOverprint() {
13994  	 	 return $this->overprint;
13995  	 }
13996  
13997  	 /**
13998  	  * Set alpha for stroking (CA) and non-stroking (ca) operations.
13999  	  * @param $stroking (float) Alpha value for stroking operations: real value from 0 (transparent) to 1 (opaque).
14000  	  * @param $bm (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity
14001  	  * @param $nonstroking (float) Alpha value for non-stroking operations: real value from 0 (transparent) to 1 (opaque).
14002  	  * @param $ais (boolean)
14003  	  * @public
14004  	  * @since 3.0.000 (2008-03-27)
14005  	  */
14006  	public function setAlpha($stroking=1, $bm='Normal', $nonstroking='', $ais=false) {
14007  	 	 if ($this->pdfa_mode) {
14008  	 	 	 // transparency is not allowed in PDF/A mode
14009  	 	 	 return;
14010  	 	 }
14011  	 	 $stroking = floatval($stroking);
14012  	 	 if (TCPDF_STATIC::empty_string($nonstroking)) {
14013  	 	 	 // default value if not set
14014  	 	 	 $nonstroking = $stroking;
14015  	 	 } else {
14016  	 	 	 $nonstroking = floatval($nonstroking);
14017  	 	 }
14018  	 	 if ($bm[0] == '/') {
14019  	 	 	 // remove trailing slash
14020  	 	 	 $bm = substr($bm, 1);
14021  	 	 }
14022  	 	 if (!in_array($bm, array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
14023  	 	 	 $bm = 'Normal';
14024  	 	 }
14025  	 	 $ais = $ais ? true : false;
14026  	 	 $this->alpha = array('CA' => $stroking, 'ca' => $nonstroking, 'BM' => '/'.$bm, 'AIS' => $ais);
14027  	 	 $gs = $this->addExtGState($this->alpha);
14028  	 	 $this->setExtGState($gs);
14029  	 }
14030  
14031  	 /**
14032  	  * Get the alpha mode array (CA, ca, BM, AIS).
14033  	  * (Check the "Entries in a Graphics State Parameter Dictionary" on PDF 32000-1:2008).
14034  	  * @return array.
14035  	  * @public
14036  	  * @since 5.9.152 (2012-03-23)
14037  	  */
14038  	public function getAlpha() {
14039  	 	 return $this->alpha;
14040  	 }
14041  
14042  	 /**
14043  	  * Set the default JPEG compression quality (1-100)
14044  	  * @param $quality (int) JPEG quality, integer between 1 and 100
14045  	  * @public
14046  	  * @since 3.0.000 (2008-03-27)
14047  	  */
14048  	public function setJPEGQuality($quality) {
14049  	 	 if (($quality < 1) OR ($quality > 100)) {
14050  	 	 	 $quality = 75;
14051  	 	 }
14052  	 	 $this->jpeg_quality = intval($quality);
14053  	 }
14054  
14055  	 /**
14056  	  * Set the default number of columns in a row for HTML tables.
14057  	  * @param $cols (int) number of columns
14058  	  * @public
14059  	  * @since 3.0.014 (2008-06-04)
14060  	  */
14061  	public function setDefaultTableColumns($cols=4) {
14062  	 	 $this->default_table_columns = intval($cols);
14063  	 }
14064  
14065  	 /**
14066  	  * Set the height of the cell (line height) respect the font height.
14067  	  * @param $h (int) cell proportion respect font height (typical value = 1.25).
14068  	  * @public
14069  	  * @since 3.0.014 (2008-06-04)
14070  	  */
14071  	public function setCellHeightRatio($h) {
14072  	 	 $this->cell_height_ratio = $h;
14073  	 }
14074  
14075  	 /**
14076  	  * return the height of cell repect font height.
14077  	  * @public
14078  	  * @since 4.0.012 (2008-07-24)
14079  	  */
14080  	public function getCellHeightRatio() {
14081  	 	 return $this->cell_height_ratio;
14082  	 }
14083  
14084  	 /**
14085  	  * Set the PDF version (check PDF reference for valid values).
14086  	  * @param $version (string) PDF document version.
14087  	  * @public
14088  	  * @since 3.1.000 (2008-06-09)
14089  	  */
14090  	public function setPDFVersion($version='1.7') {
14091  	 	 if ($this->pdfa_mode && $this->pdfa_version == 1 ) {
14092  	 	 	 // PDF/A mode
14093  	 	 	 $this->PDFVersion = '1.4';
14094  	 	 } else {
14095  	 	 	 $this->PDFVersion = $version;
14096  	 	 }
14097  	 }
14098  
14099  	 /**
14100  	  * Set the viewer preferences dictionary controlling the way the document is to be presented on the screen or in print.
14101  	  * (see Section 8.1 of PDF reference, "Viewer Preferences").
14102  	  * <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>
14103  	  * @param $preferences (array) array of options.
14104  	  * @author Nicola Asuni
14105  	  * @public
14106  	  * @since 3.1.000 (2008-06-09)
14107  	  */
14108  	public function setViewerPreferences($preferences) {
14109  	 	 $this->viewer_preferences = $preferences;
14110  	 }
14111  
14112  	 /**
14113  	  * Paints color transition registration bars
14114  	  * @param $x (float) abscissa of the top left corner of the rectangle.
14115  	  * @param $y (float) ordinate of the top left corner of the rectangle.
14116  	  * @param $w (float) width of the rectangle.
14117  	  * @param $h (float) height of the rectangle.
14118  	  * @param $transition (boolean) if true prints tcolor transitions to white.
14119  	  * @param $vertical (boolean) if true prints bar vertically.
14120  	  * @param $colors (string) colors to print separated by comma. Valid values are: A,W,R,G,B,C,M,Y,K,RGB,CMYK,ALL,ALLSPOT,<SPOT_COLOR_NAME>. Where: A = grayscale black, W = grayscale white, R = RGB red, G RGB green, B RGB blue, C = CMYK cyan, M = CMYK magenta, Y = CMYK yellow, K = CMYK key/black, RGB = RGB registration color, CMYK = CMYK registration color, ALL = Spot registration color, ALLSPOT = print all defined spot colors, <SPOT_COLOR_NAME> = name of the spot color to print.
14121  	  * @author Nicola Asuni
14122  	  * @since 4.9.000 (2010-03-26)
14123  	  * @public
14124  	  */
14125  	public function colorRegistrationBar($x, $y, $w, $h, $transition=true, $vertical=false, $colors='A,R,G,B,C,M,Y,K') {
14126  	 	 if (strpos($colors, 'ALLSPOT') !== false) {
14127  	 	 	 // expand spot colors
14128  	 	 	 $spot_colors = '';
14129  	 	 	 foreach ($this->spot_colors as $spot_color_name => $v) {
14130  	 	 	 	 $spot_colors .= ','.$spot_color_name;
14131  	 	 	 }
14132  	 	 	 if (!empty($spot_colors)) {
14133  	 	 	 	 $spot_colors = substr($spot_colors, 1);
14134  	 	 	 	 $colors = str_replace('ALLSPOT', $spot_colors, $colors);
14135  	 	 	 } else {
14136  	 	 	 	 $colors = str_replace('ALLSPOT', 'NONE', $colors);
14137  	 	 	 }
14138  	 	 }
14139  	 	 $bars = explode(',', $colors);
14140  	 	 $numbars = count($bars); // number of bars to print
14141  	 	 if ($numbars <= 0) {
14142  	 	 	 return;
14143  	 	 }
14144  	 	 // set bar measures
14145  	 	 if ($vertical) {
14146  	 	 	 $coords = array(0, 0, 0, 1);
14147  	 	 	 $wb = $w / $numbars; // bar width
14148  	 	 	 $hb = $h; // bar height
14149  	 	 	 $xd = $wb; // delta x
14150  	 	 	 $yd = 0; // delta y
14151  	 	 } else {
14152  	 	 	 $coords = array(1, 0, 0, 0);
14153  	 	 	 $wb = $w; // bar width
14154  	 	 	 $hb = $h / $numbars; // bar height
14155  	 	 	 $xd = 0; // delta x
14156  	 	 	 $yd = $hb; // delta y
14157  	 	 }
14158  	 	 $xb = $x;
14159  	 	 $yb = $y;
14160  	 	 foreach ($bars as $col) {
14161  	 	 	 switch ($col) {
14162  	 	 	 	 // set transition colors
14163  	 	 	 	 case 'A': { // BLACK (GRAYSCALE)
14164  	 	 	 	 	 $col_a = array(255);
14165  	 	 	 	 	 $col_b = array(0);
14166  	 	 	 	 	 break;
14167  	 	 	 	 }
14168  	 	 	 	 case 'W': { // WHITE (GRAYSCALE)
14169  	 	 	 	 	 $col_a = array(0);
14170  	 	 	 	 	 $col_b = array(255);
14171  	 	 	 	 	 break;
14172  	 	 	 	 }
14173  	 	 	 	 case 'R': { // RED (RGB)
14174  	 	 	 	 	 $col_a = array(255,255,255);
14175  	 	 	 	 	 $col_b = array(255,0,0);
14176  	 	 	 	 	 break;
14177  	 	 	 	 }
14178  	 	 	 	 case 'G': { // GREEN (RGB)
14179  	 	 	 	 	 $col_a = array(255,255,255);
14180  	 	 	 	 	 $col_b = array(0,255,0);
14181  	 	 	 	 	 break;
14182  	 	 	 	 }
14183  	 	 	 	 case 'B': { // BLUE (RGB)
14184  	 	 	 	 	 $col_a = array(255,255,255);
14185  	 	 	 	 	 $col_b = array(0,0,255);
14186  	 	 	 	 	 break;
14187  	 	 	 	 }
14188  	 	 	 	 case 'C': { // CYAN (CMYK)
14189  	 	 	 	 	 $col_a = array(0,0,0,0);
14190  	 	 	 	 	 $col_b = array(100,0,0,0);
14191  	 	 	 	 	 break;
14192  	 	 	 	 }
14193  	 	 	 	 case 'M': { // MAGENTA (CMYK)
14194  	 	 	 	 	 $col_a = array(0,0,0,0);
14195  	 	 	 	 	 $col_b = array(0,100,0,0);
14196  	 	 	 	 	 break;
14197  	 	 	 	 }
14198  	 	 	 	 case 'Y': { // YELLOW (CMYK)
14199  	 	 	 	 	 $col_a = array(0,0,0,0);
14200  	 	 	 	 	 $col_b = array(0,0,100,0);
14201  	 	 	 	 	 break;
14202  	 	 	 	 }
14203  	 	 	 	 case 'K': { // KEY - BLACK (CMYK)
14204  	 	 	 	 	 $col_a = array(0,0,0,0);
14205  	 	 	 	 	 $col_b = array(0,0,0,100);
14206  	 	 	 	 	 break;
14207  	 	 	 	 }
14208  	 	 	 	 case 'RGB': { // BLACK REGISTRATION (RGB)
14209  	 	 	 	 	 $col_a = array(255,255,255);
14210  	 	 	 	 	 $col_b = array(0,0,0);
14211  	 	 	 	 	 break;
14212  	 	 	 	 }
14213  	 	 	 	 case 'CMYK': { // BLACK REGISTRATION (CMYK)
14214  	 	 	 	 	 $col_a = array(0,0,0,0);
14215  	 	 	 	 	 $col_b = array(100,100,100,100);
14216  	 	 	 	 	 break;
14217  	 	 	 	 }
14218  	 	 	 	 case 'ALL': { // SPOT COLOR REGISTRATION
14219  	 	 	 	 	 $col_a = array(0,0,0,0,'None');
14220  	 	 	 	 	 $col_b = array(100,100,100,100,'All');
14221  	 	 	 	 	 break;
14222  	 	 	 	 }
14223  	 	 	 	 case 'NONE': { // SKIP THIS COLOR
14224  	 	 	 	 	 $col_a = array(0,0,0,0,'None');
14225  	 	 	 	 	 $col_b = array(0,0,0,0,'None');
14226  	 	 	 	 	 break;
14227  	 	 	 	 }
14228  	 	 	 	 default: { // SPECIFIC SPOT COLOR NAME
14229  	 	 	 	 	 $col_a = array(0,0,0,0,'None');
14230  	 	 	 	 	 $col_b = TCPDF_COLORS::getSpotColor($col, $this->spot_colors);
14231  	 	 	 	 	 if ($col_b === false) {
14232  	 	 	 	 	 	 // in case of error defaults to the registration color
14233  	 	 	 	 	 	 $col_b = array(100,100,100,100,'All');
14234  	 	 	 	 	 }
14235  	 	 	 	 	 break;
14236  	 	 	 	 }
14237  	 	 	 }
14238  	 	 	 if ($col != 'NONE') {
14239  	 	 	 	 if ($transition) {
14240  	 	 	 	 	 // color gradient
14241  	 	 	 	 	 $this->LinearGradient($xb, $yb, $wb, $hb, $col_a, $col_b, $coords);
14242  	 	 	 	 } else {
14243  	 	 	 	 	 $this->SetFillColorArray($col_b);
14244  	 	 	 	 	 // colored rectangle
14245  	 	 	 	 	 $this->Rect($xb, $yb, $wb, $hb, 'F', array());
14246  	 	 	 	 }
14247  	 	 	 	 $xb += $xd;
14248  	 	 	 	 $yb += $yd;
14249  	 	 	 }
14250  	 	 }
14251  	 }
14252  
14253  	 /**
14254  	  * Paints crop marks.
14255  	  * @param $x (float) abscissa of the crop mark center.
14256  	  * @param $y (float) ordinate of the crop mark center.
14257  	  * @param $w (float) width of the crop mark.
14258  	  * @param $h (float) height of the crop mark.
14259  	  * @param $type (string) type of crop mark, one symbol per type separated by comma: T = TOP, F = BOTTOM, L = LEFT, R = RIGHT, TL = A = TOP-LEFT, TR = B = TOP-RIGHT, BL = C = BOTTOM-LEFT, BR = D = BOTTOM-RIGHT.
14260  	  * @param $color (array) crop mark color (default spot registration color).
14261  	  * @author Nicola Asuni
14262  	  * @since 4.9.000 (2010-03-26)
14263  	  * @public
14264  	  */
14265  	public function cropMark($x, $y, $w, $h, $type='T,R,B,L', $color=array(100,100,100,100,'All')) {
14266  	 	 $this->SetLineStyle(array('width' => (0.5 / $this->k), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $color));
14267  	 	 $type = strtoupper($type);
14268  	 	 $type = preg_replace('/[^A-Z\-\,]*/', '', $type);
14269  	 	 // split type in single components
14270  	 	 $type = str_replace('-', ',', $type);
14271  	 	 $type = str_replace('TL', 'T,L', $type);
14272  	 	 $type = str_replace('TR', 'T,R', $type);
14273  	 	 $type = str_replace('BL', 'F,L', $type);
14274  	 	 $type = str_replace('BR', 'F,R', $type);
14275  	 	 $type = str_replace('A', 'T,L', $type);
14276  	 	 $type = str_replace('B', 'T,R', $type);
14277  	 	 $type = str_replace('T,RO', 'BO', $type);
14278  	 	 $type = str_replace('C', 'F,L', $type);
14279  	 	 $type = str_replace('D', 'F,R', $type);
14280  	 	 $crops = explode(',', strtoupper($type));
14281  	 	 // remove duplicates
14282  	 	 $crops = array_unique($crops);
14283  	 	 $dw = ($w / 4); // horizontal space to leave before the intersection point
14284  	 	 $dh = ($h / 4); // vertical space to leave before the intersection point
14285  	 	 foreach ($crops as $crop) {
14286  	 	 	 switch ($crop) {
14287  	 	 	 	 case 'T':
14288  	 	 	 	 case 'TOP': {
14289  	 	 	 	 	 $x1 = $x;
14290  	 	 	 	 	 $y1 = ($y - $h);
14291  	 	 	 	 	 $x2 = $x;
14292  	 	 	 	 	 $y2 = ($y - $dh);
14293  	 	 	 	 	 break;
14294  	 	 	 	 }
14295  	 	 	 	 case 'F':
14296  	 	 	 	 case 'BOTTOM': {
14297  	 	 	 	 	 $x1 = $x;
14298  	 	 	 	 	 $y1 = ($y + $dh);
14299  	 	 	 	 	 $x2 = $x;
14300  	 	 	 	 	 $y2 = ($y + $h);
14301  	 	 	 	 	 break;
14302  	 	 	 	 }
14303  	 	 	 	 case 'L':
14304  	 	 	 	 case 'LEFT': {
14305  	 	 	 	 	 $x1 = ($x - $w);
14306  	 	 	 	 	 $y1 = $y;
14307  	 	 	 	 	 $x2 = ($x - $dw);
14308  	 	 	 	 	 $y2 = $y;
14309  	 	 	 	 	 break;
14310  	 	 	 	 }
14311  	 	 	 	 case 'R':
14312  	 	 	 	 case 'RIGHT': {
14313  	 	 	 	 	 $x1 = ($x + $dw);
14314  	 	 	 	 	 $y1 = $y;
14315  	 	 	 	 	 $x2 = ($x + $w);
14316  	 	 	 	 	 $y2 = $y;
14317  	 	 	 	 	 break;
14318  	 	 	 	 }
14319  	 	 	 }
14320  	 	 	 $this->Line($x1, $y1, $x2, $y2);
14321  	 	 }
14322  	 }
14323  
14324  	 /**
14325  	  * Paints a registration mark
14326  	  * @param $x (float) abscissa of the registration mark center.
14327  	  * @param $y (float) ordinate of the registration mark center.
14328  	  * @param $r (float) radius of the crop mark.
14329  	  * @param $double (boolean) if true print two concentric crop marks.
14330  	  * @param $cola (array) crop mark color (default spot registration color 'All').
14331  	  * @param $colb (array) second crop mark color (default spot registration color 'None').
14332  	  * @author Nicola Asuni
14333  	  * @since 4.9.000 (2010-03-26)
14334  	  * @public
14335  	  */
14336  	public function registrationMark($x, $y, $r, $double=false, $cola=array(100,100,100,100,'All'), $colb=array(0,0,0,0,'None')) {
14337  	 	 $line_style = array('width' => max((0.5 / $this->k),($r / 30)), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => $cola);
14338  	 	 $this->SetFillColorArray($cola);
14339  	 	 $this->PieSector($x, $y, $r, 90, 180, 'F');
14340  	 	 $this->PieSector($x, $y, $r, 270, 360, 'F');
14341  	 	 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14342  	 	 if ($double) {
14343  	 	 	 $ri = $r * 0.5;
14344  	 	 	 $this->SetFillColorArray($colb);
14345  	 	 	 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14346  	 	 	 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14347  	 	 	 $this->SetFillColorArray($cola);
14348  	 	 	 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14349  	 	 	 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14350  	 	 	 $this->Circle($x, $y, $ri, 0, 360, 'C', $line_style, array(), 8);
14351  	 	 }
14352  	 }
14353  
14354  	 /**
14355  	  * Paints a CMYK registration mark
14356  	  * @param $x (float) abscissa of the registration mark center.
14357  	  * @param $y (float) ordinate of the registration mark center.
14358  	  * @param $r (float) radius of the crop mark.
14359  	  * @author Nicola Asuni
14360  	  * @since 6.0.038 (2013-09-30)
14361  	  * @public
14362  	  */
14363  	public function registrationMarkCMYK($x, $y, $r) {
14364  	 	 // line width
14365  	 	 $lw = max((0.5 / $this->k),($r / 8));
14366  	 	 // internal radius
14367  	 	 $ri = ($r * 0.6);
14368  	 	 // external radius
14369  	 	 $re = ($r * 1.3);
14370  	 	 // Cyan
14371  	 	 $this->SetFillColorArray(array(100,0,0,0));
14372  	 	 $this->PieSector($x, $y, $ri, 270, 360, 'F');
14373  	 	 // Magenta
14374  	 	 $this->SetFillColorArray(array(0,100,0,0));
14375  	 	 $this->PieSector($x, $y, $ri, 0, 90, 'F');
14376  	 	 // Yellow
14377  	 	 $this->SetFillColorArray(array(0,0,100,0));
14378  	 	 $this->PieSector($x, $y, $ri, 90, 180, 'F');
14379  	 	 // Key - black
14380  	 	 $this->SetFillColorArray(array(0,0,0,100));
14381  	 	 $this->PieSector($x, $y, $ri, 180, 270, 'F');
14382  	 	 // registration color
14383  	 	 $line_style = array('width' => $lw, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(100,100,100,100,'All'));
14384  	 	 $this->SetFillColorArray(array(100,100,100,100,'All'));
14385  	 	 // external circle
14386  	 	 $this->Circle($x, $y, $r, 0, 360, 'C', $line_style, array(), 8);
14387  	 	 // cross lines
14388  	 	 $this->Line($x, ($y - $re), $x, ($y - $ri));
14389  	 	 $this->Line($x, ($y + $ri), $x, ($y + $re));
14390  	 	 $this->Line(($x - $re), $y, ($x - $ri), $y);
14391  	 	 $this->Line(($x + $ri), $y, ($x + $re), $y);
14392  	 }
14393  
14394  	 /**
14395  	  * Paints a linear colour gradient.
14396  	  * @param $x (float) abscissa of the top left corner of the rectangle.
14397  	  * @param $y (float) ordinate of the top left corner of the rectangle.
14398  	  * @param $w (float) width of the rectangle.
14399  	  * @param $h (float) height of the rectangle.
14400  	  * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14401  	  * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14402  	  * @param $coords (array) 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).
14403  	  * @author Andreas W\FCrmser, Nicola Asuni
14404  	  * @since 3.1.000 (2008-06-09)
14405  	  * @public
14406  	  */
14407  	public function LinearGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0,0,1,0)) {
14408  	 	 $this->Clip($x, $y, $w, $h);
14409  	 	 $this->Gradient(2, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14410  	 }
14411  
14412  	 /**
14413  	  * Paints a radial colour gradient.
14414  	  * @param $x (float) abscissa of the top left corner of the rectangle.
14415  	  * @param $y (float) ordinate of the top left corner of the rectangle.
14416  	  * @param $w (float) width of the rectangle.
14417  	  * @param $h (float) height of the rectangle.
14418  	  * @param $col1 (array) first color (Grayscale, RGB or CMYK components).
14419  	  * @param $col2 (array) second color (Grayscale, RGB or CMYK components).
14420  	  * @param $coords (array) 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.
14421  	  * @author Andreas W\FCrmser, Nicola Asuni
14422  	  * @since 3.1.000 (2008-06-09)
14423  	  * @public
14424  	  */
14425  	public function RadialGradient($x, $y, $w, $h, $col1=array(), $col2=array(), $coords=array(0.5,0.5,0.5,0.5,1)) {
14426  	 	 $this->Clip($x, $y, $w, $h);
14427  	 	 $this->Gradient(3, $coords, array(array('color' => $col1, 'offset' => 0, 'exponent' => 1), array('color' => $col2, 'offset' => 1, 'exponent' => 1)), array(), false);
14428  	 }
14429  
14430  	 /**
14431  	  * Paints a coons patch mesh.
14432  	  * @param $x (float) abscissa of the top left corner of the rectangle.
14433  	  * @param $y (float) ordinate of the top left corner of the rectangle.
14434  	  * @param $w (float) width of the rectangle.
14435  	  * @param $h (float) height of the rectangle.
14436  	  * @param $col1 (array) first color (lower left corner) (RGB components).
14437  	  * @param $col2 (array) second color (lower right corner) (RGB components).
14438  	  * @param $col3 (array) third color (upper right corner) (RGB components).
14439  	  * @param $col4 (array) fourth color (upper left corner) (RGB components).
14440  	  * @param $coords (array) <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>
14441  	  * @param $coords_min (array) minimum value used by the coordinates. If a coordinate's value is smaller than this it will be cut to coords_min. default: 0
14442  	  * @param $coords_max (array) maximum value used by the coordinates. If a coordinate's value is greater than this it will be cut to coords_max. default: 1
14443  	  * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14444  	  * @author Andreas W\FCrmser, Nicola Asuni
14445  	  * @since 3.1.000 (2008-06-09)
14446  	  * @public
14447  	  */
14448  	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) {
14449  	 	 if ($this->pdfa_mode OR ($this->state != 2)) {
14450  	 	 	 return;
14451  	 	 }
14452  	 	 $this->Clip($x, $y, $w, $h);
14453  	 	 $n = count($this->gradients) + 1;
14454  	 	 $this->gradients[$n] = array();
14455  	 	 $this->gradients[$n]['type'] = 6; //coons patch mesh
14456  	 	 $this->gradients[$n]['coords'] = array();
14457  	 	 $this->gradients[$n]['antialias'] = $antialias;
14458  	 	 $this->gradients[$n]['colors'] = array();
14459  	 	 $this->gradients[$n]['transparency'] = false;
14460  	 	 //check the coords array if it is the simple array or the multi patch array
14461  	 	 if (!isset($coords[0]['f'])) {
14462  	 	 	 //simple array -> convert to multi patch array
14463  	 	 	 if (!isset($col1[1])) {
14464  	 	 	 	 $col1[1] = $col1[2] = $col1[0];
14465  	 	 	 }
14466  	 	 	 if (!isset($col2[1])) {
14467  	 	 	 	 $col2[1] = $col2[2] = $col2[0];
14468  	 	 	 }
14469  	 	 	 if (!isset($col3[1])) {
14470  	 	 	 	 $col3[1] = $col3[2] = $col3[0];
14471  	 	 	 }
14472  	 	 	 if (!isset($col4[1])) {
14473  	 	 	 	 $col4[1] = $col4[2] = $col4[0];
14474  	 	 	 }
14475  	 	 	 $patch_array[0]['f'] = 0;
14476  	 	 	 $patch_array[0]['points'] = $coords;
14477  	 	 	 $patch_array[0]['colors'][0]['r'] = $col1[0];
14478  	 	 	 $patch_array[0]['colors'][0]['g'] = $col1[1];
14479  	 	 	 $patch_array[0]['colors'][0]['b'] = $col1[2];
14480  	 	 	 $patch_array[0]['colors'][1]['r'] = $col2[0];
14481  	 	 	 $patch_array[0]['colors'][1]['g'] = $col2[1];
14482  	 	 	 $patch_array[0]['colors'][1]['b'] = $col2[2];
14483  	 	 	 $patch_array[0]['colors'][2]['r'] = $col3[0];
14484  	 	 	 $patch_array[0]['colors'][2]['g'] = $col3[1];
14485  	 	 	 $patch_array[0]['colors'][2]['b'] = $col3[2];
14486  	 	 	 $patch_array[0]['colors'][3]['r'] = $col4[0];
14487  	 	 	 $patch_array[0]['colors'][3]['g'] = $col4[1];
14488  	 	 	 $patch_array[0]['colors'][3]['b'] = $col4[2];
14489  	 	 } else {
14490  	 	 	 //multi patch array
14491  	 	 	 $patch_array = $coords;
14492  	 	 }
14493  	 	 $bpcd = 65535; //16 bits per coordinate
14494  	 	 //build the data stream
14495  	 	 $this->gradients[$n]['stream'] = '';
14496  	 	 $count_patch = count($patch_array);
14497  	 	 for ($i=0; $i < $count_patch; ++$i) {
14498  	 	 	 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['f']); //start with the edge flag as 8 bit
14499  	 	 	 $count_points = count($patch_array[$i]['points']);
14500  	 	 	 for ($j=0; $j < $count_points; ++$j) {
14501  	 	 	 	 //each point as 16 bit
14502  	 	 	 	 $patch_array[$i]['points'][$j] = (($patch_array[$i]['points'][$j] - $coords_min) / ($coords_max - $coords_min)) * $bpcd;
14503  	 	 	 	 if ($patch_array[$i]['points'][$j] < 0) {
14504  	 	 	 	 	 $patch_array[$i]['points'][$j] = 0;
14505  	 	 	 	 }
14506  	 	 	 	 if ($patch_array[$i]['points'][$j] > $bpcd) {
14507  	 	 	 	 	 $patch_array[$i]['points'][$j] = $bpcd;
14508  	 	 	 	 }
14509  	 	 	 	 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] / 256));
14510  	 	 	 	 $this->gradients[$n]['stream'] .= chr(floor($patch_array[$i]['points'][$j] % 256));
14511  	 	 	 }
14512  	 	 	 $count_cols = count($patch_array[$i]['colors']);
14513  	 	 	 for ($j=0; $j < $count_cols; ++$j) {
14514  	 	 	 	 //each color component as 8 bit
14515  	 	 	 	 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['r']);
14516  	 	 	 	 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['g']);
14517  	 	 	 	 $this->gradients[$n]['stream'] .= chr($patch_array[$i]['colors'][$j]['b']);
14518  	 	 	 }
14519  	 	 }
14520  	 	 //paint the gradient
14521  	 	 $this->_out('/Sh'.$n.' sh');
14522  	 	 //restore previous Graphic State
14523  	 	 $this->_outRestoreGraphicsState();
14524  	 	 if ($this->inxobj) {
14525  	 	 	 // we are inside an XObject template
14526  	 	 	 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14527  	 	 }
14528  	 }
14529  
14530  	 /**
14531  	  * Set a rectangular clipping area.
14532  	  * @param $x (float) abscissa of the top left corner of the rectangle (or top right corner for RTL mode).
14533  	  * @param $y (float) ordinate of the top left corner of the rectangle.
14534  	  * @param $w (float) width of the rectangle.
14535  	  * @param $h (float) height of the rectangle.
14536  	  * @author Andreas W\FCrmser, Nicola Asuni
14537  	  * @since 3.1.000 (2008-06-09)
14538  	  * @protected
14539  	  */
14540  	protected function Clip($x, $y, $w, $h) {
14541  	 	 if ($this->state != 2) {
14542  	 	 	  return;
14543  	 	 }
14544  	 	 if ($this->rtl) {
14545  	 	 	 $x = $this->w - $x - $w;
14546  	 	 }
14547  	 	 //save current Graphic State
14548  	 	 $s = 'q';
14549  	 	 //set clipping area
14550  	 	 $s .= sprintf(' %F %F %F %F re W n', $x*$this->k, ($this->h-$y)*$this->k, $w*$this->k, -$h*$this->k);
14551  	 	 //set up transformation matrix for gradient
14552  	 	 $s .= sprintf(' %F 0 0 %F %F %F cm', $w*$this->k, $h*$this->k, $x*$this->k, ($this->h-($y+$h))*$this->k);
14553  	 	 $this->_out($s);
14554  	 }
14555  
14556  	 /**
14557  	  * Output gradient.
14558  	  * @param $type (int) 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)
14559  	  * @param $coords (array) array of coordinates.
14560  	  * @param $stops (array) 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).
14561  	  * @param $background (array) An array of colour components appropriate to the colour space, specifying a single background colour value.
14562  	  * @param $antialias (boolean) A flag indicating whether to filter the shading function to prevent aliasing artifacts.
14563  	  * @author Nicola Asuni
14564  	  * @since 3.1.000 (2008-06-09)
14565  	  * @public
14566  	  */
14567  	public function Gradient($type, $coords, $stops, $background=array(), $antialias=false) {
14568  	 	 if ($this->pdfa_mode OR ($this->state != 2)) {
14569  	 	 	 return;
14570  	 	 }
14571  	 	 $n = count($this->gradients) + 1;
14572  	 	 $this->gradients[$n] = array();
14573  	 	 $this->gradients[$n]['type'] = $type;
14574  	 	 $this->gradients[$n]['coords'] = $coords;
14575  	 	 $this->gradients[$n]['antialias'] = $antialias;
14576  	 	 $this->gradients[$n]['colors'] = array();
14577  	 	 $this->gradients[$n]['transparency'] = false;
14578  	 	 // color space
14579  	 	 $numcolspace = count($stops[0]['color']);
14580  	 	 $bcolor = array_values($background);
14581  	 	 switch($numcolspace) {
14582  	 	 	 case 5:   // SPOT
14583  	 	 	 case 4: { // CMYK
14584  	 	 	 	 $this->gradients[$n]['colspace'] = 'DeviceCMYK';
14585  	 	 	 	 if (!empty($background)) {
14586  	 	 	 	 	 $this->gradients[$n]['background'] = sprintf('%F %F %F %F', $bcolor[0]/100, $bcolor[1]/100, $bcolor[2]/100, $bcolor[3]/100);
14587  	 	 	 	 }
14588  	 	 	 	 break;
14589  	 	 	 }
14590  	 	 	 case 3: { // RGB
14591  	 	 	 	 $this->gradients[$n]['colspace'] = 'DeviceRGB';
14592  	 	 	 	 if (!empty($background)) {
14593  	 	 	 	 	 $this->gradients[$n]['background'] = sprintf('%F %F %F', $bcolor[0]/255, $bcolor[1]/255, $bcolor[2]/255);
14594  	 	 	 	 }
14595  	 	 	 	 break;
14596  	 	 	 }
14597  	 	 	 case 1: { // GRAY SCALE
14598  	 	 	 	 $this->gradients[$n]['colspace'] = 'DeviceGray';
14599  	 	 	 	 if (!empty($background)) {
14600  	 	 	 	 	 $this->gradients[$n]['background'] = sprintf('%F', $bcolor[0]/255);
14601  	 	 	 	 }
14602  	 	 	 	 break;
14603  	 	 	 }
14604  	 	 }
14605  	 	 $num_stops = count($stops);
14606  	 	 $last_stop_id = $num_stops - 1;
14607  	 	 foreach ($stops as $key => $stop) {
14608  	 	 	 $this->gradients[$n]['colors'][$key] = array();
14609  	 	 	 // offset represents a location along the gradient vector
14610  	 	 	 if (isset($stop['offset'])) {
14611  	 	 	 	 $this->gradients[$n]['colors'][$key]['offset'] = $stop['offset'];
14612  	 	 	 } else {
14613  	 	 	 	 if ($key == 0) {
14614  	 	 	 	 	 $this->gradients[$n]['colors'][$key]['offset'] = 0;
14615  	 	 	 	 } elseif ($key == $last_stop_id) {
14616  	 	 	 	 	 $this->gradients[$n]['colors'][$key]['offset'] = 1;
14617  	 	 	 	 } else {
14618  	 	 	 	 	 $offsetstep = (1 - $this->gradients[$n]['colors'][($key - 1)]['offset']) / ($num_stops - $key);
14619  	 	 	 	 	 $this->gradients[$n]['colors'][$key]['offset'] = $this->gradients[$n]['colors'][($key - 1)]['offset'] + $offsetstep;
14620  	 	 	 	 }
14621  	 	 	 }
14622  	 	 	 if (isset($stop['opacity'])) {
14623  	 	 	 	 $this->gradients[$n]['colors'][$key]['opacity'] = $stop['opacity'];
14624  	 	 	 	 if ((!$this->pdfa_mode) AND ($stop['opacity'] < 1)) {
14625  	 	 	 	 	 $this->gradients[$n]['transparency'] = true;
14626  	 	 	 	 }
14627  	 	 	 } else {
14628  	 	 	 	 $this->gradients[$n]['colors'][$key]['opacity'] = 1;
14629  	 	 	 }
14630  	 	 	 // exponent for the exponential interpolation function
14631  	 	 	 if (isset($stop['exponent'])) {
14632  	 	 	 	 $this->gradients[$n]['colors'][$key]['exponent'] = $stop['exponent'];
14633  	 	 	 } else {
14634  	 	 	 	 $this->gradients[$n]['colors'][$key]['exponent'] = 1;
14635  	 	 	 }
14636  	 	 	 // set colors
14637  	 	 	 $color = array_values($stop['color']);
14638  	 	 	 switch($numcolspace) {
14639  	 	 	 	 case 5:   // SPOT
14640  	 	 	 	 case 4: { // CMYK
14641  	 	 	 	 	 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F %F', $color[0]/100, $color[1]/100, $color[2]/100, $color[3]/100);
14642  	 	 	 	 	 break;
14643  	 	 	 	 }
14644  	 	 	 	 case 3: { // RGB
14645  	 	 	 	 	 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F %F %F', $color[0]/255, $color[1]/255, $color[2]/255);
14646  	 	 	 	 	 break;
14647  	 	 	 	 }
14648  	 	 	 	 case 1: { // GRAY SCALE
14649  	 	 	 	 	 $this->gradients[$n]['colors'][$key]['color'] = sprintf('%F', $color[0]/255);
14650  	 	 	 	 	 break;
14651  	 	 	 	 }
14652  	 	 	 }
14653  	 	 }
14654  	 	 if ($this->gradients[$n]['transparency']) {
14655  	 	 	 // paint luminosity gradient
14656  	 	 	 $this->_out('/TGS'.$n.' gs');
14657  	 	 }
14658  	 	 //paint the gradient
14659  	 	 $this->_out('/Sh'.$n.' sh');
14660  	 	 //restore previous Graphic State
14661  	 	 $this->_outRestoreGraphicsState();
14662  	 	 if ($this->inxobj) {
14663  	 	 	 // we are inside an XObject template
14664  	 	 	 $this->xobjects[$this->xobjid]['gradients'][$n] = $this->gradients[$n];
14665  	 	 }
14666  	 }
14667  
14668  	 /**
14669  	  * Output gradient shaders.
14670  	  * @author Nicola Asuni
14671  	  * @since 3.1.000 (2008-06-09)
14672  	  * @protected
14673  	  */
14674  	function _putshaders() {
14675  	 	 if ($this->pdfa_mode) {
14676  	 	 	 return;
14677  	 	 }
14678  	 	 $idt = count($this->gradients); //index for transparency gradients
14679  	 	 foreach ($this->gradients as $id => $grad) {
14680  	 	 	 if (($grad['type'] == 2) OR ($grad['type'] == 3)) {
14681  	 	 	 	 $fc = $this->_newobj();
14682  	 	 	 	 $out = '<<';
14683  	 	 	 	 $out .= ' /FunctionType 3';
14684  	 	 	 	 $out .= ' /Domain [0 1]';
14685  	 	 	 	 $functions = '';
14686  	 	 	 	 $bounds = '';
14687  	 	 	 	 $encode = '';
14688  	 	 	 	 $i = 1;
14689  	 	 	 	 $num_cols = count($grad['colors']);
14690  	 	 	 	 $lastcols = $num_cols - 1;
14691  	 	 	 	 for ($i = 1; $i < $num_cols; ++$i) {
14692  	 	 	 	 	 $functions .= ($fc + $i).' 0 R ';
14693  	 	 	 	 	 if ($i < $lastcols) {
14694  	 	 	 	 	 	 $bounds .= sprintf('%F ', $grad['colors'][$i]['offset']);
14695  	 	 	 	 	 }
14696  	 	 	 	 	 $encode .= '0 1 ';
14697  	 	 	 	 }
14698  	 	 	 	 $out .= ' /Functions ['.trim($functions).']';
14699  	 	 	 	 $out .= ' /Bounds ['.trim($bounds).']';
14700  	 	 	 	 $out .= ' /Encode ['.trim($encode).']';
14701  	 	 	 	 $out .= ' >>';
14702  	 	 	 	 $out .= "\n".'endobj';
14703  	 	 	 	 $this->_out($out);
14704  	 	 	 	 for ($i = 1; $i < $num_cols; ++$i) {
14705  	 	 	 	 	 $this->_newobj();
14706  	 	 	 	 	 $out = '<<';
14707  	 	 	 	 	 $out .= ' /FunctionType 2';
14708  	 	 	 	 	 $out .= ' /Domain [0 1]';
14709  	 	 	 	 	 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['color'].']';
14710  	 	 	 	 	 $out .= ' /C1 ['.$grad['colors'][$i]['color'].']';
14711  	 	 	 	 	 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14712  	 	 	 	 	 $out .= ' >>';
14713  	 	 	 	 	 $out .= "\n".'endobj';
14714  	 	 	 	 	 $this->_out($out);
14715  	 	 	 	 }
14716  	 	 	 	 // set transparency functions
14717  	 	 	 	 if ($grad['transparency']) {
14718  	 	 	 	 	 $ft = $this->_newobj();
14719  	 	 	 	 	 $out = '<<';
14720  	 	 	 	 	 $out .= ' /FunctionType 3';
14721  	 	 	 	 	 $out .= ' /Domain [0 1]';
14722  	 	 	 	 	 $functions = '';
14723  	 	 	 	 	 $i = 1;
14724  	 	 	 	 	 $num_cols = count($grad['colors']);
14725  	 	 	 	 	 for ($i = 1; $i < $num_cols; ++$i) {
14726  	 	 	 	 	 	 $functions .= ($ft + $i).' 0 R ';
14727  	 	 	 	 	 }
14728  	 	 	 	 	 $out .= ' /Functions ['.trim($functions).']';
14729  	 	 	 	 	 $out .= ' /Bounds ['.trim($bounds).']';
14730  	 	 	 	 	 $out .= ' /Encode ['.trim($encode).']';
14731  	 	 	 	 	 $out .= ' >>';
14732  	 	 	 	 	 $out .= "\n".'endobj';
14733  	 	 	 	 	 $this->_out($out);
14734  	 	 	 	 	 for ($i = 1; $i < $num_cols; ++$i) {
14735  	 	 	 	 	 	 $this->_newobj();
14736  	 	 	 	 	 	 $out = '<<';
14737  	 	 	 	 	 	 $out .= ' /FunctionType 2';
14738  	 	 	 	 	 	 $out .= ' /Domain [0 1]';
14739  	 	 	 	 	 	 $out .= ' /C0 ['.$grad['colors'][($i - 1)]['opacity'].']';
14740  	 	 	 	 	 	 $out .= ' /C1 ['.$grad['colors'][$i]['opacity'].']';
14741  	 	 	 	 	 	 $out .= ' /N '.$grad['colors'][$i]['exponent'];
14742  	 	 	 	 	 	 $out .= ' >>';
14743  	 	 	 	 	 	 $out .= "\n".'endobj';
14744  	 	 	 	 	 	 $this->_out($out);
14745  	 	 	 	 	 }
14746  	 	 	 	 }
14747  	 	 	 }
14748  	 	 	 // set shading object
14749  	 	 	 $this->_newobj();
14750  	 	 	 $out = '<< /ShadingType '.$grad['type'];
14751  	 	 	 if (isset($grad['colspace'])) {
14752  	 	 	 	 $out .= ' /ColorSpace /'.$grad['colspace'];
14753  	 	 	 } else {
14754  	 	 	 	 $out .= ' /ColorSpace /DeviceRGB';
14755  	 	 	 }
14756  	 	 	 if (isset($grad['background']) AND !empty($grad['background'])) {
14757  	 	 	 	 $out .= ' /Background ['.$grad['background'].']';
14758  	 	 	 }
14759  	 	 	 if (isset($grad['antialias']) AND ($grad['antialias'] === true)) {
14760  	 	 	 	 $out .= ' /AntiAlias true';
14761  	 	 	 }
14762  	 	 	 if ($grad['type'] == 2) {
14763  	 	 	 	 $out .= ' '.sprintf('/Coords [%F %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3]);
14764  	 	 	 	 $out .= ' /Domain [0 1]';
14765  	 	 	 	 $out .= ' /Function '.$fc.' 0 R';
14766  	 	 	 	 $out .= ' /Extend [true true]';
14767  	 	 	 	 $out .= ' >>';
14768  	 	 	 } elseif ($grad['type'] == 3) {
14769  	 	 	 	 //x0, y0, r0, x1, y1, r1
14770  	 	 	 	 //at this this time radius of inner circle is 0
14771  	 	 	 	 $out .= ' '.sprintf('/Coords [%F %F 0 %F %F %F]', $grad['coords'][0], $grad['coords'][1], $grad['coords'][2], $grad['coords'][3], $grad['coords'][4]);
14772  	 	 	 	 $out .= ' /Domain [0 1]';
14773  	 	 	 	 $out .= ' /Function '.$fc.' 0 R';
14774  	 	 	 	 $out .= ' /Extend [true true]';
14775  	 	 	 	 $out .= ' >>';
14776  	 	 	 } elseif ($grad['type'] == 6) {
14777  	 	 	 	 $out .= ' /BitsPerCoordinate 16';
14778  	 	 	 	 $out .= ' /BitsPerComponent 8';
14779  	 	 	 	 $out .= ' /Decode[0 1 0 1 0 1 0 1 0 1]';
14780  	 	 	 	 $out .= ' /BitsPerFlag 8';
14781  	 	 	 	 $stream = $this->_getrawstream($grad['stream']);
14782  	 	 	 	 $out .= ' /Length '.strlen($stream);
14783  	 	 	 	 $out .= ' >>';
14784  	 	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
14785  	 	 	 }
14786  	 	 	 $out .= "\n".'endobj';
14787  	 	 	 $this->_out($out);
14788  	 	 	 if ($grad['transparency']) {
14789  	 	 	 	 $shading_transparency = preg_replace('/\/ColorSpace \/[^\s]+/si', '/ColorSpace /DeviceGray', $out);
14790  	 	 	 	 $shading_transparency = preg_replace('/\/Function [0-9]+ /si', '/Function '.$ft.' ', $shading_transparency);
14791  	 	 	 }
14792  	 	 	 $this->gradients[$id]['id'] = $this->n;
14793  	 	 	 // set pattern object
14794  	 	 	 $this->_newobj();
14795  	 	 	 $out = '<< /Type /Pattern /PatternType 2';
14796  	 	 	 $out .= ' /Shading '.$this->gradients[$id]['id'].' 0 R';
14797  	 	 	 $out .= ' >>';
14798  	 	 	 $out .= "\n".'endobj';
14799  	 	 	 $this->_out($out);
14800  	 	 	 $this->gradients[$id]['pattern'] = $this->n;
14801  	 	 	 // set shading and pattern for transparency mask
14802  	 	 	 if ($grad['transparency']) {
14803  	 	 	 	 // luminosity pattern
14804  	 	 	 	 $idgs = $id + $idt;
14805  	 	 	 	 $this->_newobj();
14806  	 	 	 	 $this->_out($shading_transparency);
14807  	 	 	 	 $this->gradients[$idgs]['id'] = $this->n;
14808  	 	 	 	 $this->_newobj();
14809  	 	 	 	 $out = '<< /Type /Pattern /PatternType 2';
14810  	 	 	 	 $out .= ' /Shading '.$this->gradients[$idgs]['id'].' 0 R';
14811  	 	 	 	 $out .= ' >>';
14812  	 	 	 	 $out .= "\n".'endobj';
14813  	 	 	 	 $this->_out($out);
14814  	 	 	 	 $this->gradients[$idgs]['pattern'] = $this->n;
14815  	 	 	 	 // luminosity XObject
14816  	 	 	 	 $oid = $this->_newobj();
14817  	 	 	 	 $this->xobjects['LX'.$oid] = array('n' => $oid);
14818  	 	 	 	 $filter = '';
14819  	 	 	 	 $stream = 'q /a0 gs /Pattern cs /p'.$idgs.' scn 0 0 '.$this->wPt.' '.$this->hPt.' re f Q';
14820  	 	 	 	 if ($this->compress) {
14821  	 	 	 	 	 $filter = ' /Filter /FlateDecode';
14822  	 	 	 	 	 $stream = gzcompress($stream);
14823  	 	 	 	 }
14824  	 	 	 	 $stream = $this->_getrawstream($stream);
14825  	 	 	 	 $out = '<< /Type /XObject /Subtype /Form /FormType 1'.$filter;
14826  	 	 	 	 $out .= ' /Length '.strlen($stream);
14827  	 	 	 	 $rect = sprintf('%F %F', $this->wPt, $this->hPt);
14828  	 	 	 	 $out .= ' /BBox [0 0 '.$rect.']';
14829  	 	 	 	 $out .= ' /Group << /Type /Group /S /Transparency /CS /DeviceGray >>';
14830  	 	 	 	 $out .= ' /Resources <<';
14831  	 	 	 	 $out .= ' /ExtGState << /a0 << /ca 1 /CA 1 >> >>';
14832  	 	 	 	 $out .= ' /Pattern << /p'.$idgs.' '.$this->gradients[$idgs]['pattern'].' 0 R >>';
14833  	 	 	 	 $out .= ' >>';
14834  	 	 	 	 $out .= ' >> ';
14835  	 	 	 	 $out .= ' stream'."\n".$stream."\n".'endstream';
14836  	 	 	 	 $out .= "\n".'endobj';
14837  	 	 	 	 $this->_out($out);
14838  	 	 	 	 // SMask
14839  	 	 	 	 $this->_newobj();
14840  	 	 	 	 $out = '<< /Type /Mask /S /Luminosity /G '.($this->n - 1).' 0 R >>'."\n".'endobj';
14841  	 	 	 	 $this->_out($out);
14842  	 	 	 	 // ExtGState
14843  	 	 	 	 $this->_newobj();
14844  	 	 	 	 $out = '<< /Type /ExtGState /SMask '.($this->n - 1).' 0 R /AIS false >>'."\n".'endobj';
14845  	 	 	 	 $this->_out($out);
14846  	 	 	 	 $this->extgstates[] = array('n' => $this->n, 'name' => 'TGS'.$id);
14847  	 	 	 }
14848  	 	 }
14849  	 }
14850  
14851  	 /**
14852  	  * Draw the sector of a circle.
14853  	  * It can be used for instance to render pie charts.
14854  	  * @param $xc (float) abscissa of the center.
14855  	  * @param $yc (float) ordinate of the center.
14856  	  * @param $r (float) radius.
14857  	  * @param $a (float) start angle (in degrees).
14858  	  * @param $b (float) end angle (in degrees).
14859  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14860  	  * @param $cw: (float) indicates whether to go clockwise (default: true).
14861  	  * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock). Default: 90.
14862  	  * @author Maxime Delorme, Nicola Asuni
14863  	  * @since 3.1.000 (2008-06-09)
14864  	  * @public
14865  	  */
14866  	public function PieSector($xc, $yc, $r, $a, $b, $style='FD', $cw=true, $o=90) {
14867  	 	 $this->PieSectorXY($xc, $yc, $r, $r, $a, $b, $style, $cw, $o);
14868  	 }
14869  
14870  	 /**
14871  	  * Draw the sector of an ellipse.
14872  	  * It can be used for instance to render pie charts.
14873  	  * @param $xc (float) abscissa of the center.
14874  	  * @param $yc (float) ordinate of the center.
14875  	  * @param $rx (float) the x-axis radius.
14876  	  * @param $ry (float) the y-axis radius.
14877  	  * @param $a (float) start angle (in degrees).
14878  	  * @param $b (float) end angle (in degrees).
14879  	  * @param $style (string) Style of rendering. See the getPathPaintOperator() function for more information.
14880  	  * @param $cw: (float) indicates whether to go clockwise.
14881  	  * @param $o: (float) origin of angles (0 for 3 o'clock, 90 for noon, 180 for 9 o'clock, 270 for 6 o'clock).
14882  	  * @param $nc (integer) Number of curves used to draw a 90 degrees portion of arc.
14883  	  * @author Maxime Delorme, Nicola Asuni
14884  	  * @since 3.1.000 (2008-06-09)
14885  	  * @public
14886  	  */
14887  	public function PieSectorXY($xc, $yc, $rx, $ry, $a, $b, $style='FD', $cw=false, $o=0, $nc=2) {
14888  	 	 if ($this->state != 2) {
14889  	 	 	  return;
14890  	 	 }
14891  	 	 if ($this->rtl) {
14892  	 	 	 $xc = ($this->w - $xc);
14893  	 	 }
14894  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style);
14895  	 	 if ($op == 'f') {
14896  	 	 	 $line_style = array();
14897  	 	 }
14898  	 	 if ($cw) {
14899  	 	 	 $d = $b;
14900  	 	 	 $b = (360 - $a + $o);
14901  	 	 	 $a = (360 - $d + $o);
14902  	 	 } else {
14903  	 	 	 $b += $o;
14904  	 	 	 $a += $o;
14905  	 	 }
14906  	 	 $this->_outellipticalarc($xc, $yc, $rx, $ry, 0, $a, $b, true, $nc);
14907  	 	 $this->_out($op);
14908  	 }
14909  
14910  	 /**
14911  	  * Embed vector-based Adobe Illustrator (AI) or AI-compatible EPS files.
14912  	  * NOTE: EPS is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
14913  	  * Only vector drawing is supported, not text or bitmap.
14914  	  * 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).
14915  	  * @param $file (string) Name of the file containing the image or a '@' character followed by the EPS/AI data string.
14916  	  * @param $x (float) Abscissa of the upper-left corner.
14917  	  * @param $y (float) Ordinate of the upper-left corner.
14918  	  * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
14919  	  * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
14920  	  * @param $link (mixed) URL or identifier returned by AddLink().
14921  	  * @param $useBoundingBox (boolean) specifies whether to position the bounding box (true) or the complete canvas (false) at location (x,y). Default value is true.
14922  	  * @param $align (string) 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>
14923  	  * @param $palign (string) 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>
14924  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
14925  	  * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
14926  	  * @param $fixoutvals (boolean) if true remove values outside the bounding box.
14927  	  * @author Valentin Schmidt, Nicola Asuni
14928  	  * @since 3.1.000 (2008-06-09)
14929  	  * @public
14930  	  */
14931  	public function ImageEps($file, $x='', $y='', $w=0, $h=0, $link='', $useBoundingBox=true, $align='', $palign='', $border=0, $fitonpage=false, $fixoutvals=false) {
14932  	 	 if ($this->state != 2) {
14933  	 	 	  return;
14934  	 	 }
14935  	 	 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
14936  	 	 	 // convert EPS to raster image using GD or ImageMagick libraries
14937  	 	 	 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
14938  	 	 }
14939  	 	 if ($x === '') {
14940  	 	 	 $x = $this->x;
14941  	 	 }
14942  	 	 if ($y === '') {
14943  	 	 	 $y = $this->y;
14944  	 	 }
14945  	 	 // check page for no-write regions and adapt page margins if necessary
14946  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
14947  	 	 $k = $this->k;
14948  	 	 if ($file[0] === '@') { // image from string
14949  	 	 	 $data = substr($file, 1);
14950  	 	 } else { // EPS/AI file
14951              $data = $this->getCachedFileContents($file);
14952  	 	 }
14953  	 	 if ($data === FALSE) {
14954  	 	 	 $this->Error('EPS file not found: '.$file);
14955  	 	 }
14956  	 	 $regs = array();
14957  	 	 // EPS/AI compatibility check (only checks files created by Adobe Illustrator!)
14958  	 	 preg_match("/%%Creator:([^\r\n]+)/", $data, $regs); # find Creator
14959  	 	 if (count($regs) > 1) {
14960  	 	 	 $version_str = trim($regs[1]); # e.g. "Adobe Illustrator(R) 8.0"
14961  	 	 	 if (strpos($version_str, 'Adobe Illustrator') !== false) {
14962  	 	 	 	 $versexp = explode(' ', $version_str);
14963  	 	 	 	 $version = (float)array_pop($versexp);
14964  	 	 	 	 if ($version >= 9) {
14965  	 	 	 	 	 $this->Error('This version of Adobe Illustrator file is not supported: '.$file);
14966  	 	 	 	 }
14967  	 	 	 }
14968  	 	 }
14969  	 	 // strip binary bytes in front of PS-header
14970  	 	 $start = strpos($data, '%!PS-Adobe');
14971  	 	 if ($start > 0) {
14972  	 	 	 $data = substr($data, $start);
14973  	 	 }
14974  	 	 // find BoundingBox params
14975  	 	 preg_match("/%%BoundingBox:([^\r\n]+)/", $data, $regs);
14976  	 	 if (count($regs) > 1) {
14977  	 	 	 list($x1, $y1, $x2, $y2) = explode(' ', trim($regs[1]));
14978  	 	 } else {
14979  	 	 	 $this->Error('No BoundingBox found in EPS/AI file: '.$file);
14980  	 	 }
14981  	 	 $start = strpos($data, '%%EndSetup');
14982  	 	 if ($start === false) {
14983  	 	 	 $start = strpos($data, '%%EndProlog');
14984  	 	 }
14985  	 	 if ($start === false) {
14986  	 	 	 $start = strpos($data, '%%BoundingBox');
14987  	 	 }
14988  	 	 $data = substr($data, $start);
14989  	 	 $end = strpos($data, '%%PageTrailer');
14990  	 	 if ($end===false) {
14991  	 	 	 $end = strpos($data, 'showpage');
14992  	 	 }
14993  	 	 if ($end) {
14994  	 	 	 $data = substr($data, 0, $end);
14995  	 	 }
14996  	 	 // calculate image width and height on document
14997  	 	 if (($w <= 0) AND ($h <= 0)) {
14998  	 	 	 $w = ($x2 - $x1) / $k;
14999  	 	 	 $h = ($y2 - $y1) / $k;
15000  	 	 } elseif ($w <= 0) {
15001  	 	 	 $w = ($x2-$x1) / $k * ($h / (($y2 - $y1) / $k));
15002  	 	 } elseif ($h <= 0) {
15003  	 	 	 $h = ($y2 - $y1) / $k * ($w / (($x2 - $x1) / $k));
15004  	 	 }
15005  	 	 // fit the image on available space
15006  	 	 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
15007  	 	 if ($this->rasterize_vector_images) {
15008  	 	 	 // convert EPS to raster image using GD or ImageMagick libraries
15009  	 	 	 return $this->Image($file, $x, $y, $w, $h, 'EPS', $link, $align, true, 300, $palign, false, false, $border, false, false, $fitonpage);
15010  	 	 }
15011  	 	 // set scaling factors
15012  	 	 $scale_x = $w / (($x2 - $x1) / $k);
15013  	 	 $scale_y = $h / (($y2 - $y1) / $k);
15014  	 	 // set alignment
15015  	 	 $this->img_rb_y = $y + $h;
15016  	 	 // set alignment
15017  	 	 if ($this->rtl) {
15018  	 	 	 if ($palign == 'L') {
15019  	 	 	 	 $ximg = $this->lMargin;
15020  	 	 	 } elseif ($palign == 'C') {
15021  	 	 	 	 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15022  	 	 	 } elseif ($palign == 'R') {
15023  	 	 	 	 $ximg = $this->w - $this->rMargin - $w;
15024  	 	 	 } else {
15025  	 	 	 	 $ximg = $x - $w;
15026  	 	 	 }
15027  	 	 	 $this->img_rb_x = $ximg;
15028  	 	 } else {
15029  	 	 	 if ($palign == 'L') {
15030  	 	 	 	 $ximg = $this->lMargin;
15031  	 	 	 } elseif ($palign == 'C') {
15032  	 	 	 	 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15033  	 	 	 } elseif ($palign == 'R') {
15034  	 	 	 	 $ximg = $this->w - $this->rMargin - $w;
15035  	 	 	 } else {
15036  	 	 	 	 $ximg = $x;
15037  	 	 	 }
15038  	 	 	 $this->img_rb_x = $ximg + $w;
15039  	 	 }
15040  	 	 if ($useBoundingBox) {
15041  	 	 	 $dx = $ximg * $k - $x1;
15042  	 	 	 $dy = $y * $k - $y1;
15043  	 	 } else {
15044  	 	 	 $dx = $ximg * $k;
15045  	 	 	 $dy = $y * $k;
15046  	 	 }
15047  	 	 // save the current graphic state
15048  	 	 $this->_out('q'.$this->epsmarker);
15049  	 	 // translate
15050  	 	 $this->_out(sprintf('%F %F %F %F %F %F cm', 1, 0, 0, 1, $dx, $dy + ($this->hPt - (2 * $y * $k) - ($y2 - $y1))));
15051  	 	 // scale
15052  	 	 if (isset($scale_x)) {
15053  	 	 	 $this->_out(sprintf('%F %F %F %F %F %F cm', $scale_x, 0, 0, $scale_y, $x1 * (1 - $scale_x), $y2 * (1 - $scale_y)));
15054  	 	 }
15055  	 	 // handle pc/unix/mac line endings
15056  	 	 $lines = preg_split('/[\r\n]+/si', $data, -1, PREG_SPLIT_NO_EMPTY);
15057  	 	 $u=0;
15058  	 	 $cnt = count($lines);
15059  	 	 for ($i=0; $i < $cnt; ++$i) {
15060  	 	 	 $line = $lines[$i];
15061  	 	 	 if (($line == '') OR ($line[0] == '%')) {
15062  	 	 	 	 continue;
15063  	 	 	 }
15064  	 	 	 $len = strlen($line);
15065  	 	 	 // check for spot color names
15066  	 	 	 $color_name = '';
15067  	 	 	 if (strcasecmp('x', substr(trim($line), -1)) == 0) {
15068  	 	 	 	 if (preg_match('/\([^\)]*\)/', $line, $matches) > 0) {
15069  	 	 	 	 	 // extract spot color name
15070  	 	 	 	 	 $color_name = $matches[0];
15071  	 	 	 	 	 // remove color name from string
15072  	 	 	 	 	 $line = str_replace(' '.$color_name, '', $line);
15073  	 	 	 	 	 // remove pharentesis from color name
15074  	 	 	 	 	 $color_name = substr($color_name, 1, -1);
15075  	 	 	 	 }
15076  	 	 	 }
15077  	 	 	 $chunks = explode(' ', $line);
15078  	 	 	 $cmd = trim(array_pop($chunks));
15079  	 	 	 // RGB
15080  	 	 	 if (($cmd == 'Xa') OR ($cmd == 'XA')) {
15081  	 	 	 	 $b = array_pop($chunks);
15082  	 	 	 	 $g = array_pop($chunks);
15083  	 	 	 	 $r = array_pop($chunks);
15084  	 	 	 	 $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!
15085  	 	 	 	 continue;
15086  	 	 	 }
15087  	 	 	 $skip = false;
15088  	 	 	 if ($fixoutvals) {
15089  	 	 	 	 // check for values outside the bounding box
15090  	 	 	 	 switch ($cmd) {
15091  	 	 	 	 	 case 'm':
15092  	 	 	 	 	 case 'l':
15093  	 	 	 	 	 case 'L': {
15094  	 	 	 	 	 	 // skip values outside bounding box
15095  	 	 	 	 	 	 foreach ($chunks as $key => $val) {
15096  	 	 	 	 	 	 	 if ((($key % 2) == 0) AND (($val < $x1) OR ($val > $x2))) {
15097  	 	 	 	 	 	 	 	 $skip = true;
15098  	 	 	 	 	 	 	 } elseif ((($key % 2) != 0) AND (($val < $y1) OR ($val > $y2))) {
15099  	 	 	 	 	 	 	 	 $skip = true;
15100  	 	 	 	 	 	 	 }
15101  	 	 	 	 	 	 }
15102  	 	 	 	 	 }
15103  	 	 	 	 }
15104  	 	 	 }
15105  	 	 	 switch ($cmd) {
15106  	 	 	 	 case 'm':
15107  	 	 	 	 case 'l':
15108  	 	 	 	 case 'v':
15109  	 	 	 	 case 'y':
15110  	 	 	 	 case 'c':
15111  	 	 	 	 case 'k':
15112  	 	 	 	 case 'K':
15113  	 	 	 	 case 'g':
15114  	 	 	 	 case 'G':
15115  	 	 	 	 case 's':
15116  	 	 	 	 case 'S':
15117  	 	 	 	 case 'J':
15118  	 	 	 	 case 'j':
15119  	 	 	 	 case 'w':
15120  	 	 	 	 case 'M':
15121  	 	 	 	 case 'd':
15122  	 	 	 	 case 'n': {
15123  	 	 	 	 	 if ($skip) {
15124  	 	 	 	 	 	 break;
15125  	 	 	 	 	 }
15126  	 	 	 	 	 $this->_out($line);
15127  	 	 	 	 	 break;
15128  	 	 	 	 }
15129  	 	 	 	 case 'x': {// custom fill color
15130  	 	 	 	 	 if (empty($color_name)) {
15131  	 	 	 	 	 	 // CMYK color
15132  	 	 	 	 	 	 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15133  	 	 	 	 	 	 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' k');
15134  	 	 	 	 	 } else {
15135  	 	 	 	 	 	 // Spot Color (CMYK + tint)
15136  	 	 	 	 	 	 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15137  	 	 	 	 	 	 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15138  	 	 	 	 	 	 $color_cmd = sprintf('/CS%d cs %F scn', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15139  	 	 	 	 	 	 $this->_out($color_cmd);
15140  	 	 	 	 	 }
15141  	 	 	 	 	 break;
15142  	 	 	 	 }
15143  	 	 	 	 case 'X': { // custom stroke color
15144  	 	 	 	 	 if (empty($color_name)) {
15145  	 	 	 	 	 	 // CMYK color
15146  	 	 	 	 	 	 list($col_c, $col_m, $col_y, $col_k) = $chunks;
15147  	 	 	 	 	 	 $this->_out(''.$col_c.' '.$col_m.' '.$col_y.' '.$col_k.' K');
15148  	 	 	 	 	 } else {
15149  	 	 	 	 	 	 // Spot Color (CMYK + tint)
15150  	 	 	 	 	 	 list($col_c, $col_m, $col_y, $col_k, $col_t) = $chunks;
15151  	 	 	 	 	 	 $this->AddSpotColor($color_name, ($col_c * 100), ($col_m * 100), ($col_y * 100), ($col_k * 100));
15152  	 	 	 	 	 	 $color_cmd = sprintf('/CS%d CS %F SCN', $this->spot_colors[$color_name]['i'], (1 - $col_t));
15153  	 	 	 	 	 	 $this->_out($color_cmd);
15154  	 	 	 	 	 }
15155  	 	 	 	 	 break;
15156  	 	 	 	 }
15157  	 	 	 	 case 'Y':
15158  	 	 	 	 case 'N':
15159  	 	 	 	 case 'V':
15160  	 	 	 	 case 'L':
15161  	 	 	 	 case 'C': {
15162  	 	 	 	 	 if ($skip) {
15163  	 	 	 	 	 	 break;
15164  	 	 	 	 	 }
15165  	 	 	 	 	 $line[($len - 1)] = strtolower($cmd);
15166  	 	 	 	 	 $this->_out($line);
15167  	 	 	 	 	 break;
15168  	 	 	 	 }
15169  	 	 	 	 case 'b':
15170  	 	 	 	 case 'B': {
15171  	 	 	 	 	 $this->_out($cmd . '*');
15172  	 	 	 	 	 break;
15173  	 	 	 	 }
15174  	 	 	 	 case 'f':
15175  	 	 	 	 case 'F': {
15176  	 	 	 	 	 if ($u > 0) {
15177  	 	 	 	 	 	 $isU = false;
15178  	 	 	 	 	 	 $max = min(($i + 5), $cnt);
15179  	 	 	 	 	 	 for ($j = ($i + 1); $j < $max; ++$j) {
15180  	 	 	 	 	 	 	 $isU = ($isU OR (($lines[$j] == 'U') OR ($lines[$j] == '*U')));
15181  	 	 	 	 	 	 }
15182  	 	 	 	 	 	 if ($isU) {
15183  	 	 	 	 	 	 	 $this->_out('f*');
15184  	 	 	 	 	 	 }
15185  	 	 	 	 	 } else {
15186  	 	 	 	 	 	 $this->_out('f*');
15187  	 	 	 	 	 }
15188  	 	 	 	 	 break;
15189  	 	 	 	 }
15190  	 	 	 	 case '*u': {
15191  	 	 	 	 	 ++$u;
15192  	 	 	 	 	 break;
15193  	 	 	 	 }
15194  	 	 	 	 case '*U': {
15195  	 	 	 	 	 --$u;
15196  	 	 	 	 	 break;
15197  	 	 	 	 }
15198  	 	 	 }
15199  	 	 }
15200  	 	 // restore previous graphic state
15201  	 	 $this->_out($this->epsmarker.'Q');
15202  	 	 if (!empty($border)) {
15203  	 	 	 $bx = $this->x;
15204  	 	 	 $by = $this->y;
15205  	 	 	 $this->x = $ximg;
15206  	 	 	 if ($this->rtl) {
15207  	 	 	 	 $this->x += $w;
15208  	 	 	 }
15209  	 	 	 $this->y = $y;
15210  	 	 	 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
15211  	 	 	 $this->x = $bx;
15212  	 	 	 $this->y = $by;
15213  	 	 }
15214  	 	 if ($link) {
15215  	 	 	 $this->Link($ximg, $y, $w, $h, $link, 0);
15216  	 	 }
15217  	 	 // set pointer to align the next text/objects
15218  	 	 switch($align) {
15219  	 	 	 case 'T':{
15220  	 	 	 	 $this->y = $y;
15221  	 	 	 	 $this->x = $this->img_rb_x;
15222  	 	 	 	 break;
15223  	 	 	 }
15224  	 	 	 case 'M':{
15225  	 	 	 	 $this->y = $y + round($h/2);
15226  	 	 	 	 $this->x = $this->img_rb_x;
15227  	 	 	 	 break;
15228  	 	 	 }
15229  	 	 	 case 'B':{
15230  	 	 	 	 $this->y = $this->img_rb_y;
15231  	 	 	 	 $this->x = $this->img_rb_x;
15232  	 	 	 	 break;
15233  	 	 	 }
15234  	 	 	 case 'N':{
15235  	 	 	 	 $this->SetY($this->img_rb_y);
15236  	 	 	 	 break;
15237  	 	 	 }
15238  	 	 	 default:{
15239  	 	 	 	 break;
15240  	 	 	 }
15241  	 	 }
15242  	 	 $this->endlinex = $this->img_rb_x;
15243  	 }
15244  
15245  	 /**
15246  	  * Set document barcode.
15247  	  * @param $bc (string) barcode
15248  	  * @public
15249  	  */
15250  	public function setBarcode($bc='') {
15251  	 	 $this->barcode = $bc;
15252  	 }
15253  
15254  	 /**
15255  	  * Get current barcode.
15256  	  * @return string
15257  	  * @public
15258  	  * @since 4.0.012 (2008-07-24)
15259  	  */
15260  	public function getBarcode() {
15261  	 	 return $this->barcode;
15262  	 }
15263  
15264  	 /**
15265  	  * Print a Linear Barcode.
15266  	  * @param $code (string) code to print
15267  	  * @param $type (string) type of barcode (see tcpdf_barcodes_1d.php for supported formats).
15268  	  * @param $x (int) x position in user units (empty string = current x position)
15269  	  * @param $y (int) y position in user units (empty string = current y position)
15270  	  * @param $w (int) width in user units (empty string = remaining page width)
15271  	  * @param $h (int) height in user units (empty string = remaining page height)
15272  	  * @param $xres (float) width of the smallest bar in user units (empty string = default value = 0.4mm)
15273  	  * @param $style (array) array of options:<ul>
15274  	  * <li>boolean $style['border'] if true prints a border</li>
15275  	  * <li>int $style['padding'] padding to leave around the barcode in user units (set to 'auto' for automatic padding)</li>
15276  	  * <li>int $style['hpadding'] horizontal padding in user units (set to 'auto' for automatic padding)</li>
15277  	  * <li>int $style['vpadding'] vertical padding in user units (set to 'auto' for automatic padding)</li>
15278  	  * <li>array $style['fgcolor'] color array for bars and text</li>
15279  	  * <li>mixed $style['bgcolor'] color array for background (set to false for transparent)</li>
15280  	  * <li>boolean $style['text'] if true prints text below the barcode</li>
15281  	  * <li>string $style['label'] override default label</li>
15282  	  * <li>string $style['font'] font name for text</li><li>int $style['fontsize'] font size for text</li>
15283  	  * <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>
15284  	  * <li>string $style['position'] horizontal position of the containing barcode cell on the page: L = left margin; C = center; R = right margin.</li>
15285  	  * <li>string $style['align'] horizontal position of the barcode on the containing rectangle: L = left; C = center; R = right.</li>
15286  	  * <li>string $style['stretch'] if true stretch the barcode to best fit the available width, otherwise uses $xres resolution for a single bar.</li>
15287  	  * <li>string $style['fitwidth'] if true reduce the width to fit the barcode width + padding. When this option is enabled the 'stretch' option is automatically disabled.</li>
15288  	  * <li>string $style['cellfitalign'] this option works only when 'fitwidth' is true and 'position' is unset or empty. Set the horizontal position of the containing barcode cell inside the specified rectangle: L = left; C = center; R = right.</li></ul>
15289  	  * @param $align (string) 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>
15290  	  * @author Nicola Asuni
15291  	  * @since 3.1.000 (2008-06-09)
15292  	  * @public
15293  	  */
15294  	public function write1DBarcode($code, $type, $x='', $y='', $w='', $h='', $xres='', $style=array(), $align='') {
15295  	 	 if (TCPDF_STATIC::empty_string(trim($code))) {
15296  	 	 	 return;
15297  	 	 }
15298  	 	 require_once(dirname(__FILE__).'/tcpdf_barcodes_1d.php');
15299  	 	 // save current graphic settings
15300  	 	 $gvars = $this->getGraphicVars();
15301  	 	 // create new barcode object
15302  	 	 $barcodeobj = new TCPDFBarcode($code, $type);
15303  	 	 $arrcode = $barcodeobj->getBarcodeArray();
15304  	 	 if (($arrcode === false) OR empty($arrcode) OR ($arrcode['maxw'] <= 0)) {
15305  	 	 	 $this->Error('Error in 1D barcode string');
15306  	 	 }
15307  	 	 if ($arrcode['maxh'] <= 0) {
15308  	 	 	 $arrcode['maxh'] = 1;
15309  	 	 }
15310  	 	 // set default values
15311  	 	 if (!isset($style['position'])) {
15312  	 	 	 $style['position'] = '';
15313  	 	 } elseif ($style['position'] == 'S') {
15314  	 	 	 // keep this for backward compatibility
15315  	 	 	 $style['position'] = '';
15316  	 	 	 $style['stretch'] = true;
15317  	 	 }
15318  	 	 if (!isset($style['fitwidth'])) {
15319  	 	 	 if (!isset($style['stretch'])) {
15320  	 	 	 	 $style['fitwidth'] = true;
15321  	 	 	 } else {
15322  	 	 	 	 $style['fitwidth'] = false;
15323  	 	 	 }
15324  	 	 }
15325  	 	 if ($style['fitwidth']) {
15326  	 	 	 // disable stretch
15327  	 	 	 $style['stretch'] = false;
15328  	 	 }
15329  	 	 if (!isset($style['stretch'])) {
15330  	 	 	 if (($w === '') OR ($w <= 0)) {
15331  	 	 	 	 $style['stretch'] = false;
15332  	 	 	 } else {
15333  	 	 	 	 $style['stretch'] = true;
15334  	 	 	 }
15335  	 	 }
15336  	 	 if (!isset($style['fgcolor'])) {
15337  	 	 	 $style['fgcolor'] = array(0,0,0); // default black
15338  	 	 }
15339  	 	 if (!isset($style['bgcolor'])) {
15340  	 	 	 $style['bgcolor'] = false; // default transparent
15341  	 	 }
15342  	 	 if (!isset($style['border'])) {
15343  	 	 	 $style['border'] = false;
15344  	 	 }
15345  	 	 $fontsize = 0;
15346  	 	 if (!isset($style['text'])) {
15347  	 	 	 $style['text'] = false;
15348  	 	 }
15349  	 	 if ($style['text'] AND isset($style['font'])) {
15350  	 	 	 if (isset($style['fontsize'])) {
15351  	 	 	 	 $fontsize = $style['fontsize'];
15352  	 	 	 }
15353  	 	 	 $this->SetFont($style['font'], '', $fontsize);
15354  	 	 }
15355  	 	 if (!isset($style['stretchtext'])) {
15356  	 	 	 $style['stretchtext'] = 4;
15357  	 	 }
15358  	 	 if ($x === '') {
15359  	 	 	 $x = $this->x;
15360  	 	 }
15361  	 	 if ($y === '') {
15362  	 	 	 $y = $this->y;
15363  	 	 }
15364  	 	 // check page for no-write regions and adapt page margins if necessary
15365  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15366  	 	 if (($w === '') OR ($w <= 0)) {
15367  	 	 	 if ($this->rtl) {
15368  	 	 	 	 $w = $x - $this->lMargin;
15369  	 	 	 } else {
15370  	 	 	 	 $w = $this->w - $this->rMargin - $x;
15371  	 	 	 }
15372  	 	 }
15373  	 	 // padding
15374  	 	 if (!isset($style['padding'])) {
15375  	 	 	 $padding = 0;
15376  	 	 } elseif ($style['padding'] === 'auto') {
15377  	 	 	 $padding = 10 * ($w / ($arrcode['maxw'] + 20));
15378  	 	 } else {
15379  	 	 	 $padding = floatval($style['padding']);
15380  	 	 }
15381  	 	 // horizontal padding
15382  	 	 if (!isset($style['hpadding'])) {
15383  	 	 	 $hpadding = $padding;
15384  	 	 } elseif ($style['hpadding'] === 'auto') {
15385  	 	 	 $hpadding = 10 * ($w / ($arrcode['maxw'] + 20));
15386  	 	 } else {
15387  	 	 	 $hpadding = floatval($style['hpadding']);
15388  	 	 }
15389  	 	 // vertical padding
15390  	 	 if (!isset($style['vpadding'])) {
15391  	 	 	 $vpadding = $padding;
15392  	 	 } elseif ($style['vpadding'] === 'auto') {
15393  	 	 	 $vpadding = ($hpadding / 2);
15394  	 	 } else {
15395  	 	 	 $vpadding = floatval($style['vpadding']);
15396  	 	 }
15397  	 	 // calculate xres (single bar width)
15398  	 	 $max_xres = ($w - (2 * $hpadding)) / $arrcode['maxw'];
15399  	 	 if ($style['stretch']) {
15400  	 	 	 $xres = $max_xres;
15401  	 	 } else {
15402  	 	 	 if (TCPDF_STATIC::empty_string($xres)) {
15403  	 	 	 	 $xres = (0.141 * $this->k); // default bar width = 0.4 mm
15404  	 	 	 }
15405  	 	 	 if ($xres > $max_xres) {
15406  	 	 	 	 // correct xres to fit on $w
15407  	 	 	 	 $xres = $max_xres;
15408  	 	 	 }
15409  	 	 	 if ((isset($style['padding']) AND ($style['padding'] === 'auto'))
15410  	 	 	 	 OR (isset($style['hpadding']) AND ($style['hpadding'] === 'auto'))) {
15411  	 	 	 	 $hpadding = 10 * $xres;
15412  	 	 	 	 if (isset($style['vpadding']) AND ($style['vpadding'] === 'auto')) {
15413  	 	 	 	 	 $vpadding = ($hpadding / 2);
15414  	 	 	 	 }
15415  	 	 	 }
15416  	 	 }
15417  	 	 if ($style['fitwidth']) {
15418  	 	 	 $wold = $w;
15419  	 	 	 $w = (($arrcode['maxw'] * $xres) + (2 * $hpadding));
15420  	 	 	 if (isset($style['cellfitalign'])) {
15421  	 	 	 	 switch ($style['cellfitalign']) {
15422  	 	 	 	 	 case 'L': {
15423  	 	 	 	 	 	 if ($this->rtl) {
15424  	 	 	 	 	 	 	 $x -= ($wold - $w);
15425  	 	 	 	 	 	 }
15426  	 	 	 	 	 	 break;
15427  	 	 	 	 	 }
15428  	 	 	 	 	 case 'R': {
15429  	 	 	 	 	 	 if (!$this->rtl) {
15430  	 	 	 	 	 	 	 $x += ($wold - $w);
15431  	 	 	 	 	 	 }
15432  	 	 	 	 	 	 break;
15433  	 	 	 	 	 }
15434  	 	 	 	 	 case 'C': {
15435  	 	 	 	 	 	 if ($this->rtl) {
15436  	 	 	 	 	 	 	 $x -= (($wold - $w) / 2);
15437  	 	 	 	 	 	 } else {
15438  	 	 	 	 	 	 	 $x += (($wold - $w) / 2);
15439  	 	 	 	 	 	 }
15440  	 	 	 	 	 	 break;
15441  	 	 	 	 	 }
15442  	 	 	 	 	 default : {
15443  	 	 	 	 	 	 break;
15444  	 	 	 	 	 }
15445  	 	 	 	 }
15446  	 	 	 }
15447  	 	 }
15448  	 	 $text_height = $this->getCellHeight($fontsize / $this->k);
15449  	 	 // height
15450  	 	 if (($h === '') OR ($h <= 0)) {
15451  	 	 	 // set default height
15452  	 	 	 $h = (($arrcode['maxw'] * $xres) / 3) + (2 * $vpadding) + $text_height;
15453  	 	 }
15454  	 	 $barh = $h - $text_height - (2 * $vpadding);
15455  	 	 if ($barh <=0) {
15456  	 	 	 // try to reduce font or padding to fit barcode on available height
15457  	 	 	 if ($text_height > $h) {
15458  	 	 	 	 $fontsize = (($h * $this->k) / (4 * $this->cell_height_ratio));
15459  	 	 	 	 $text_height = $this->getCellHeight($fontsize / $this->k);
15460  	 	 	 	 $this->SetFont($style['font'], '', $fontsize);
15461  	 	 	 }
15462  	 	 	 if ($vpadding > 0) {
15463  	 	 	 	 $vpadding = (($h - $text_height) / 4);
15464  	 	 	 }
15465  	 	 	 $barh = $h - $text_height - (2 * $vpadding);
15466  	 	 }
15467  	 	 // fit the barcode on available space
15468  	 	 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15469  	 	 // set alignment
15470  	 	 $this->img_rb_y = $y + $h;
15471  	 	 // set alignment
15472  	 	 if ($this->rtl) {
15473  	 	 	 if ($style['position'] == 'L') {
15474  	 	 	 	 $xpos = $this->lMargin;
15475  	 	 	 } elseif ($style['position'] == 'C') {
15476  	 	 	 	 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15477  	 	 	 } elseif ($style['position'] == 'R') {
15478  	 	 	 	 $xpos = $this->w - $this->rMargin - $w;
15479  	 	 	 } else {
15480  	 	 	 	 $xpos = $x - $w;
15481  	 	 	 }
15482  	 	 	 $this->img_rb_x = $xpos;
15483  	 	 } else {
15484  	 	 	 if ($style['position'] == 'L') {
15485  	 	 	 	 $xpos = $this->lMargin;
15486  	 	 	 } elseif ($style['position'] == 'C') {
15487  	 	 	 	 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15488  	 	 	 } elseif ($style['position'] == 'R') {
15489  	 	 	 	 $xpos = $this->w - $this->rMargin - $w;
15490  	 	 	 } else {
15491  	 	 	 	 $xpos = $x;
15492  	 	 	 }
15493  	 	 	 $this->img_rb_x = $xpos + $w;
15494  	 	 }
15495  	 	 $xpos_rect = $xpos;
15496  	 	 if (!isset($style['align'])) {
15497  	 	 	 $style['align'] = 'C';
15498  	 	 }
15499  	 	 switch ($style['align']) {
15500  	 	 	 case 'L': {
15501  	 	 	 	 $xpos = $xpos_rect + $hpadding;
15502  	 	 	 	 break;
15503  	 	 	 }
15504  	 	 	 case 'R': {
15505  	 	 	 	 $xpos = $xpos_rect + ($w - ($arrcode['maxw'] * $xres)) - $hpadding;
15506  	 	 	 	 break;
15507  	 	 	 }
15508  	 	 	 case 'C':
15509  	 	 	 default : {
15510  	 	 	 	 $xpos = $xpos_rect + (($w - ($arrcode['maxw'] * $xres)) / 2);
15511  	 	 	 	 break;
15512  	 	 	 }
15513  	 	 }
15514  	 	 $xpos_text = $xpos;
15515  	 	 // barcode is always printed in LTR direction
15516  	 	 $tempRTL = $this->rtl;
15517  	 	 $this->rtl = false;
15518  	 	 // print background color
15519  	 	 if ($style['bgcolor']) {
15520  	 	 	 $this->Rect($xpos_rect, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15521  	 	 } elseif ($style['border']) {
15522  	 	 	 $this->Rect($xpos_rect, $y, $w, $h, 'D');
15523  	 	 }
15524  	 	 // set foreground color
15525  	 	 $this->SetDrawColorArray($style['fgcolor']);
15526  	 	 $this->SetTextColorArray($style['fgcolor']);
15527  	 	 // print bars
15528  	 	 foreach ($arrcode['bcode'] as $k => $v) {
15529  	 	 	 $bw = ($v['w'] * $xres);
15530  	 	 	 if ($v['t']) {
15531  	 	 	 	 // draw a vertical bar
15532  	 	 	 	 $ypos = $y + $vpadding + ($v['p'] * $barh / $arrcode['maxh']);
15533  	 	 	 	 $this->Rect($xpos, $ypos, $bw, ($v['h'] * $barh / $arrcode['maxh']), 'F', array(), $style['fgcolor']);
15534  	 	 	 }
15535  	 	 	 $xpos += $bw;
15536  	 	 }
15537  	 	 // print text
15538  	 	 if ($style['text']) {
15539  	 	 	 if (isset($style['label']) AND !TCPDF_STATIC::empty_string($style['label'])) {
15540  	 	 	 	 $label = $style['label'];
15541  	 	 	 } else {
15542  	 	 	 	 $label = $code;
15543  	 	 	 }
15544  	 	 	 $txtwidth = ($arrcode['maxw'] * $xres);
15545  	 	 	 if ($this->GetStringWidth($label) > $txtwidth) {
15546  	 	 	 	 $style['stretchtext'] = 2;
15547  	 	 	 }
15548  	 	 	 // print text
15549  	 	 	 $this->x = $xpos_text;
15550  	 	 	 $this->y = $y + $vpadding + $barh;
15551  	 	 	 $cellpadding = $this->cell_padding;
15552  	 	 	 $this->SetCellPadding(0);
15553  	 	 	 $this->Cell($txtwidth, 0, $label, 0, 0, 'C', false, '', $style['stretchtext'], false, 'T', 'T');
15554  	 	 	 $this->cell_padding = $cellpadding;
15555  	 	 }
15556  	 	 // restore original direction
15557  	 	 $this->rtl = $tempRTL;
15558  	 	 // restore previous settings
15559  	 	 $this->setGraphicVars($gvars);
15560  	 	 // set pointer to align the next text/objects
15561  	 	 switch($align) {
15562  	 	 	 case 'T':{
15563  	 	 	 	 $this->y = $y;
15564  	 	 	 	 $this->x = $this->img_rb_x;
15565  	 	 	 	 break;
15566  	 	 	 }
15567  	 	 	 case 'M':{
15568  	 	 	 	 $this->y = $y + round($h / 2);
15569  	 	 	 	 $this->x = $this->img_rb_x;
15570  	 	 	 	 break;
15571  	 	 	 }
15572  	 	 	 case 'B':{
15573  	 	 	 	 $this->y = $this->img_rb_y;
15574  	 	 	 	 $this->x = $this->img_rb_x;
15575  	 	 	 	 break;
15576  	 	 	 }
15577  	 	 	 case 'N':{
15578  	 	 	 	 $this->SetY($this->img_rb_y);
15579  	 	 	 	 break;
15580  	 	 	 }
15581  	 	 	 default:{
15582  	 	 	 	 break;
15583  	 	 	 }
15584  	 	 }
15585  	 	 $this->endlinex = $this->img_rb_x;
15586  	 }
15587  
15588  	 /**
15589  	  * Print 2D Barcode.
15590  	  * @param $code (string) code to print
15591  	  * @param $type (string) type of barcode (see tcpdf_barcodes_2d.php for supported formats).
15592  	  * @param $x (int) x position in user units
15593  	  * @param $y (int) y position in user units
15594  	  * @param $w (int) width in user units
15595  	  * @param $h (int) height in user units
15596  	  * @param $style (array) array of options:<ul>
15597  	  * <li>boolean $style['border'] if true prints a border around the barcode</li>
15598  	  * <li>int $style['padding'] padding to leave around the barcode in barcode units (set to 'auto' for automatic padding)</li>
15599  	  * <li>int $style['hpadding'] horizontal padding in barcode units (set to 'auto' for automatic padding)</li>
15600  	  * <li>int $style['vpadding'] vertical padding in barcode units (set to 'auto' for automatic padding)</li>
15601  	  * <li>int $style['module_width'] width of a single module in points</li>
15602  	  * <li>int $style['module_height'] height of a single module in points</li>
15603  	  * <li>array $style['fgcolor'] color array for bars and text</li>
15604  	  * <li>mixed $style['bgcolor'] color array for background or false for transparent</li>
15605  	  * <li>string $style['position'] barcode position on the page: L = left margin; C = center; R = right margin; S = stretch</li>
15606  	  * @param $align (string) 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>
15607  	  * @param $distort (boolean) if true distort the barcode to fit width and height, otherwise preserve aspect ratio
15608  	  * @author Nicola Asuni
15609  	  * @since 4.5.037 (2009-04-07)
15610  	  * @public
15611  	  */
15612  	public function write2DBarcode($code, $type, $x='', $y='', $w='', $h='', $style=array(), $align='', $distort=false) {
15613  	 	 if (TCPDF_STATIC::empty_string(trim($code))) {
15614  	 	 	 return;
15615  	 	 }
15616  	 	 require_once(dirname(__FILE__).'/tcpdf_barcodes_2d.php');
15617  	 	 // save current graphic settings
15618  	 	 $gvars = $this->getGraphicVars();
15619  	 	 // create new barcode object
15620  	 	 $barcodeobj = new TCPDF2DBarcode($code, $type);
15621  	 	 $arrcode = $barcodeobj->getBarcodeArray();
15622  	 	 if (($arrcode === false) OR empty($arrcode) OR !isset($arrcode['num_rows']) OR ($arrcode['num_rows'] == 0) OR !isset($arrcode['num_cols']) OR ($arrcode['num_cols'] == 0)) {
15623  	 	 	 $this->Error('Error in 2D barcode string');
15624  	 	 }
15625  	 	 // set default values
15626  	 	 if (!isset($style['position'])) {
15627  	 	 	 $style['position'] = '';
15628  	 	 }
15629  	 	 if (!isset($style['fgcolor'])) {
15630  	 	 	 $style['fgcolor'] = array(0,0,0); // default black
15631  	 	 }
15632  	 	 if (!isset($style['bgcolor'])) {
15633  	 	 	 $style['bgcolor'] = false; // default transparent
15634  	 	 }
15635  	 	 if (!isset($style['border'])) {
15636  	 	 	 $style['border'] = false;
15637  	 	 }
15638  	 	 // padding
15639  	 	 if (!isset($style['padding'])) {
15640  	 	 	 $style['padding'] = 0;
15641  	 	 } elseif ($style['padding'] === 'auto') {
15642  	 	 	 $style['padding'] = 4;
15643  	 	 }
15644  	 	 if (!isset($style['hpadding'])) {
15645  	 	 	 $style['hpadding'] = $style['padding'];
15646  	 	 } elseif ($style['hpadding'] === 'auto') {
15647  	 	 	 $style['hpadding'] = 4;
15648  	 	 }
15649  	 	 if (!isset($style['vpadding'])) {
15650  	 	 	 $style['vpadding'] = $style['padding'];
15651  	 	 } elseif ($style['vpadding'] === 'auto') {
15652  	 	 	 $style['vpadding'] = 4;
15653  	 	 }
15654  	 	 $hpad = (2 * $style['hpadding']);
15655  	 	 $vpad = (2 * $style['vpadding']);
15656  	 	 // cell (module) dimension
15657  	 	 if (!isset($style['module_width'])) {
15658  	 	 	 $style['module_width'] = 1; // width of a single module in points
15659  	 	 }
15660  	 	 if (!isset($style['module_height'])) {
15661  	 	 	 $style['module_height'] = 1; // height of a single module in points
15662  	 	 }
15663  	 	 if ($x === '') {
15664  	 	 	 $x = $this->x;
15665  	 	 }
15666  	 	 if ($y === '') {
15667  	 	 	 $y = $this->y;
15668  	 	 }
15669  	 	 // check page for no-write regions and adapt page margins if necessary
15670  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
15671  	 	 // number of barcode columns and rows
15672  	 	 $rows = $arrcode['num_rows'];
15673  	 	 $cols = $arrcode['num_cols'];
15674  	 	 if (($rows <= 0) || ($cols <= 0)){
15675  	 	 	 $this->Error('Error in 2D barcode string');
15676  	 	 }
15677  	 	 // module width and height
15678  	 	 $mw = $style['module_width'];
15679  	 	 $mh = $style['module_height'];
15680  	 	 if (($mw <= 0) OR ($mh <= 0)) {
15681  	 	 	 $this->Error('Error in 2D barcode string');
15682  	 	 }
15683  	 	 // get max dimensions
15684  	 	 if ($this->rtl) {
15685  	 	 	 $maxw = $x - $this->lMargin;
15686  	 	 } else {
15687  	 	 	 $maxw = $this->w - $this->rMargin - $x;
15688  	 	 }
15689  	 	 $maxh = ($this->h - $this->tMargin - $this->bMargin);
15690  	 	 $ratioHW = ((($rows * $mh) + $hpad) / (($cols * $mw) + $vpad));
15691  	 	 $ratioWH = ((($cols * $mw) + $vpad) / (($rows * $mh) + $hpad));
15692  	 	 if (!$distort) {
15693  	 	 	 if (($maxw * $ratioHW) > $maxh) {
15694  	 	 	 	 $maxw = $maxh * $ratioWH;
15695  	 	 	 }
15696  	 	 	 if (($maxh * $ratioWH) > $maxw) {
15697  	 	 	 	 $maxh = $maxw * $ratioHW;
15698  	 	 	 }
15699  	 	 }
15700  	 	 // set maximum dimensions
15701  	 	 if ($w > $maxw) {
15702  	 	 	 $w = $maxw;
15703  	 	 }
15704  	 	 if ($h > $maxh) {
15705  	 	 	 $h = $maxh;
15706  	 	 }
15707  	 	 // set dimensions
15708  	 	 if ((($w === '') OR ($w <= 0)) AND (($h === '') OR ($h <= 0))) {
15709  	 	 	 $w = ($cols + $hpad) * ($mw / $this->k);
15710  	 	 	 $h = ($rows + $vpad) * ($mh / $this->k);
15711  	 	 } elseif (($w === '') OR ($w <= 0)) {
15712  	 	 	 $w = $h * $ratioWH;
15713  	 	 } elseif (($h === '') OR ($h <= 0)) {
15714  	 	 	 $h = $w * $ratioHW;
15715  	 	 }
15716  	 	 // barcode size (excluding padding)
15717  	 	 $bw = ($w * $cols) / ($cols + $hpad);
15718  	 	 $bh = ($h * $rows) / ($rows + $vpad);
15719  	 	 // dimension of single barcode cell unit
15720  	 	 $cw = $bw / $cols;
15721  	 	 $ch = $bh / $rows;
15722  	 	 if (!$distort) {
15723  	 	 	 if (($cw / $ch) > ($mw / $mh)) {
15724  	 	 	 	 // correct horizontal distortion
15725  	 	 	 	 $cw = $ch * $mw / $mh;
15726  	 	 	 	 $bw = $cw * $cols;
15727  	 	 	 	 $style['hpadding'] = ($w - $bw) / (2 * $cw);
15728  	 	 	 } else {
15729  	 	 	 	 // correct vertical distortion
15730  	 	 	 	 $ch = $cw * $mh / $mw;
15731  	 	 	 	 $bh = $ch * $rows;
15732  	 	 	 	 $style['vpadding'] = ($h - $bh) / (2 * $ch);
15733  	 	 	 }
15734  	 	 }
15735  	 	 // fit the barcode on available space
15736  	 	 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, false);
15737  	 	 // set alignment
15738  	 	 $this->img_rb_y = $y + $h;
15739  	 	 // set alignment
15740  	 	 if ($this->rtl) {
15741  	 	 	 if ($style['position'] == 'L') {
15742  	 	 	 	 $xpos = $this->lMargin;
15743  	 	 	 } elseif ($style['position'] == 'C') {
15744  	 	 	 	 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15745  	 	 	 } elseif ($style['position'] == 'R') {
15746  	 	 	 	 $xpos = $this->w - $this->rMargin - $w;
15747  	 	 	 } else {
15748  	 	 	 	 $xpos = $x - $w;
15749  	 	 	 }
15750  	 	 	 $this->img_rb_x = $xpos;
15751  	 	 } else {
15752  	 	 	 if ($style['position'] == 'L') {
15753  	 	 	 	 $xpos = $this->lMargin;
15754  	 	 	 } elseif ($style['position'] == 'C') {
15755  	 	 	 	 $xpos = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
15756  	 	 	 } elseif ($style['position'] == 'R') {
15757  	 	 	 	 $xpos = $this->w - $this->rMargin - $w;
15758  	 	 	 } else {
15759  	 	 	 	 $xpos = $x;
15760  	 	 	 }
15761  	 	 	 $this->img_rb_x = $xpos + $w;
15762  	 	 }
15763  	 	 $xstart = $xpos + ($style['hpadding'] * $cw);
15764  	 	 $ystart = $y + ($style['vpadding'] * $ch);
15765  	 	 // barcode is always printed in LTR direction
15766  	 	 $tempRTL = $this->rtl;
15767  	 	 $this->rtl = false;
15768  	 	 // print background color
15769  	 	 if ($style['bgcolor']) {
15770  	 	 	 $this->Rect($xpos, $y, $w, $h, $style['border'] ? 'DF' : 'F', '', $style['bgcolor']);
15771  	 	 } elseif ($style['border']) {
15772  	 	 	 $this->Rect($xpos, $y, $w, $h, 'D');
15773  	 	 }
15774  	 	 // set foreground color
15775  	 	 $this->SetDrawColorArray($style['fgcolor']);
15776  	 	 // print barcode cells
15777  	 	 // for each row
15778  	 	 for ($r = 0; $r < $rows; ++$r) {
15779  	 	 	 $xr = $xstart;
15780  	 	 	 // for each column
15781  	 	 	 for ($c = 0; $c < $cols; ++$c) {
15782  	 	 	 	 if ($arrcode['bcode'][$r][$c] == 1) {
15783  	 	 	 	 	 // draw a single barcode cell
15784  	 	 	 	 	 $this->Rect($xr, $ystart, $cw, $ch, 'F', array(), $style['fgcolor']);
15785  	 	 	 	 }
15786  	 	 	 	 $xr += $cw;
15787  	 	 	 }
15788  	 	 	 $ystart += $ch;
15789  	 	 }
15790  	 	 // restore original direction
15791  	 	 $this->rtl = $tempRTL;
15792  	 	 // restore previous settings
15793  	 	 $this->setGraphicVars($gvars);
15794  	 	 // set pointer to align the next text/objects
15795  	 	 switch($align) {
15796  	 	 	 case 'T':{
15797  	 	 	 	 $this->y = $y;
15798  	 	 	 	 $this->x = $this->img_rb_x;
15799  	 	 	 	 break;
15800  	 	 	 }
15801  	 	 	 case 'M':{
15802  	 	 	 	 $this->y = $y + round($h/2);
15803  	 	 	 	 $this->x = $this->img_rb_x;
15804  	 	 	 	 break;
15805  	 	 	 }
15806  	 	 	 case 'B':{
15807  	 	 	 	 $this->y = $this->img_rb_y;
15808  	 	 	 	 $this->x = $this->img_rb_x;
15809  	 	 	 	 break;
15810  	 	 	 }
15811  	 	 	 case 'N':{
15812  	 	 	 	 $this->SetY($this->img_rb_y);
15813  	 	 	 	 break;
15814  	 	 	 }
15815  	 	 	 default:{
15816  	 	 	 	 break;
15817  	 	 	 }
15818  	 	 }
15819  	 	 $this->endlinex = $this->img_rb_x;
15820  	 }
15821  
15822  	 /**
15823  	  * Returns an array containing current margins:
15824  	  * <ul>
15825  	 	 	 <li>$ret['left'] = left margin</li>
15826  	 	 	 <li>$ret['right'] = right margin</li>
15827  	 	 	 <li>$ret['top'] = top margin</li>
15828  	 	 	 <li>$ret['bottom'] = bottom margin</li>
15829  	 	 	 <li>$ret['header'] = header margin</li>
15830  	 	 	 <li>$ret['footer'] = footer margin</li>
15831  	 	 	 <li>$ret['cell'] = cell padding array</li>
15832  	 	 	 <li>$ret['padding_left'] = cell left padding</li>
15833  	 	 	 <li>$ret['padding_top'] = cell top padding</li>
15834  	 	 	 <li>$ret['padding_right'] = cell right padding</li>
15835  	 	 	 <li>$ret['padding_bottom'] = cell bottom padding</li>
15836  	  * </ul>
15837  	  * @return array containing all margins measures
15838  	  * @public
15839  	  * @since 3.2.000 (2008-06-23)
15840  	  */
15841  	public function getMargins() {
15842  	 	 $ret = array(
15843  	 	 	 'left' => $this->lMargin,
15844  	 	 	 'right' => $this->rMargin,
15845  	 	 	 'top' => $this->tMargin,
15846  	 	 	 'bottom' => $this->bMargin,
15847  	 	 	 'header' => $this->header_margin,
15848  	 	 	 'footer' => $this->footer_margin,
15849  	 	 	 'cell' => $this->cell_padding,
15850  	 	 	 'padding_left' => $this->cell_padding['L'],
15851  	 	 	 'padding_top' => $this->cell_padding['T'],
15852  	 	 	 'padding_right' => $this->cell_padding['R'],
15853  	 	 	 'padding_bottom' => $this->cell_padding['B']
15854  	 	 );
15855  	 	 return $ret;
15856  	 }
15857  
15858  	 /**
15859  	  * Returns an array containing original margins:
15860  	  * <ul>
15861  	 	 	 <li>$ret['left'] = left margin</li>
15862  	 	 	 <li>$ret['right'] = right margin</li>
15863  	  * </ul>
15864  	  * @return array containing all margins measures
15865  	  * @public
15866  	  * @since 4.0.012 (2008-07-24)
15867  	  */
15868  	public function getOriginalMargins() {
15869  	 	 $ret = array(
15870  	 	 	 'left' => $this->original_lMargin,
15871  	 	 	 'right' => $this->original_rMargin
15872  	 	 );
15873  	 	 return $ret;
15874  	 }
15875  
15876  	 /**
15877  	  * Returns the current font size.
15878  	  * @return current font size
15879  	  * @public
15880  	  * @since 3.2.000 (2008-06-23)
15881  	  */
15882  	public function getFontSize() {
15883  	 	 return $this->FontSize;
15884  	 }
15885  
15886  	 /**
15887  	  * Returns the current font size in points unit.
15888  	  * @return current font size in points unit
15889  	  * @public
15890  	  * @since 3.2.000 (2008-06-23)
15891  	  */
15892  	public function getFontSizePt() {
15893  	 	 return $this->FontSizePt;
15894  	 }
15895  
15896  	 /**
15897  	  * Returns the current font family name.
15898  	  * @return string current font family name
15899  	  * @public
15900  	  * @since 4.3.008 (2008-12-05)
15901  	  */
15902  	public function getFontFamily() {
15903  	 	 return $this->FontFamily;
15904  	 }
15905  
15906  	 /**
15907  	  * Returns the current font style.
15908  	  * @return string current font style
15909  	  * @public
15910  	  * @since 4.3.008 (2008-12-05)
15911  	  */
15912  	public function getFontStyle() {
15913  	 	 return $this->FontStyle;
15914  	 }
15915  
15916  	 /**
15917  	  * Cleanup HTML code (requires HTML Tidy library).
15918  	  * @param $html (string) htmlcode to fix
15919  	  * @param $default_css (string) CSS commands to add
15920  	  * @param $tagvs (array) parameters for setHtmlVSpace method
15921  	  * @param $tidy_options (array) options for tidy_parse_string function
15922  	  * @return string XHTML code cleaned up
15923  	  * @author Nicola Asuni
15924  	  * @public
15925  	  * @since 5.9.017 (2010-11-16)
15926  	  * @see setHtmlVSpace()
15927  	  */
15928  	public function fixHTMLCode($html, $default_css='', $tagvs='', $tidy_options='') {
15929  	 	 return TCPDF_STATIC::fixHTMLCode($html, $default_css, $tagvs, $tidy_options, $this->tagvspaces);
15930  	 }
15931  
15932  	 /**
15933  	  * Returns the border width from CSS property
15934  	  * @param $width (string) border width
15935  	  * @return int with in user units
15936  	  * @protected
15937  	  * @since 5.7.000 (2010-08-02)
15938  	  */
15939  	protected function getCSSBorderWidth($width) {
15940  	 	 if ($width == 'thin') {
15941  	 	 	 $width = (2 / $this->k);
15942  	 	 } elseif ($width == 'medium') {
15943  	 	 	 $width = (4 / $this->k);
15944  	 	 } elseif ($width == 'thick') {
15945  	 	 	 $width = (6 / $this->k);
15946  	 	 } else {
15947  	 	 	 $width = $this->getHTMLUnitToUnits($width, 1, 'px', false);
15948  	 	 }
15949  	 	 return $width;
15950  	 }
15951  
15952  	 /**
15953  	  * Returns the border dash style from CSS property
15954  	  * @param $style (string) border style to convert
15955  	  * @return int sash style (return -1 in case of none or hidden border)
15956  	  * @protected
15957  	  * @since 5.7.000 (2010-08-02)
15958  	  */
15959  	protected function getCSSBorderDashStyle($style) {
15960  	 	 switch (strtolower($style)) {
15961  	 	 	 case 'none':
15962  	 	 	 case 'hidden': {
15963  	 	 	 	 $dash = -1;
15964  	 	 	 	 break;
15965  	 	 	 }
15966  	 	 	 case 'dotted': {
15967  	 	 	 	 $dash = 1;
15968  	 	 	 	 break;
15969  	 	 	 }
15970  	 	 	 case 'dashed': {
15971  	 	 	 	 $dash = 3;
15972  	 	 	 	 break;
15973  	 	 	 }
15974  	 	 	 case 'double':
15975  	 	 	 case 'groove':
15976  	 	 	 case 'ridge':
15977  	 	 	 case 'inset':
15978  	 	 	 case 'outset':
15979  	 	 	 case 'solid':
15980  	 	 	 default: {
15981  	 	 	 	 $dash = 0;
15982  	 	 	 	 break;
15983  	 	 	 }
15984  	 	 }
15985  	 	 return $dash;
15986  	 }
15987  
15988  	 /**
15989  	  * Returns the border style array from CSS border properties
15990  	  * @param $cssborder (string) border properties
15991  	  * @return array containing border properties
15992  	  * @protected
15993  	  * @since 5.7.000 (2010-08-02)
15994  	  */
15995  	protected function getCSSBorderStyle($cssborder) {
15996  	 	 $bprop = preg_split('/[\s]+/', trim($cssborder));
15997  	 	 $border = array(); // value to be returned
15998  	 	 switch (count($bprop)) {
15999  	 	 	 case 3: {
16000  	 	 	 	 $width = $bprop[0];
16001  	 	 	 	 $style = $bprop[1];
16002  	 	 	 	 $color = $bprop[2];
16003  	 	 	 	 break;
16004  	 	 	 }
16005  	 	 	 case 2: {
16006  	 	 	 	 $width = 'medium';
16007  	 	 	 	 $style = $bprop[0];
16008  	 	 	 	 $color = $bprop[1];
16009  	 	 	 	 break;
16010  	 	 	 }
16011  	 	 	 case 1: {
16012  	 	 	 	 $width = 'medium';
16013  	 	 	 	 $style = $bprop[0];
16014  	 	 	 	 $color = 'black';
16015  	 	 	 	 break;
16016  	 	 	 }
16017  	 	 	 default: {
16018  	 	 	 	 $width = 'medium';
16019  	 	 	 	 $style = 'solid';
16020  	 	 	 	 $color = 'black';
16021  	 	 	 	 break;
16022  	 	 	 }
16023  	 	 }
16024  	 	 if ($style == 'none') {
16025  	 	 	 return array();
16026  	 	 }
16027  	 	 $border['cap'] = 'square';
16028  	 	 $border['join'] = 'miter';
16029  	 	 $border['dash'] = $this->getCSSBorderDashStyle($style);
16030  	 	 if ($border['dash'] < 0) {
16031  	 	 	 return array();
16032  	 	 }
16033  	 	 $border['width'] = $this->getCSSBorderWidth($width);
16034  	 	 $border['color'] = TCPDF_COLORS::convertHTMLColorToDec($color, $this->spot_colors);
16035  	 	 return $border;
16036  	 }
16037  
16038  	 /**
16039  	  * Get the internal Cell padding from CSS attribute.
16040  	  * @param $csspadding (string) padding properties
16041  	  * @param $width (float) width of the containing element
16042  	  * @return array of cell paddings
16043  	  * @public
16044  	  * @since 5.9.000 (2010-10-04)
16045  	  */
16046  	public function getCSSPadding($csspadding, $width=0) {
16047  	 	 $padding = preg_split('/[\s]+/', trim($csspadding));
16048  	 	 $cell_padding = array(); // value to be returned
16049  	 	 switch (count($padding)) {
16050  	 	 	 case 4: {
16051  	 	 	 	 $cell_padding['T'] = $padding[0];
16052  	 	 	 	 $cell_padding['R'] = $padding[1];
16053  	 	 	 	 $cell_padding['B'] = $padding[2];
16054  	 	 	 	 $cell_padding['L'] = $padding[3];
16055  	 	 	 	 break;
16056  	 	 	 }
16057  	 	 	 case 3: {
16058  	 	 	 	 $cell_padding['T'] = $padding[0];
16059  	 	 	 	 $cell_padding['R'] = $padding[1];
16060  	 	 	 	 $cell_padding['B'] = $padding[2];
16061  	 	 	 	 $cell_padding['L'] = $padding[1];
16062  	 	 	 	 break;
16063  	 	 	 }
16064  	 	 	 case 2: {
16065  	 	 	 	 $cell_padding['T'] = $padding[0];
16066  	 	 	 	 $cell_padding['R'] = $padding[1];
16067  	 	 	 	 $cell_padding['B'] = $padding[0];
16068  	 	 	 	 $cell_padding['L'] = $padding[1];
16069  	 	 	 	 break;
16070  	 	 	 }
16071  	 	 	 case 1: {
16072  	 	 	 	 $cell_padding['T'] = $padding[0];
16073  	 	 	 	 $cell_padding['R'] = $padding[0];
16074  	 	 	 	 $cell_padding['B'] = $padding[0];
16075  	 	 	 	 $cell_padding['L'] = $padding[0];
16076  	 	 	 	 break;
16077  	 	 	 }
16078  	 	 	 default: {
16079  	 	 	 	 return $this->cell_padding;
16080  	 	 	 }
16081  	 	 }
16082  	 	 if ($width == 0) {
16083  	 	 	 $width = $this->w - $this->lMargin - $this->rMargin;
16084  	 	 }
16085  	 	 $cell_padding['T'] = $this->getHTMLUnitToUnits($cell_padding['T'], $width, 'px', false);
16086  	 	 $cell_padding['R'] = $this->getHTMLUnitToUnits($cell_padding['R'], $width, 'px', false);
16087  	 	 $cell_padding['B'] = $this->getHTMLUnitToUnits($cell_padding['B'], $width, 'px', false);
16088  	 	 $cell_padding['L'] = $this->getHTMLUnitToUnits($cell_padding['L'], $width, 'px', false);
16089  	 	 return $cell_padding;
16090  	 }
16091  
16092  	 /**
16093  	  * Get the internal Cell margin from CSS attribute.
16094  	  * @param $cssmargin (string) margin properties
16095  	  * @param $width (float) width of the containing element
16096  	  * @return array of cell margins
16097  	  * @public
16098  	  * @since 5.9.000 (2010-10-04)
16099  	  */
16100  	public function getCSSMargin($cssmargin, $width=0) {
16101  	 	 $margin = preg_split('/[\s]+/', trim($cssmargin));
16102  	 	 $cell_margin = array(); // value to be returned
16103  	 	 switch (count($margin)) {
16104  	 	 	 case 4: {
16105  	 	 	 	 $cell_margin['T'] = $margin[0];
16106  	 	 	 	 $cell_margin['R'] = $margin[1];
16107  	 	 	 	 $cell_margin['B'] = $margin[2];
16108  	 	 	 	 $cell_margin['L'] = $margin[3];
16109  	 	 	 	 break;
16110  	 	 	 }
16111  	 	 	 case 3: {
16112  	 	 	 	 $cell_margin['T'] = $margin[0];
16113  	 	 	 	 $cell_margin['R'] = $margin[1];
16114  	 	 	 	 $cell_margin['B'] = $margin[2];
16115  	 	 	 	 $cell_margin['L'] = $margin[1];
16116  	 	 	 	 break;
16117  	 	 	 }
16118  	 	 	 case 2: {
16119  	 	 	 	 $cell_margin['T'] = $margin[0];
16120  	 	 	 	 $cell_margin['R'] = $margin[1];
16121  	 	 	 	 $cell_margin['B'] = $margin[0];
16122  	 	 	 	 $cell_margin['L'] = $margin[1];
16123  	 	 	 	 break;
16124  	 	 	 }
16125  	 	 	 case 1: {
16126  	 	 	 	 $cell_margin['T'] = $margin[0];
16127  	 	 	 	 $cell_margin['R'] = $margin[0];
16128  	 	 	 	 $cell_margin['B'] = $margin[0];
16129  	 	 	 	 $cell_margin['L'] = $margin[0];
16130  	 	 	 	 break;
16131  	 	 	 }
16132  	 	 	 default: {
16133  	 	 	 	 return $this->cell_margin;
16134  	 	 	 }
16135  	 	 }
16136  	 	 if ($width == 0) {
16137  	 	 	 $width = $this->w - $this->lMargin - $this->rMargin;
16138  	 	 }
16139  	 	 $cell_margin['T'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['T']), $width, 'px', false);
16140  	 	 $cell_margin['R'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['R']), $width, 'px', false);
16141  	 	 $cell_margin['B'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['B']), $width, 'px', false);
16142  	 	 $cell_margin['L'] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $cell_margin['L']), $width, 'px', false);
16143  	 	 return $cell_margin;
16144  	 }
16145  
16146  	 /**
16147  	  * Get the border-spacing from CSS attribute.
16148  	  * @param $cssbspace (string) border-spacing CSS properties
16149  	  * @param $width (float) width of the containing element
16150  	  * @return array of border spacings
16151  	  * @public
16152  	  * @since 5.9.010 (2010-10-27)
16153  	  */
16154  	public function getCSSBorderMargin($cssbspace, $width=0) {
16155  	 	 $space = preg_split('/[\s]+/', trim($cssbspace));
16156  	 	 $border_spacing = array(); // value to be returned
16157  	 	 switch (count($space)) {
16158  	 	 	 case 2: {
16159  	 	 	 	 $border_spacing['H'] = $space[0];
16160  	 	 	 	 $border_spacing['V'] = $space[1];
16161  	 	 	 	 break;
16162  	 	 	 }
16163  	 	 	 case 1: {
16164  	 	 	 	 $border_spacing['H'] = $space[0];
16165  	 	 	 	 $border_spacing['V'] = $space[0];
16166  	 	 	 	 break;
16167  	 	 	 }
16168  	 	 	 default: {
16169  	 	 	 	 return array('H' => 0, 'V' => 0);
16170  	 	 	 }
16171  	 	 }
16172  	 	 if ($width == 0) {
16173  	 	 	 $width = $this->w - $this->lMargin - $this->rMargin;
16174  	 	 }
16175  	 	 $border_spacing['H'] = $this->getHTMLUnitToUnits($border_spacing['H'], $width, 'px', false);
16176  	 	 $border_spacing['V'] = $this->getHTMLUnitToUnits($border_spacing['V'], $width, 'px', false);
16177  	 	 return $border_spacing;
16178  	 }
16179  
16180  	 /**
16181  	  * Returns the letter-spacing value from CSS value
16182  	  * @param $spacing (string) letter-spacing value
16183  	  * @param $parent (float) font spacing (tracking) value of the parent element
16184  	  * @return float quantity to increases or decreases the space between characters in a text.
16185  	  * @protected
16186  	  * @since 5.9.000 (2010-10-02)
16187  	  */
16188  	protected function getCSSFontSpacing($spacing, $parent=0) {
16189  	 	 $val = 0; // value to be returned
16190  	 	 $spacing = trim($spacing);
16191  	 	 switch ($spacing) {
16192  	 	 	 case 'normal': {
16193  	 	 	 	 $val = 0;
16194  	 	 	 	 break;
16195  	 	 	 }
16196  	 	 	 case 'inherit': {
16197  	 	 	 	 if ($parent == 'normal') {
16198  	 	 	 	 	 $val = 0;
16199  	 	 	 	 } else {
16200  	 	 	 	 	 $val = $parent;
16201  	 	 	 	 }
16202  	 	 	 	 break;
16203  	 	 	 }
16204  	 	 	 default: {
16205  	 	 	 	 $val = $this->getHTMLUnitToUnits($spacing, 0, 'px', false);
16206  	 	 	 }
16207  	 	 }
16208  	 	 return $val;
16209  	 }
16210  
16211  	 /**
16212  	  * Returns the percentage of font stretching from CSS value
16213  	  * @param $stretch (string) stretch mode
16214  	  * @param $parent (float) stretch value of the parent element
16215  	  * @return float font stretching percentage
16216  	  * @protected
16217  	  * @since 5.9.000 (2010-10-02)
16218  	  */
16219  	protected function getCSSFontStretching($stretch, $parent=100) {
16220  	 	 $val = 100; // value to be returned
16221  	 	 $stretch = trim($stretch);
16222  	 	 switch ($stretch) {
16223  	 	 	 case 'ultra-condensed': {
16224  	 	 	 	 $val = 40;
16225  	 	 	 	 break;
16226  	 	 	 }
16227  	 	 	 case 'extra-condensed': {
16228  	 	 	 	 $val = 55;
16229  	 	 	 	 break;
16230  	 	 	 }
16231  	 	 	 case 'condensed': {
16232  	 	 	 	 $val = 70;
16233  	 	 	 	 break;
16234  	 	 	 }
16235  	 	 	 case 'semi-condensed': {
16236  	 	 	 	 $val = 85;
16237  	 	 	 	 break;
16238  	 	 	 }
16239  	 	 	 case 'normal': {
16240  	 	 	 	 $val = 100;
16241  	 	 	 	 break;
16242  	 	 	 }
16243  	 	 	 case 'semi-expanded': {
16244  	 	 	 	 $val = 115;
16245  	 	 	 	 break;
16246  	 	 	 }
16247  	 	 	 case 'expanded': {
16248  	 	 	 	 $val = 130;
16249  	 	 	 	 break;
16250  	 	 	 }
16251  	 	 	 case 'extra-expanded': {
16252  	 	 	 	 $val = 145;
16253  	 	 	 	 break;
16254  	 	 	 }
16255  	 	 	 case 'ultra-expanded': {
16256  	 	 	 	 $val = 160;
16257  	 	 	 	 break;
16258  	 	 	 }
16259  	 	 	 case 'wider': {
16260  	 	 	 	 $val = ($parent + 10);
16261  	 	 	 	 break;
16262  	 	 	 }
16263  	 	 	 case 'narrower': {
16264  	 	 	 	 $val = ($parent - 10);
16265  	 	 	 	 break;
16266  	 	 	 }
16267  	 	 	 case 'inherit': {
16268  	 	 	 	 if ($parent == 'normal') {
16269  	 	 	 	 	 $val = 100;
16270  	 	 	 	 } else {
16271  	 	 	 	 	 $val = $parent;
16272  	 	 	 	 }
16273  	 	 	 	 break;
16274  	 	 	 }
16275  	 	 	 default: {
16276  	 	 	 	 $val = $this->getHTMLUnitToUnits($stretch, 100, '%', false);
16277  	 	 	 }
16278  	 	 }
16279  	 	 return $val;
16280  	 }
16281  
16282  	 /**
16283  	  * Convert HTML string containing font size value to points
16284  	  * @param $val (string) String containing font size value and unit.
16285  	  * @param $refsize (float) Reference font size in points.
16286  	  * @param $parent_size (float) Parent font size in points.
16287  	  * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
16288  	  * @return float value in points
16289  	  * @public
16290  	  */
16291  	public function getHTMLFontUnits($val, $refsize=12, $parent_size=12, $defaultunit='pt') {
16292  	 	 $refsize = TCPDF_FONTS::getFontRefSize($refsize);
16293  	 	 $parent_size = TCPDF_FONTS::getFontRefSize($parent_size, $refsize);
16294  	 	 switch ($val) {
16295  	 	 	 case 'xx-small': {
16296  	 	 	 	 $size = ($refsize - 4);
16297  	 	 	 	 break;
16298  	 	 	 }
16299  	 	 	 case 'x-small': {
16300  	 	 	 	 $size = ($refsize - 3);
16301  	 	 	 	 break;
16302  	 	 	 }
16303  	 	 	 case 'small': {
16304  	 	 	 	 $size = ($refsize - 2);
16305  	 	 	 	 break;
16306  	 	 	 }
16307  	 	 	 case 'medium': {
16308  	 	 	 	 $size = $refsize;
16309  	 	 	 	 break;
16310  	 	 	 }
16311  	 	 	 case 'large': {
16312  	 	 	 	 $size = ($refsize + 2);
16313  	 	 	 	 break;
16314  	 	 	 }
16315  	 	 	 case 'x-large': {
16316  	 	 	 	 $size = ($refsize + 4);
16317  	 	 	 	 break;
16318  	 	 	 }
16319  	 	 	 case 'xx-large': {
16320  	 	 	 	 $size = ($refsize + 6);
16321  	 	 	 	 break;
16322  	 	 	 }
16323  	 	 	 case 'smaller': {
16324  	 	 	 	 $size = ($parent_size - 3);
16325  	 	 	 	 break;
16326  	 	 	 }
16327  	 	 	 case 'larger': {
16328  	 	 	 	 $size = ($parent_size + 3);
16329  	 	 	 	 break;
16330  	 	 	 }
16331  	 	 	 default: {
16332  	 	 	 	 $size = $this->getHTMLUnitToUnits($val, $parent_size, $defaultunit, true);
16333  	 	 	 }
16334  	 	 }
16335  	 	 return $size;
16336  	 }
16337  
16338  	 /**
16339  	  * Returns the HTML DOM array.
16340  	  * @param $html (string) html code
16341  	  * @return array
16342  	  * @protected
16343  	  * @since 3.2.000 (2008-06-20)
16344  	  */
16345  	protected function getHtmlDomArray($html) {
16346  	 	 // array of CSS styles ( selector => properties).
16347  	 	 $css = array();
16348  	 	 // get CSS array defined at previous call
16349  	 	 $matches = array();
16350  	 	 if (preg_match_all('/<cssarray>([^\<]*)<\/cssarray>/isU', $html, $matches) > 0) {
16351  	 	 	 if (isset($matches[1][0])) {
16352  	 	 	 	 $css = array_merge($css, json_decode($this->unhtmlentities($matches[1][0]), true));
16353  	 	 	 }
16354  	 	 	 $html = preg_replace('/<cssarray>(.*?)<\/cssarray>/isU', '', $html);
16355  	 	 }
16356  	 	 // extract external CSS files
16357  	 	 $matches = array();
16358  	 	 if (preg_match_all('/<link([^\>]*)>/isU', $html, $matches) > 0) {
16359  	 	 	 foreach ($matches[1] as $key => $link) {
16360  	 	 	 	 $type = array();
16361  	 	 	 	 if (preg_match('/type[\s]*=[\s]*"text\/css"/', $link, $type)) {
16362  	 	 	 	 	 $type = array();
16363  	 	 	 	 	 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $link, $type);
16364  	 	 	 	 	 // get 'all' and 'print' media, other media types are discarded
16365  	 	 	 	 	 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16366  	 	 	 	 	 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16367  	 	 	 	 	 	 $type = array();
16368  	 	 	 	 	 	 if (preg_match('/href[\s]*=[\s]*"([^"]*)"/', $link, $type) > 0) {
16369  	 	 	 	 	 	 	 // read CSS data file
16370                              $cssdata = $this->getCachedFileContents(trim($type[1]));
16371  	 	 	 	 	 	 	 if (($cssdata !== FALSE) AND (strlen($cssdata) > 0)) {
16372  	 	 	 	 	 	 	 	 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16373  	 	 	 	 	 	 	 }
16374  	 	 	 	 	 	 }
16375  	 	 	 	 	 }
16376  	 	 	 	 }
16377  	 	 	 }
16378  	 	 }
16379  	 	 // extract style tags
16380  	 	 $matches = array();
16381  	 	 if (preg_match_all('/<style([^\>]*)>([^\<]*)<\/style>/isU', $html, $matches) > 0) {
16382  	 	 	 foreach ($matches[1] as $key => $media) {
16383  	 	 	 	 $type = array();
16384  	 	 	 	 preg_match('/media[\s]*=[\s]*"([^"]*)"/', $media, $type);
16385  	 	 	 	 // get 'all' and 'print' media, other media types are discarded
16386  	 	 	 	 // (all, braille, embossed, handheld, print, projection, screen, speech, tty, tv)
16387  	 	 	 	 if (empty($type) OR (isset($type[1]) AND (($type[1] == 'all') OR ($type[1] == 'print')))) {
16388  	 	 	 	 	 $cssdata = $matches[2][$key];
16389  	 	 	 	 	 $css = array_merge($css, TCPDF_STATIC::extractCSSproperties($cssdata));
16390  	 	 	 	 }
16391  	 	 	 }
16392  	 	 }
16393  	 	 // create a special tag to contain the CSS array (used for table content)
16394  	 	 $csstagarray = '<cssarray>'.htmlentities(json_encode($css)).'</cssarray>';
16395  	 	 // remove head and style blocks
16396  	 	 $html = preg_replace('/<head([^\>]*)>(.*)?<\/head>/siU', '', $html);
16397  	 	 $html = preg_replace('/<style([^\>]*)>([^\<]*)<\/style>/isU', '', $html);
16398  	 	 // define block tags
16399  	 	 $blocktags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table','tr','td');
16400  	 	 // define self-closing tags
16401  	 	 $selfclosingtags = array('area','base','basefont','br','hr','input','img','link','meta');
16402  	 	 // remove all unsupported tags (the line below lists all supported tags)
16403  	 	 $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><hr/><i><img><input><label><li><ol><option><p><pre><s><select><small><span><strike><strong><sub><sup><table><tablehead><tcpdf><td><textarea><th><thead><tr><tt><u><ul>');
16404  	 	 //replace some blank characters
16405  	 	 $html = preg_replace('/<pre/', '<xre', $html); // preserve pre tag
16406  	 	 $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);
16407  	 	 $html = preg_replace('@(\r\n|\r)@', "\n", $html);
16408  	 	 $repTable = array("\t" => ' ', "\0" => ' ', "\x0B" => ' ', "\\" => "\\\\");
16409  	 	 $html = strtr($html, $repTable);
16410  	 	 $offset = 0;
16411  	 	 while (($offset < strlen($html)) AND ($pos = strpos($html, '</pre>', $offset)) !== false) {
16412  	 	 	 $html_a = substr($html, 0, $offset);
16413  	 	 	 $html_b = substr($html, $offset, ($pos - $offset + 6));
16414  	 	 	 while (preg_match("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", $html_b)) {
16415  	 	 	 	 // preserve newlines on <pre> tag
16416  	 	 	 	 $html_b = preg_replace("'<xre([^\>]*)>(.*?)\n(.*?)</pre>'si", "<xre\\1>\\2<br />\\3</pre>", $html_b);
16417  	 	 	 }
16418  	 	 	 while (preg_match("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], $html_b)) {
16419  	 	 	 	 // preserve spaces on <pre> tag
16420  	 	 	 	 $html_b = preg_replace("'<xre([^\>]*)>(.*?)".$this->re_space['p']."(.*?)</pre>'".$this->re_space['m'], "<xre\\1>\\2&nbsp;\\3</pre>", $html_b);
16421  	 	 	 }
16422  	 	 	 $html = $html_a.$html_b.substr($html, $pos + 6);
16423  	 	 	 $offset = strlen($html_a.$html_b);
16424  	 	 }
16425  	 	 $offset = 0;
16426  	 	 while (($offset < strlen($html)) AND ($pos = strpos($html, '</textarea>', $offset)) !== false) {
16427  	 	 	 $html_a = substr($html, 0, $offset);
16428  	 	 	 $html_b = substr($html, $offset, ($pos - $offset + 11));
16429  	 	 	 while (preg_match("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", $html_b)) {
16430  	 	 	 	 // preserve newlines on <textarea> tag
16431  	 	 	 	 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)\n(.*?)</textarea>'si", "<textarea\\1>\\2<TBR>\\3</textarea>", $html_b);
16432  	 	 	 	 $html_b = preg_replace("'<textarea([^\>]*)>(.*?)[\"](.*?)</textarea>'si", "<textarea\\1>\\2''\\3</textarea>", $html_b);
16433  	 	 	 }
16434  	 	 	 $html = $html_a.$html_b.substr($html, $pos + 11);
16435  	 	 	 $offset = strlen($html_a.$html_b);
16436  	 	 }
16437  	 	 $html = preg_replace('/([\s]*)<option/si', '<option', $html);
16438  	 	 $html = preg_replace('/<\/option>([\s]*)/si', '</option>', $html);
16439  	 	 $offset = 0;
16440  	 	 while (($offset < strlen($html)) AND ($pos = strpos($html, '</option>', $offset)) !== false) {
16441  	 	 	 $html_a = substr($html, 0, $offset);
16442  	 	 	 $html_b = substr($html, $offset, ($pos - $offset + 9));
16443  	 	 	 while (preg_match("'<option([^\>]*)>(.*?)</option>'si", $html_b)) {
16444  	 	 	 	 $html_b = preg_replace("'<option([\s]+)value=\"([^\"]*)\"([^\>]*)>(.*?)</option>'si", "\\2#!TaB!#\\4#!NwL!#", $html_b);
16445  	 	 	 	 $html_b = preg_replace("'<option([^\>]*)>(.*?)</option>'si", "\\2#!NwL!#", $html_b);
16446  	 	 	 }
16447  	 	 	 $html = $html_a.$html_b.substr($html, $pos + 9);
16448  	 	 	 $offset = strlen($html_a.$html_b);
16449  	 	 }
16450  	 	 if (preg_match("'</select'si", $html)) {
16451  	 	 	 $html = preg_replace("'<select([^\>]*)>'si", "<select\\1 opt=\"", $html);
16452  	 	 	 $html = preg_replace("'#!NwL!#</select>'si", "\" />", $html);
16453  	 	 }
16454  	 	 $html = str_replace("\n", ' ', $html);
16455  	 	 // restore textarea newlines
16456  	 	 $html = str_replace('<TBR>', "\n", $html);
16457  	 	 // remove extra spaces from code
16458  	 	 $html = preg_replace('/[\s]+<\/(table|tr|ul|ol|dl)>/', '</\\1>', $html);
16459  	 	 $html = preg_replace('/'.$this->re_space['p'].'+<\/(td|th|li|dt|dd)>/'.$this->re_space['m'], '</\\1>', $html);
16460  	 	 $html = preg_replace('/[\s]+<(tr|td|th|li|dt|dd)/', '<\\1', $html);
16461  	 	 $html = preg_replace('/'.$this->re_space['p'].'+<(ul|ol|dl|br)/'.$this->re_space['m'], '<\\1', $html);
16462  	 	 $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);
16463  	 	 $html = preg_replace('/<\/(td|th)>/', '<marker style="font-size:0"/></\\1>', $html);
16464  	 	 $html = preg_replace('/<\/table>([\s]*)<marker style="font-size:0"\/>/', '</table>', $html);
16465  	 	 $html = preg_replace('/'.$this->re_space['p'].'+<img/'.$this->re_space['m'], chr(32).'<img', $html);
16466  	 	 $html = preg_replace('/<img([^\>]*)>[\s]+([^\<])/xi', '<img\\1>&nbsp;\\2', $html);
16467  	 	 $html = preg_replace('/<img([^\>]*)>/xi', '<img\\1><span><marker style="font-size:0"/></span>', $html);
16468  	 	 $html = preg_replace('/<xre/', '<pre', $html); // restore pre tag
16469  	 	 $html = preg_replace('/<textarea([^\>]*)>([^\<]*)<\/textarea>/xi', '<textarea\\1 value="\\2" />', $html);
16470  	 	 $html = preg_replace('/<li([^\>]*)><\/li>/', '<li\\1>&nbsp;</li>', $html);
16471  	 	 $html = preg_replace('/<li([^\>]*)>'.$this->re_space['p'].'*<img/'.$this->re_space['m'], '<li\\1><font size="1">&nbsp;</font><img', $html);
16472  	 	 $html = preg_replace('/<([^\>\/]*)>[\s]/', '<\\1>&nbsp;', $html); // preserve some spaces
16473  	 	 $html = preg_replace('/[\s]<\/([^\>]*)>/', '&nbsp;</\\1>', $html); // preserve some spaces
16474  	 	 $html = preg_replace('/<su([bp])/', '<zws/><su\\1', $html); // fix sub/sup alignment
16475  	 	 $html = preg_replace('/<\/su([bp])>/', '</su\\1><zws/>', $html); // fix sub/sup alignment
16476  	 	 $html = preg_replace('/'.$this->re_space['p'].'+/'.$this->re_space['m'], chr(32), $html); // replace multiple spaces with a single space
16477  	 	 // trim string
16478  	 	 $html = $this->stringTrim($html);
16479  	 	 // fix br tag after li
16480  	 	 $html = preg_replace('/<li><br([^\>]*)>/', '<li> <br\\1>', $html);
16481  	 	 // fix first image tag alignment
16482  	 	 $html = preg_replace('/^<img/', '<span style="font-size:0"><br /></span> <img', $html, 1);
16483  	 	 // pattern for generic tag
16484  	 	 $tagpattern = '/(<[^>]+>)/';
16485  	 	 // explodes the string
16486  	 	 $a = preg_split($tagpattern, $html, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
16487  	 	 // count elements
16488  	 	 $maxel = count($a);
16489  	 	 $elkey = 0;
16490  	 	 $key = 0;
16491  	 	 // create an array of elements
16492  	 	 $dom = array();
16493  	 	 $dom[$key] = array();
16494  	 	 // set inheritable properties fot the first void element
16495  	 	 // possible inheritable properties are: azimuth, border-collapse, border-spacing, caption-side, color, cursor, direction, empty-cells, font, font-family, font-stretch, font-size, font-size-adjust, font-style, font-variant, font-weight, letter-spacing, line-height, list-style, list-style-image, list-style-position, list-style-type, orphans, page, page-break-inside, quotes, speak, speak-header, text-align, text-indent, text-transform, volume, white-space, widows, word-spacing
16496  	 	 $dom[$key]['tag'] = false;
16497  	 	 $dom[$key]['block'] = false;
16498  	 	 $dom[$key]['value'] = '';
16499  	 	 $dom[$key]['parent'] = 0;
16500  	 	 $dom[$key]['hide'] = false;
16501  	 	 $dom[$key]['fontname'] = $this->FontFamily;
16502  	 	 $dom[$key]['fontstyle'] = $this->FontStyle;
16503  	 	 $dom[$key]['fontsize'] = $this->FontSizePt;
16504  	 	 $dom[$key]['font-stretch'] = $this->font_stretching;
16505  	 	 $dom[$key]['letter-spacing'] = $this->font_spacing;
16506  	 	 $dom[$key]['stroke'] = $this->textstrokewidth;
16507  	 	 $dom[$key]['fill'] = (($this->textrendermode % 2) == 0);
16508  	 	 $dom[$key]['clip'] = ($this->textrendermode > 3);
16509  	 	 $dom[$key]['line-height'] = $this->cell_height_ratio;
16510  	 	 $dom[$key]['bgcolor'] = false;
16511  	 	 $dom[$key]['fgcolor'] = $this->fgcolor; // color
16512  	 	 $dom[$key]['strokecolor'] = $this->strokecolor;
16513  	 	 $dom[$key]['align'] = '';
16514  	 	 $dom[$key]['listtype'] = '';
16515  	 	 $dom[$key]['text-indent'] = 0;
16516  	 	 $dom[$key]['text-transform'] = '';
16517  	 	 $dom[$key]['border'] = array();
16518  	 	 $dom[$key]['dir'] = $this->rtl?'rtl':'ltr';
16519  	 	 $thead = false; // true when we are inside the THEAD tag
16520  	 	 ++$key;
16521  	 	 $level = array();
16522  	 	 array_push($level, 0); // root
16523  	 	 while ($elkey < $maxel) {
16524  	 	 	 $dom[$key] = array();
16525  	 	 	 $element = $a[$elkey];
16526  	 	 	 $dom[$key]['elkey'] = $elkey;
16527  	 	 	 if (preg_match($tagpattern, $element)) {
16528  	 	 	 	 // html tag
16529  	 	 	 	 $element = substr($element, 1, -1);
16530  	 	 	 	 // get tag name
16531  	 	 	 	 preg_match('/[\/]?([a-zA-Z0-9]*)/', $element, $tag);
16532  	 	 	 	 $tagname = strtolower($tag[1]);
16533  	 	 	 	 // check if we are inside a table header
16534  	 	 	 	 if ($tagname == 'thead') {
16535  	 	 	 	 	 if ($element[0] == '/') {
16536  	 	 	 	 	 	 $thead = false;
16537  	 	 	 	 	 } else {
16538  	 	 	 	 	 	 $thead = true;
16539  	 	 	 	 	 }
16540  	 	 	 	 	 ++$elkey;
16541  	 	 	 	 	 continue;
16542  	 	 	 	 }
16543  	 	 	 	 $dom[$key]['tag'] = true;
16544  	 	 	 	 $dom[$key]['value'] = $tagname;
16545  	 	 	 	 if (in_array($dom[$key]['value'], $blocktags)) {
16546  	 	 	 	 	 $dom[$key]['block'] = true;
16547  	 	 	 	 } else {
16548  	 	 	 	 	 $dom[$key]['block'] = false;
16549  	 	 	 	 }
16550  	 	 	 	 if ($element[0] == '/') {
16551  	 	 	 	 	 // *** closing html tag
16552  	 	 	 	 	 $dom[$key]['opening'] = false;
16553  	 	 	 	 	 $dom[$key]['parent'] = end($level);
16554  	 	 	 	 	 array_pop($level);
16555  	 	 	 	 	 $dom[$key]['hide'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['hide'];
16556  	 	 	 	 	 $dom[$key]['fontname'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontname'];
16557  	 	 	 	 	 $dom[$key]['fontstyle'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontstyle'];
16558  	 	 	 	 	 $dom[$key]['fontsize'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fontsize'];
16559  	 	 	 	 	 $dom[$key]['font-stretch'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['font-stretch'];
16560  	 	 	 	 	 $dom[$key]['letter-spacing'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['letter-spacing'];
16561  	 	 	 	 	 $dom[$key]['stroke'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['stroke'];
16562  	 	 	 	 	 $dom[$key]['fill'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fill'];
16563  	 	 	 	 	 $dom[$key]['clip'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['clip'];
16564  	 	 	 	 	 $dom[$key]['line-height'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['line-height'];
16565  	 	 	 	 	 $dom[$key]['bgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['bgcolor'];
16566  	 	 	 	 	 $dom[$key]['fgcolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['fgcolor'];
16567  	 	 	 	 	 $dom[$key]['strokecolor'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['strokecolor'];
16568  	 	 	 	 	 $dom[$key]['align'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['align'];
16569  	 	 	 	 	 $dom[$key]['text-transform'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['text-transform'];
16570  	 	 	 	 	 $dom[$key]['dir'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['dir'];
16571  	 	 	 	 	 if (isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'])) {
16572  	 	 	 	 	 	 $dom[$key]['listtype'] = $dom[($dom[($dom[$key]['parent'])]['parent'])]['listtype'];
16573  	 	 	 	 	 }
16574  	 	 	 	 	 // set the number of columns in table tag
16575  	 	 	 	 	 if (($dom[$key]['value'] == 'tr') AND (!isset($dom[($dom[($dom[$key]['parent'])]['parent'])]['cols']))) {
16576  	 	 	 	 	 	 $dom[($dom[($dom[$key]['parent'])]['parent'])]['cols'] = $dom[($dom[$key]['parent'])]['cols'];
16577  	 	 	 	 	 }
16578  	 	 	 	 	 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
16579  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['content'] = $csstagarray;
16580  	 	 	 	 	 	 for ($i = ($dom[$key]['parent'] + 1); $i < $key; ++$i) {
16581  	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['content'] .= stripslashes($a[$dom[$i]['elkey']]);
16582  	 	 	 	 	 	 }
16583  	 	 	 	 	 	 $key = $i;
16584  	 	 	 	 	 	 // mark nested tables
16585  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['content'] = str_replace('<table', '<table nested="true"', $dom[($dom[$key]['parent'])]['content']);
16586  	 	 	 	 	 	 // remove thead sections from nested tables
16587  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['content'] = str_replace('<thead>', '', $dom[($dom[$key]['parent'])]['content']);
16588  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['content'] = str_replace('</thead>', '', $dom[($dom[$key]['parent'])]['content']);
16589  	 	 	 	 	 }
16590  	 	 	 	 	 // store header rows on a new table
16591  	 	 	 	 	 if (
16592  	 	 	 	 	 	 ($dom[$key]['value'] === 'tr')
16593  	 	 	 	 	 	 && !empty($dom[($dom[$key]['parent'])]['thead'])
16594  	 	 	 	 	 	 && ($dom[($dom[$key]['parent'])]['thead'] === true)
16595  	 	 	 	 	 ) {
16596  	 	 	 	 	 	 if (TCPDF_STATIC::empty_string($dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'])) {
16597  	 	 	 	 	 	 	 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] = $csstagarray.$a[$dom[($dom[($dom[$key]['parent'])]['parent'])]['elkey']];
16598  	 	 	 	 	 	 }
16599  	 	 	 	 	 	 for ($i = $dom[$key]['parent']; $i <= $key; ++$i) {
16600  	 	 	 	 	 	 	 $dom[($dom[($dom[$key]['parent'])]['parent'])]['thead'] .= $a[$dom[$i]['elkey']];
16601  	 	 	 	 	 	 }
16602  	 	 	 	 	 	 if (!isset($dom[($dom[$key]['parent'])]['attribute'])) {
16603  	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['attribute'] = array();
16604  	 	 	 	 	 	 }
16605  	 	 	 	 	 	 // header elements must be always contained in a single page
16606  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['attribute']['nobr'] = 'true';
16607  	 	 	 	 	 }
16608  	 	 	 	 	 if (($dom[$key]['value'] == 'table') AND (!TCPDF_STATIC::empty_string($dom[($dom[$key]['parent'])]['thead']))) {
16609  	 	 	 	 	 	 // remove the nobr attributes from the table header
16610  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['thead'] = str_replace(' nobr="true"', '', $dom[($dom[$key]['parent'])]['thead']);
16611  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['thead'] .= '</tablehead>';
16612  	 	 	 	 	 }
16613  	 	 	 	 } else {
16614  	 	 	 	 	 // *** opening or self-closing html tag
16615  	 	 	 	 	 $dom[$key]['opening'] = true;
16616  	 	 	 	 	 $dom[$key]['parent'] = end($level);
16617  	 	 	 	 	 if ((substr($element, -1, 1) == '/') OR (in_array($dom[$key]['value'], $selfclosingtags))) {
16618  	 	 	 	 	 	 // self-closing tag
16619  	 	 	 	 	 	 $dom[$key]['self'] = true;
16620  	 	 	 	 	 } else {
16621  	 	 	 	 	 	 // opening tag
16622  	 	 	 	 	 	 array_push($level, $key);
16623  	 	 	 	 	 	 $dom[$key]['self'] = false;
16624  	 	 	 	 	 }
16625  	 	 	 	 	 // copy some values from parent
16626  	 	 	 	 	 $parentkey = 0;
16627  	 	 	 	 	 if ($key > 0) {
16628  	 	 	 	 	 	 $parentkey = $dom[$key]['parent'];
16629  	 	 	 	 	 	 $dom[$key]['hide'] = $dom[$parentkey]['hide'];
16630  	 	 	 	 	 	 $dom[$key]['fontname'] = $dom[$parentkey]['fontname'];
16631  	 	 	 	 	 	 $dom[$key]['fontstyle'] = $dom[$parentkey]['fontstyle'];
16632  	 	 	 	 	 	 $dom[$key]['fontsize'] = $dom[$parentkey]['fontsize'];
16633  	 	 	 	 	 	 $dom[$key]['font-stretch'] = $dom[$parentkey]['font-stretch'];
16634  	 	 	 	 	 	 $dom[$key]['letter-spacing'] = $dom[$parentkey]['letter-spacing'];
16635  	 	 	 	 	 	 $dom[$key]['stroke'] = $dom[$parentkey]['stroke'];
16636  	 	 	 	 	 	 $dom[$key]['fill'] = $dom[$parentkey]['fill'];
16637  	 	 	 	 	 	 $dom[$key]['clip'] = $dom[$parentkey]['clip'];
16638  	 	 	 	 	 	 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16639  	 	 	 	 	 	 $dom[$key]['bgcolor'] = $dom[$parentkey]['bgcolor'];
16640  	 	 	 	 	 	 $dom[$key]['fgcolor'] = $dom[$parentkey]['fgcolor'];
16641  	 	 	 	 	 	 $dom[$key]['strokecolor'] = $dom[$parentkey]['strokecolor'];
16642  	 	 	 	 	 	 $dom[$key]['align'] = $dom[$parentkey]['align'];
16643  	 	 	 	 	 	 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16644  	 	 	 	 	 	 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16645  	 	 	 	 	 	 $dom[$key]['text-transform'] = $dom[$parentkey]['text-transform'];
16646  	 	 	 	 	 	 $dom[$key]['border'] = array();
16647  	 	 	 	 	 	 $dom[$key]['dir'] = $dom[$parentkey]['dir'];
16648  	 	 	 	 	 }
16649  	 	 	 	 	 // get attributes
16650  	 	 	 	 	 preg_match_all('/([^=\s]*)[\s]*=[\s]*"([^"]*)"/', $element, $attr_array, PREG_PATTERN_ORDER);
16651  	 	 	 	 	 $dom[$key]['attribute'] = array(); // reset attribute array
16652                      foreach($attr_array[1] as $id => $name) {
16653                          $dom[$key]['attribute'][strtolower($name)] = $attr_array[2][$id];
16654                      }
16655  	 	 	 	 	 if (!empty($css)) {
16656  	 	 	 	 	 	 // merge CSS style to current style
16657  	 	 	 	 	 	 list($dom[$key]['csssel'], $dom[$key]['cssdata']) = TCPDF_STATIC::getCSSdataArray($dom, $key, $css);
16658  	 	 	 	 	 	 $dom[$key]['attribute']['style'] = TCPDF_STATIC::getTagStyleFromCSSarray($dom[$key]['cssdata']);
16659  	 	 	 	 	 }
16660  	 	 	 	 	 // split style attributes
16661  	 	 	 	 	 if (isset($dom[$key]['attribute']['style']) AND !empty($dom[$key]['attribute']['style'])) {
16662  	 	 	 	 	 	 // get style attributes
16663  	 	 	 	 	 	 preg_match_all('/([^;:\s]*):([^;]*)/', $dom[$key]['attribute']['style'], $style_array, PREG_PATTERN_ORDER);
16664  	 	 	 	 	 	 $dom[$key]['style'] = array(); // reset style attribute array
16665                          foreach($style_array[1] as $id => $name) {
16666                              // in case of duplicate attribute the last replace the previous
16667                              $dom[$key]['style'][strtolower($name)] = trim($style_array[2][$id]);
16668                          }
16669  	 	 	 	 	 	 // --- get some style attributes ---
16670  	 	 	 	 	 	 // text direction
16671  	 	 	 	 	 	 if (isset($dom[$key]['style']['direction'])) {
16672  	 	 	 	 	 	 	 $dom[$key]['dir'] = $dom[$key]['style']['direction'];
16673  	 	 	 	 	 	 }
16674  	 	 	 	 	 	 // display
16675  	 	 	 	 	 	 if (isset($dom[$key]['style']['display'])) {
16676  	 	 	 	 	 	 	 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['style']['display'])) == 'none');
16677  	 	 	 	 	 	 }
16678  	 	 	 	 	 	 // font family
16679  	 	 	 	 	 	 if (isset($dom[$key]['style']['font-family'])) {
16680  	 	 	 	 	 	 	 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['style']['font-family']);
16681  	 	 	 	 	 	 }
16682  	 	 	 	 	 	 // list-style-type
16683  	 	 	 	 	 	 if (isset($dom[$key]['style']['list-style-type'])) {
16684  	 	 	 	 	 	 	 $dom[$key]['listtype'] = trim(strtolower($dom[$key]['style']['list-style-type']));
16685  	 	 	 	 	 	 	 if ($dom[$key]['listtype'] == 'inherit') {
16686  	 	 	 	 	 	 	 	 $dom[$key]['listtype'] = $dom[$parentkey]['listtype'];
16687  	 	 	 	 	 	 	 }
16688  	 	 	 	 	 	 }
16689  	 	 	 	 	 	 // text-indent
16690  	 	 	 	 	 	 if (isset($dom[$key]['style']['text-indent'])) {
16691  	 	 	 	 	 	 	 $dom[$key]['text-indent'] = $this->getHTMLUnitToUnits($dom[$key]['style']['text-indent']);
16692  	 	 	 	 	 	 	 if ($dom[$key]['text-indent'] == 'inherit') {
16693  	 	 	 	 	 	 	 	 $dom[$key]['text-indent'] = $dom[$parentkey]['text-indent'];
16694  	 	 	 	 	 	 	 }
16695  	 	 	 	 	 	 }
16696  	 	 	 	 	 	 // text-transform
16697  	 	 	 	 	 	 if (isset($dom[$key]['style']['text-transform'])) {
16698  	 	 	 	 	 	 	 $dom[$key]['text-transform'] = $dom[$key]['style']['text-transform'];
16699  	 	 	 	 	 	 }
16700  	 	 	 	 	 	 // font size
16701  	 	 	 	 	 	 if (isset($dom[$key]['style']['font-size'])) {
16702  	 	 	 	 	 	 	 $fsize = trim($dom[$key]['style']['font-size']);
16703  	 	 	 	 	 	 	 $dom[$key]['fontsize'] = $this->getHTMLFontUnits($fsize, $dom[0]['fontsize'], $dom[$parentkey]['fontsize'], 'pt');
16704  	 	 	 	 	 	 }
16705  	 	 	 	 	 	 // font-stretch
16706  	 	 	 	 	 	 if (isset($dom[$key]['style']['font-stretch'])) {
16707  	 	 	 	 	 	 	 $dom[$key]['font-stretch'] = $this->getCSSFontStretching($dom[$key]['style']['font-stretch'], $dom[$parentkey]['font-stretch']);
16708  	 	 	 	 	 	 }
16709  	 	 	 	 	 	 // letter-spacing
16710  	 	 	 	 	 	 if (isset($dom[$key]['style']['letter-spacing'])) {
16711  	 	 	 	 	 	 	 $dom[$key]['letter-spacing'] = $this->getCSSFontSpacing($dom[$key]['style']['letter-spacing'], $dom[$parentkey]['letter-spacing']);
16712  	 	 	 	 	 	 }
16713  	 	 	 	 	 	 // line-height (internally is the cell height ratio)
16714  	 	 	 	 	 	 if (isset($dom[$key]['style']['line-height'])) {
16715  	 	 	 	 	 	 	 $lineheight = trim($dom[$key]['style']['line-height']);
16716  	 	 	 	 	 	 	 switch ($lineheight) {
16717  	 	 	 	 	 	 	 	 // A normal line height. This is default
16718  	 	 	 	 	 	 	 	 case 'normal': {
16719  	 	 	 	 	 	 	 	 	 $dom[$key]['line-height'] = $dom[0]['line-height'];
16720  	 	 	 	 	 	 	 	 	 break;
16721  	 	 	 	 	 	 	 	 }
16722  	 	 	 	 	 	 	 	 case 'inherit': {
16723  	 	 	 	 	 	 	 	 	 $dom[$key]['line-height'] = $dom[$parentkey]['line-height'];
16724  	 	 	 	 	 	 	 	 }
16725  	 	 	 	 	 	 	 	 default: {
16726  	 	 	 	 	 	 	 	 	 if (is_numeric($lineheight)) {
16727  	 	 	 	 	 	 	 	 	 	 // convert to percentage of font height
16728  	 	 	 	 	 	 	 	 	 	 $lineheight = ($lineheight * 100).'%';
16729  	 	 	 	 	 	 	 	 	 }
16730  	 	 	 	 	 	 	 	 	 $dom[$key]['line-height'] = $this->getHTMLUnitToUnits($lineheight, 1, '%', true);
16731  	 	 	 	 	 	 	 	 	 if (substr($lineheight, -1) !== '%') {
16732  	 	 	 	 	 	 	 	 	 	 if ($dom[$key]['fontsize'] <= 0) {
16733  	 	 	 	 	 	 	 	 	 	 	 $dom[$key]['line-height'] = 1;
16734  	 	 	 	 	 	 	 	 	 	 } else {
16735  	 	 	 	 	 	 	 	 	 	 	 $dom[$key]['line-height'] = (($dom[$key]['line-height'] - $this->cell_padding['T'] - $this->cell_padding['B']) / $dom[$key]['fontsize']);
16736  	 	 	 	 	 	 	 	 	 	 }
16737  	 	 	 	 	 	 	 	 	 }
16738  	 	 	 	 	 	 	 	 }
16739  	 	 	 	 	 	 	 }
16740  	 	 	 	 	 	 }
16741  	 	 	 	 	 	 // font style
16742  	 	 	 	 	 	 if (isset($dom[$key]['style']['font-weight'])) {
16743  	 	 	 	 	 	 	 if (strtolower($dom[$key]['style']['font-weight'][0]) == 'n') {
16744  	 	 	 	 	 	 	 	 if (strpos($dom[$key]['fontstyle'], 'B') !== false) {
16745  	 	 	 	 	 	 	 	 	 $dom[$key]['fontstyle'] = str_replace('B', '', $dom[$key]['fontstyle']);
16746  	 	 	 	 	 	 	 	 }
16747  	 	 	 	 	 	 	 } elseif (strtolower($dom[$key]['style']['font-weight'][0]) == 'b') {
16748  	 	 	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'B';
16749  	 	 	 	 	 	 	 }
16750  	 	 	 	 	 	 }
16751  	 	 	 	 	 	 if (isset($dom[$key]['style']['font-style']) AND (strtolower($dom[$key]['style']['font-style'][0]) == 'i')) {
16752  	 	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'I';
16753  	 	 	 	 	 	 }
16754  	 	 	 	 	 	 // font color
16755  	 	 	 	 	 	 if (isset($dom[$key]['style']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['color']))) {
16756  	 	 	 	 	 	 	 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['color'], $this->spot_colors);
16757  	 	 	 	 	 	 } elseif ($dom[$key]['value'] == 'a') {
16758  	 	 	 	 	 	 	 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
16759  	 	 	 	 	 	 }
16760  	 	 	 	 	 	 // background color
16761  	 	 	 	 	 	 if (isset($dom[$key]['style']['background-color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['style']['background-color']))) {
16762  	 	 	 	 	 	 	 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['background-color'], $this->spot_colors);
16763  	 	 	 	 	 	 }
16764  	 	 	 	 	 	 // text-decoration
16765  	 	 	 	 	 	 if (isset($dom[$key]['style']['text-decoration'])) {
16766  	 	 	 	 	 	 	 $decors = explode(' ', strtolower($dom[$key]['style']['text-decoration']));
16767  	 	 	 	 	 	 	 foreach ($decors as $dec) {
16768  	 	 	 	 	 	 	 	 $dec = trim($dec);
16769  	 	 	 	 	 	 	 	 if (!TCPDF_STATIC::empty_string($dec)) {
16770  	 	 	 	 	 	 	 	 	 if ($dec[0] == 'u') {
16771  	 	 	 	 	 	 	 	 	 	 // underline
16772  	 	 	 	 	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'U';
16773  	 	 	 	 	 	 	 	 	 } elseif ($dec[0] == 'l') {
16774  	 	 	 	 	 	 	 	 	 	 // line-through
16775  	 	 	 	 	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'D';
16776  	 	 	 	 	 	 	 	 	 } elseif ($dec[0] == 'o') {
16777  	 	 	 	 	 	 	 	 	 	 // overline
16778  	 	 	 	 	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'O';
16779  	 	 	 	 	 	 	 	 	 }
16780  	 	 	 	 	 	 	 	 }
16781  	 	 	 	 	 	 	 }
16782  	 	 	 	 	 	 } elseif ($dom[$key]['value'] == 'a') {
16783  	 	 	 	 	 	 	 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16784  	 	 	 	 	 	 }
16785  	 	 	 	 	 	 // check for width attribute
16786  	 	 	 	 	 	 if (isset($dom[$key]['style']['width'])) {
16787  	 	 	 	 	 	 	 $dom[$key]['width'] = $dom[$key]['style']['width'];
16788  	 	 	 	 	 	 }
16789  	 	 	 	 	 	 // check for height attribute
16790  	 	 	 	 	 	 if (isset($dom[$key]['style']['height'])) {
16791  	 	 	 	 	 	 	 $dom[$key]['height'] = $dom[$key]['style']['height'];
16792  	 	 	 	 	 	 }
16793  	 	 	 	 	 	 // check for text alignment
16794  	 	 	 	 	 	 if (isset($dom[$key]['style']['text-align'])) {
16795  	 	 	 	 	 	 	 $dom[$key]['align'] = strtoupper($dom[$key]['style']['text-align'][0]);
16796  	 	 	 	 	 	 }
16797  	 	 	 	 	 	 // check for CSS border properties
16798  	 	 	 	 	 	 if (isset($dom[$key]['style']['border'])) {
16799  	 	 	 	 	 	 	 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border']);
16800  	 	 	 	 	 	 	 if (!empty($borderstyle)) {
16801  	 	 	 	 	 	 	 	 $dom[$key]['border']['LTRB'] = $borderstyle;
16802  	 	 	 	 	 	 	 }
16803  	 	 	 	 	 	 }
16804  	 	 	 	 	 	 if (isset($dom[$key]['style']['border-color'])) {
16805  	 	 	 	 	 	 	 $brd_colors = preg_split('/[\s]+/', trim($dom[$key]['style']['border-color']));
16806  	 	 	 	 	 	 	 if (isset($brd_colors[3])) {
16807  	 	 	 	 	 	 	 	 $dom[$key]['border']['L']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[3], $this->spot_colors);
16808  	 	 	 	 	 	 	 }
16809  	 	 	 	 	 	 	 if (isset($brd_colors[1])) {
16810  	 	 	 	 	 	 	 	 $dom[$key]['border']['R']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[1], $this->spot_colors);
16811  	 	 	 	 	 	 	 }
16812  	 	 	 	 	 	 	 if (isset($brd_colors[0])) {
16813  	 	 	 	 	 	 	 	 $dom[$key]['border']['T']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[0], $this->spot_colors);
16814  	 	 	 	 	 	 	 }
16815  	 	 	 	 	 	 	 if (isset($brd_colors[2])) {
16816  	 	 	 	 	 	 	 	 $dom[$key]['border']['B']['color'] = TCPDF_COLORS::convertHTMLColorToDec($brd_colors[2], $this->spot_colors);
16817  	 	 	 	 	 	 	 }
16818  	 	 	 	 	 	 }
16819  	 	 	 	 	 	 if (isset($dom[$key]['style']['border-width'])) {
16820  	 	 	 	 	 	 	 $brd_widths = preg_split('/[\s]+/', trim($dom[$key]['style']['border-width']));
16821  	 	 	 	 	 	 	 if (isset($brd_widths[3])) {
16822  	 	 	 	 	 	 	 	 $dom[$key]['border']['L']['width'] = $this->getCSSBorderWidth($brd_widths[3]);
16823  	 	 	 	 	 	 	 }
16824  	 	 	 	 	 	 	 if (isset($brd_widths[1])) {
16825  	 	 	 	 	 	 	 	 $dom[$key]['border']['R']['width'] = $this->getCSSBorderWidth($brd_widths[1]);
16826  	 	 	 	 	 	 	 }
16827  	 	 	 	 	 	 	 if (isset($brd_widths[0])) {
16828  	 	 	 	 	 	 	 	 $dom[$key]['border']['T']['width'] = $this->getCSSBorderWidth($brd_widths[0]);
16829  	 	 	 	 	 	 	 }
16830  	 	 	 	 	 	 	 if (isset($brd_widths[2])) {
16831  	 	 	 	 	 	 	 	 $dom[$key]['border']['B']['width'] = $this->getCSSBorderWidth($brd_widths[2]);
16832  	 	 	 	 	 	 	 }
16833  	 	 	 	 	 	 }
16834  	 	 	 	 	 	 if (isset($dom[$key]['style']['border-style'])) {
16835  	 	 	 	 	 	 	 $brd_styles = preg_split('/[\s]+/', trim($dom[$key]['style']['border-style']));
16836  	 	 	 	 	 	 	 if (isset($brd_styles[3]) AND ($brd_styles[3]!='none')) {
16837  	 	 	 	 	 	 	 	 $dom[$key]['border']['L']['cap'] = 'square';
16838  	 	 	 	 	 	 	 	 $dom[$key]['border']['L']['join'] = 'miter';
16839  	 	 	 	 	 	 	 	 $dom[$key]['border']['L']['dash'] = $this->getCSSBorderDashStyle($brd_styles[3]);
16840  	 	 	 	 	 	 	 	 if ($dom[$key]['border']['L']['dash'] < 0) {
16841  	 	 	 	 	 	 	 	 	 $dom[$key]['border']['L'] = array();
16842  	 	 	 	 	 	 	 	 }
16843  	 	 	 	 	 	 	 }
16844  	 	 	 	 	 	 	 if (isset($brd_styles[1])) {
16845  	 	 	 	 	 	 	 	 $dom[$key]['border']['R']['cap'] = 'square';
16846  	 	 	 	 	 	 	 	 $dom[$key]['border']['R']['join'] = 'miter';
16847  	 	 	 	 	 	 	 	 $dom[$key]['border']['R']['dash'] = $this->getCSSBorderDashStyle($brd_styles[1]);
16848  	 	 	 	 	 	 	 	 if ($dom[$key]['border']['R']['dash'] < 0) {
16849  	 	 	 	 	 	 	 	 	 $dom[$key]['border']['R'] = array();
16850  	 	 	 	 	 	 	 	 }
16851  	 	 	 	 	 	 	 }
16852  	 	 	 	 	 	 	 if (isset($brd_styles[0])) {
16853  	 	 	 	 	 	 	 	 $dom[$key]['border']['T']['cap'] = 'square';
16854  	 	 	 	 	 	 	 	 $dom[$key]['border']['T']['join'] = 'miter';
16855  	 	 	 	 	 	 	 	 $dom[$key]['border']['T']['dash'] = $this->getCSSBorderDashStyle($brd_styles[0]);
16856  	 	 	 	 	 	 	 	 if ($dom[$key]['border']['T']['dash'] < 0) {
16857  	 	 	 	 	 	 	 	 	 $dom[$key]['border']['T'] = array();
16858  	 	 	 	 	 	 	 	 }
16859  	 	 	 	 	 	 	 }
16860  	 	 	 	 	 	 	 if (isset($brd_styles[2])) {
16861  	 	 	 	 	 	 	 	 $dom[$key]['border']['B']['cap'] = 'square';
16862  	 	 	 	 	 	 	 	 $dom[$key]['border']['B']['join'] = 'miter';
16863  	 	 	 	 	 	 	 	 $dom[$key]['border']['B']['dash'] = $this->getCSSBorderDashStyle($brd_styles[2]);
16864  	 	 	 	 	 	 	 	 if ($dom[$key]['border']['B']['dash'] < 0) {
16865  	 	 	 	 	 	 	 	 	 $dom[$key]['border']['B'] = array();
16866  	 	 	 	 	 	 	 	 }
16867  	 	 	 	 	 	 	 }
16868  	 	 	 	 	 	 }
16869  	 	 	 	 	 	 $cellside = array('L' => 'left', 'R' => 'right', 'T' => 'top', 'B' => 'bottom');
16870  	 	 	 	 	 	 foreach ($cellside as $bsk => $bsv) {
16871  	 	 	 	 	 	 	 if (isset($dom[$key]['style']['border-'.$bsv])) {
16872  	 	 	 	 	 	 	 	 $borderstyle = $this->getCSSBorderStyle($dom[$key]['style']['border-'.$bsv]);
16873  	 	 	 	 	 	 	 	 if (!empty($borderstyle)) {
16874  	 	 	 	 	 	 	 	 	 $dom[$key]['border'][$bsk] = $borderstyle;
16875  	 	 	 	 	 	 	 	 }
16876  	 	 	 	 	 	 	 }
16877  	 	 	 	 	 	 	 if (isset($dom[$key]['style']['border-'.$bsv.'-color'])) {
16878  	 	 	 	 	 	 	 	 $dom[$key]['border'][$bsk]['color'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['style']['border-'.$bsv.'-color'], $this->spot_colors);
16879  	 	 	 	 	 	 	 }
16880  	 	 	 	 	 	 	 if (isset($dom[$key]['style']['border-'.$bsv.'-width'])) {
16881  	 	 	 	 	 	 	 	 $dom[$key]['border'][$bsk]['width'] = $this->getCSSBorderWidth($dom[$key]['style']['border-'.$bsv.'-width']);
16882  	 	 	 	 	 	 	 }
16883  	 	 	 	 	 	 	 if (isset($dom[$key]['style']['border-'.$bsv.'-style'])) {
16884  	 	 	 	 	 	 	 	 $dom[$key]['border'][$bsk]['dash'] = $this->getCSSBorderDashStyle($dom[$key]['style']['border-'.$bsv.'-style']);
16885  	 	 	 	 	 	 	 	 if ($dom[$key]['border'][$bsk]['dash'] < 0) {
16886  	 	 	 	 	 	 	 	 	 $dom[$key]['border'][$bsk] = array();
16887  	 	 	 	 	 	 	 	 }
16888  	 	 	 	 	 	 	 }
16889  	 	 	 	 	 	 }
16890  	 	 	 	 	 	 // check for CSS padding properties
16891  	 	 	 	 	 	 if (isset($dom[$key]['style']['padding'])) {
16892  	 	 	 	 	 	 	 $dom[$key]['padding'] = $this->getCSSPadding($dom[$key]['style']['padding']);
16893  	 	 	 	 	 	 } else {
16894  	 	 	 	 	 	 	 $dom[$key]['padding'] = $this->cell_padding;
16895  	 	 	 	 	 	 }
16896  	 	 	 	 	 	 foreach ($cellside as $psk => $psv) {
16897  	 	 	 	 	 	 	 if (isset($dom[$key]['style']['padding-'.$psv])) {
16898  	 	 	 	 	 	 	 	 $dom[$key]['padding'][$psk] = $this->getHTMLUnitToUnits($dom[$key]['style']['padding-'.$psv], 0, 'px', false);
16899  	 	 	 	 	 	 	 }
16900  	 	 	 	 	 	 }
16901  	 	 	 	 	 	 // check for CSS margin properties
16902  	 	 	 	 	 	 if (isset($dom[$key]['style']['margin'])) {
16903  	 	 	 	 	 	 	 $dom[$key]['margin'] = $this->getCSSMargin($dom[$key]['style']['margin']);
16904  	 	 	 	 	 	 } else {
16905  	 	 	 	 	 	 	 $dom[$key]['margin'] = $this->cell_margin;
16906  	 	 	 	 	 	 }
16907  	 	 	 	 	 	 foreach ($cellside as $psk => $psv) {
16908  	 	 	 	 	 	 	 if (isset($dom[$key]['style']['margin-'.$psv])) {
16909  	 	 	 	 	 	 	 	 $dom[$key]['margin'][$psk] = $this->getHTMLUnitToUnits(str_replace('auto', '0', $dom[$key]['style']['margin-'.$psv]), 0, 'px', false);
16910  	 	 	 	 	 	 	 }
16911  	 	 	 	 	 	 }
16912  	 	 	 	 	 	 // check for CSS border-spacing properties
16913  	 	 	 	 	 	 if (isset($dom[$key]['style']['border-spacing'])) {
16914  	 	 	 	 	 	 	 $dom[$key]['border-spacing'] = $this->getCSSBorderMargin($dom[$key]['style']['border-spacing']);
16915  	 	 	 	 	 	 }
16916  	 	 	 	 	 	 // page-break-inside
16917  	 	 	 	 	 	 if (isset($dom[$key]['style']['page-break-inside']) AND ($dom[$key]['style']['page-break-inside'] == 'avoid')) {
16918  	 	 	 	 	 	 	 $dom[$key]['attribute']['nobr'] = 'true';
16919  	 	 	 	 	 	 }
16920  	 	 	 	 	 	 // page-break-before
16921  	 	 	 	 	 	 if (isset($dom[$key]['style']['page-break-before'])) {
16922  	 	 	 	 	 	 	 if ($dom[$key]['style']['page-break-before'] == 'always') {
16923  	 	 	 	 	 	 	 	 $dom[$key]['attribute']['pagebreak'] = 'true';
16924  	 	 	 	 	 	 	 } elseif ($dom[$key]['style']['page-break-before'] == 'left') {
16925  	 	 	 	 	 	 	 	 $dom[$key]['attribute']['pagebreak'] = 'left';
16926  	 	 	 	 	 	 	 } elseif ($dom[$key]['style']['page-break-before'] == 'right') {
16927  	 	 	 	 	 	 	 	 $dom[$key]['attribute']['pagebreak'] = 'right';
16928  	 	 	 	 	 	 	 }
16929  	 	 	 	 	 	 }
16930  	 	 	 	 	 	 // page-break-after
16931  	 	 	 	 	 	 if (isset($dom[$key]['style']['page-break-after'])) {
16932  	 	 	 	 	 	 	 if ($dom[$key]['style']['page-break-after'] == 'always') {
16933  	 	 	 	 	 	 	 	 $dom[$key]['attribute']['pagebreakafter'] = 'true';
16934  	 	 	 	 	 	 	 } elseif ($dom[$key]['style']['page-break-after'] == 'left') {
16935  	 	 	 	 	 	 	 	 $dom[$key]['attribute']['pagebreakafter'] = 'left';
16936  	 	 	 	 	 	 	 } elseif ($dom[$key]['style']['page-break-after'] == 'right') {
16937  	 	 	 	 	 	 	 	 $dom[$key]['attribute']['pagebreakafter'] = 'right';
16938  	 	 	 	 	 	 	 }
16939  	 	 	 	 	 	 }
16940  	 	 	 	 	 }
16941  	 	 	 	 	 if (isset($dom[$key]['attribute']['display'])) {
16942  	 	 	 	 	 	 $dom[$key]['hide'] = (trim(strtolower($dom[$key]['attribute']['display'])) == 'none');
16943  	 	 	 	 	 }
16944  	 	 	 	 	 if (isset($dom[$key]['attribute']['border']) AND ($dom[$key]['attribute']['border'] != 0)) {
16945  	 	 	 	 	 	 $borderstyle = $this->getCSSBorderStyle($dom[$key]['attribute']['border'].' solid black');
16946  	 	 	 	 	 	 if (!empty($borderstyle)) {
16947  	 	 	 	 	 	 	 $dom[$key]['border']['LTRB'] = $borderstyle;
16948  	 	 	 	 	 	 }
16949  	 	 	 	 	 }
16950  	 	 	 	 	 // check for font tag
16951  	 	 	 	 	 if ($dom[$key]['value'] == 'font') {
16952  	 	 	 	 	 	 // font family
16953  	 	 	 	 	 	 if (isset($dom[$key]['attribute']['face'])) {
16954  	 	 	 	 	 	 	 $dom[$key]['fontname'] = $this->getFontFamilyName($dom[$key]['attribute']['face']);
16955  	 	 	 	 	 	 }
16956  	 	 	 	 	 	 // font size
16957  	 	 	 	 	 	 if (isset($dom[$key]['attribute']['size'])) {
16958  	 	 	 	 	 	 	 if ($key > 0) {
16959  	 	 	 	 	 	 	 	 if ($dom[$key]['attribute']['size'][0] == '+') {
16960  	 	 	 	 	 	 	 	 	 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] + intval(substr($dom[$key]['attribute']['size'], 1));
16961  	 	 	 	 	 	 	 	 } elseif ($dom[$key]['attribute']['size'][0] == '-') {
16962  	 	 	 	 	 	 	 	 	 $dom[$key]['fontsize'] = $dom[($dom[$key]['parent'])]['fontsize'] - intval(substr($dom[$key]['attribute']['size'], 1));
16963  	 	 	 	 	 	 	 	 } else {
16964  	 	 	 	 	 	 	 	 	 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16965  	 	 	 	 	 	 	 	 }
16966  	 	 	 	 	 	 	 } else {
16967  	 	 	 	 	 	 	 	 $dom[$key]['fontsize'] = intval($dom[$key]['attribute']['size']);
16968  	 	 	 	 	 	 	 }
16969  	 	 	 	 	 	 }
16970  	 	 	 	 	 }
16971  	 	 	 	 	 // force natural alignment for lists
16972  	 	 	 	 	 if ((($dom[$key]['value'] == 'ul') OR ($dom[$key]['value'] == 'ol') OR ($dom[$key]['value'] == 'dl'))
16973  	 	 	 	 	 	 AND (!isset($dom[$key]['align']) OR TCPDF_STATIC::empty_string($dom[$key]['align']) OR ($dom[$key]['align'] != 'J'))) {
16974  	 	 	 	 	 	 if ($this->rtl) {
16975  	 	 	 	 	 	 	 $dom[$key]['align'] = 'R';
16976  	 	 	 	 	 	 } else {
16977  	 	 	 	 	 	 	 $dom[$key]['align'] = 'L';
16978  	 	 	 	 	 	 }
16979  	 	 	 	 	 }
16980  	 	 	 	 	 if (($dom[$key]['value'] == 'small') OR ($dom[$key]['value'] == 'sup') OR ($dom[$key]['value'] == 'sub')) {
16981  	 	 	 	 	 	 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
16982  	 	 	 	 	 	 	 $dom[$key]['fontsize'] = $dom[$key]['fontsize'] * K_SMALL_RATIO;
16983  	 	 	 	 	 	 }
16984  	 	 	 	 	 }
16985  	 	 	 	 	 if (($dom[$key]['value'] == 'strong') OR ($dom[$key]['value'] == 'b')) {
16986  	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'B';
16987  	 	 	 	 	 }
16988  	 	 	 	 	 if (($dom[$key]['value'] == 'em') OR ($dom[$key]['value'] == 'i')) {
16989  	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'I';
16990  	 	 	 	 	 }
16991  	 	 	 	 	 if ($dom[$key]['value'] == 'u') {
16992  	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'U';
16993  	 	 	 	 	 }
16994  	 	 	 	 	 if (($dom[$key]['value'] == 'del') OR ($dom[$key]['value'] == 's') OR ($dom[$key]['value'] == 'strike')) {
16995  	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'D';
16996  	 	 	 	 	 }
16997  	 	 	 	 	 if (!isset($dom[$key]['style']['text-decoration']) AND ($dom[$key]['value'] == 'a')) {
16998  	 	 	 	 	 	 $dom[$key]['fontstyle'] = $this->htmlLinkFontStyle;
16999  	 	 	 	 	 }
17000  	 	 	 	 	 if (($dom[$key]['value'] == 'pre') OR ($dom[$key]['value'] == 'tt')) {
17001  	 	 	 	 	 	 $dom[$key]['fontname'] = $this->default_monospaced_font;
17002  	 	 	 	 	 }
17003  	 	 	 	 	 if (!empty($dom[$key]['value']) AND ($dom[$key]['value'][0] == 'h') AND (intval($dom[$key]['value'][1]) > 0) AND (intval($dom[$key]['value'][1]) < 7)) {
17004  	 	 	 	 	 	 // headings h1, h2, h3, h4, h5, h6
17005  	 	 	 	 	 	 if (!isset($dom[$key]['attribute']['size']) AND !isset($dom[$key]['style']['font-size'])) {
17006  	 	 	 	 	 	 	 $headsize = (4 - intval($dom[$key]['value'][1])) * 2;
17007  	 	 	 	 	 	 	 $dom[$key]['fontsize'] = $dom[0]['fontsize'] + $headsize;
17008  	 	 	 	 	 	 }
17009  	 	 	 	 	 	 if (!isset($dom[$key]['style']['font-weight'])) {
17010  	 	 	 	 	 	 	 $dom[$key]['fontstyle'] .= 'B';
17011  	 	 	 	 	 	 }
17012  	 	 	 	 	 }
17013  	 	 	 	 	 if (($dom[$key]['value'] == 'table')) {
17014  	 	 	 	 	 	 $dom[$key]['rows'] = 0; // number of rows
17015  	 	 	 	 	 	 $dom[$key]['trids'] = array(); // IDs of TR elements
17016  	 	 	 	 	 	 $dom[$key]['thead'] = ''; // table header rows
17017  	 	 	 	 	 }
17018  	 	 	 	 	 if (($dom[$key]['value'] == 'tr')) {
17019  	 	 	 	 	 	 $dom[$key]['cols'] = 0;
17020  	 	 	 	 	 	 if ($thead) {
17021  	 	 	 	 	 	 	 $dom[$key]['thead'] = true;
17022  	 	 	 	 	 	 	 // rows on thead block are printed as a separate table
17023  	 	 	 	 	 	 } else {
17024  	 	 	 	 	 	 	 $dom[$key]['thead'] = false;
17025  	 	 	 	 	 	 	 $parent = $dom[$key]['parent'];
17026  
17027  	 	 	 	 	 	 	 if (!isset($dom[$parent]['rows'])) {
17028  	 	 	 	 	 	 	 	 $dom[$parent]['rows'] = 0;
17029  	 	 	 	 	 	 	 }
17030  	 	 	 	 	 	 	 // store the number of rows on table element
17031  	 	 	 	 	 	 	 ++$dom[$parent]['rows'];
17032  
17033  	 	 	 	 	 	 	 if (!isset($dom[$parent]['trids'])) {
17034  	 	 	 	 	 	 	 	 $dom[$parent]['trids'] = array();
17035  	 	 	 	 	 	 	 }
17036  
17037  	 	 	 	 	 	 	 // store the TR elements IDs on table element
17038  	 	 	 	 	 	 	 array_push($dom[$parent]['trids'], $key);
17039  	 	 	 	 	 	 }
17040  	 	 	 	 	 }
17041  	 	 	 	 	 if (($dom[$key]['value'] == 'th') OR ($dom[$key]['value'] == 'td')) {
17042  	 	 	 	 	 	 if (isset($dom[$key]['attribute']['colspan'])) {
17043  	 	 	 	 	 	 	 $colspan = intval($dom[$key]['attribute']['colspan']);
17044  	 	 	 	 	 	 } else {
17045  	 	 	 	 	 	 	 $colspan = 1;
17046  	 	 	 	 	 	 }
17047  	 	 	 	 	 	 $dom[$key]['attribute']['colspan'] = $colspan;
17048  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['cols'] += $colspan;
17049  	 	 	 	 	 }
17050  	 	 	 	 	 // text direction
17051  	 	 	 	 	 if (isset($dom[$key]['attribute']['dir'])) {
17052  	 	 	 	 	 	 $dom[$key]['dir'] = $dom[$key]['attribute']['dir'];
17053  	 	 	 	 	 }
17054  	 	 	 	 	 // set foreground color attribute
17055  	 	 	 	 	 if (isset($dom[$key]['attribute']['color']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['color']))) {
17056  	 	 	 	 	 	 $dom[$key]['fgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['color'], $this->spot_colors);
17057  	 	 	 	 	 } elseif (!isset($dom[$key]['style']['color']) AND ($dom[$key]['value'] == 'a')) {
17058  	 	 	 	 	 	 $dom[$key]['fgcolor'] = $this->htmlLinkColorArray;
17059  	 	 	 	 	 }
17060  	 	 	 	 	 // set background color attribute
17061  	 	 	 	 	 if (isset($dom[$key]['attribute']['bgcolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['bgcolor']))) {
17062  	 	 	 	 	 	 $dom[$key]['bgcolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['bgcolor'], $this->spot_colors);
17063  	 	 	 	 	 }
17064  	 	 	 	 	 // set stroke color attribute
17065  	 	 	 	 	 if (isset($dom[$key]['attribute']['strokecolor']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['strokecolor']))) {
17066  	 	 	 	 	 	 $dom[$key]['strokecolor'] = TCPDF_COLORS::convertHTMLColorToDec($dom[$key]['attribute']['strokecolor'], $this->spot_colors);
17067  	 	 	 	 	 }
17068  	 	 	 	 	 // check for width attribute
17069  	 	 	 	 	 if (isset($dom[$key]['attribute']['width'])) {
17070  	 	 	 	 	 	 $dom[$key]['width'] = $dom[$key]['attribute']['width'];
17071  	 	 	 	 	 }
17072  	 	 	 	 	 // check for height attribute
17073  	 	 	 	 	 if (isset($dom[$key]['attribute']['height'])) {
17074  	 	 	 	 	 	 $dom[$key]['height'] = $dom[$key]['attribute']['height'];
17075  	 	 	 	 	 }
17076  	 	 	 	 	 // check for text alignment
17077  	 	 	 	 	 if (isset($dom[$key]['attribute']['align']) AND (!TCPDF_STATIC::empty_string($dom[$key]['attribute']['align'])) AND ($dom[$key]['value'] !== 'img')) {
17078  	 	 	 	 	 	 $dom[$key]['align'] = strtoupper($dom[$key]['attribute']['align'][0]);
17079  	 	 	 	 	 }
17080  	 	 	 	 	 // check for text rendering mode (the following attributes do not exist in HTML)
17081  	 	 	 	 	 if (isset($dom[$key]['attribute']['stroke'])) {
17082  	 	 	 	 	 	 // font stroke width
17083  	 	 	 	 	 	 $dom[$key]['stroke'] = $this->getHTMLUnitToUnits($dom[$key]['attribute']['stroke'], $dom[$key]['fontsize'], 'pt', true);
17084  	 	 	 	 	 }
17085  	 	 	 	 	 if (isset($dom[$key]['attribute']['fill'])) {
17086  	 	 	 	 	 	 // font fill
17087  	 	 	 	 	 	 if ($dom[$key]['attribute']['fill'] == 'true') {
17088  	 	 	 	 	 	 	 $dom[$key]['fill'] = true;
17089  	 	 	 	 	 	 } else {
17090  	 	 	 	 	 	 	 $dom[$key]['fill'] = false;
17091  	 	 	 	 	 	 }
17092  	 	 	 	 	 }
17093  	 	 	 	 	 if (isset($dom[$key]['attribute']['clip'])) {
17094  	 	 	 	 	 	 // clipping mode
17095  	 	 	 	 	 	 if ($dom[$key]['attribute']['clip'] == 'true') {
17096  	 	 	 	 	 	 	 $dom[$key]['clip'] = true;
17097  	 	 	 	 	 	 } else {
17098  	 	 	 	 	 	 	 $dom[$key]['clip'] = false;
17099  	 	 	 	 	 	 }
17100  	 	 	 	 	 }
17101  	 	 	 	 } // end opening tag
17102  	 	 	 } else {
17103  	 	 	 	 // text
17104  	 	 	 	 $dom[$key]['tag'] = false;
17105  	 	 	 	 $dom[$key]['block'] = false;
17106  	 	 	 	 $dom[$key]['parent'] = end($level);
17107  	 	 	 	 $dom[$key]['dir'] = $dom[$dom[$key]['parent']]['dir'];
17108  	 	 	 	 if (!empty($dom[$dom[$key]['parent']]['text-transform'])) {
17109  	 	 	 	 	 // text-transform for unicode requires mb_convert_case (Multibyte String Functions)
17110  	 	 	 	 	 if (function_exists('mb_convert_case')) {
17111  	 	 	 	 	 	 $ttm = array('capitalize' => MB_CASE_TITLE, 'uppercase' => MB_CASE_UPPER, 'lowercase' => MB_CASE_LOWER);
17112  	 	 	 	 	 	 if (isset($ttm[$dom[$dom[$key]['parent']]['text-transform']])) {
17113  	 	 	 	 	 	 	 $element = mb_convert_case($element, $ttm[$dom[$dom[$key]['parent']]['text-transform']], $this->encoding);
17114  	 	 	 	 	 	 }
17115  	 	 	 	 	 } elseif (!$this->isunicode) {
17116  	 	 	 	 	 	 switch ($dom[$dom[$key]['parent']]['text-transform']) {
17117  	 	 	 	 	 	 	 case 'capitalize': {
17118  	 	 	 	 	 	 	 	 $element = ucwords(strtolower($element));
17119  	 	 	 	 	 	 	 	 break;
17120  	 	 	 	 	 	 	 }
17121  	 	 	 	 	 	 	 case 'uppercase': {
17122  	 	 	 	 	 	 	 	 $element = strtoupper($element);
17123  	 	 	 	 	 	 	 	 break;
17124  	 	 	 	 	 	 	 }
17125  	 	 	 	 	 	 	 case 'lowercase': {
17126  	 	 	 	 	 	 	 	 $element = strtolower($element);
17127  	 	 	 	 	 	 	 	 break;
17128  	 	 	 	 	 	 	 }
17129  	 	 	 	 	 	 }
17130  	 	 	 	 	 }
17131  	 	 	 	 }
17132  	 	 	 	 $dom[$key]['value'] = stripslashes($this->unhtmlentities($element));
17133  	 	 	 }
17134  	 	 	 ++$elkey;
17135  	 	 	 ++$key;
17136  	 	 }
17137  	 	 return $dom;
17138  	 }
17139  
17140  	 /**
17141  	  * Returns the string used to find spaces
17142  	  * @return string
17143  	  * @protected
17144  	  * @author Nicola Asuni
17145  	  * @since 4.8.024 (2010-01-15)
17146  	  */
17147  	protected function getSpaceString() {
17148  	 	 $spacestr = chr(32);
17149  	 	 if ($this->isUnicodeFont()) {
17150  	 	 	 $spacestr = chr(0).chr(32);
17151  	 	 }
17152  	 	 return $spacestr;
17153  	 }
17154  
17155  	 /**
17156  	  * Return an hash code used to ensure that the serialized data has been generated by this TCPDF instance.
17157  	  * @param $data (string) serialized data
17158  	  * @return string
17159  	  * @public static
17160  	  */
17161  	protected function getHashForTCPDFtagParams($data) {
17162  	 	 return md5(strlen($data).$this->file_id.$data);
17163  	 }
17164  
17165  	 /**
17166  	  * Serialize an array of parameters to be used with TCPDF tag in HTML code.
17167  	  * @param $data (array) parameters array
17168  	  * @return string containing serialized data
17169  	  * @public static
17170  	  */
17171  	public function serializeTCPDFtagParameters($data) {
17172  	 	 $encoded = urlencode(json_encode($data));
17173  	 	 return $this->getHashForTCPDFtagParams($encoded).$encoded;
17174  	 }
17175  
17176  	 /**
17177  	  * Unserialize parameters to be used with TCPDF tag in HTML code.
17178  	  * @param $data (string) serialized data
17179  	  * @return array containing unserialized data
17180  	  * @protected static
17181  	  */
17182  	protected function unserializeTCPDFtagParameters($data) {
17183  	 	 $hash = substr($data, 0, 32);
17184  	 	 $encoded = substr($data, 32);
17185  	 	 if ($hash != $this->getHashForTCPDFtagParams($encoded)) {
17186  	 	 	 $this->Error('Invalid parameters');
17187  	 	 }
17188  	 	 return json_decode(urldecode($encoded), true);
17189  	 }
17190  
17191  	 /**
17192  	  * Prints a cell (rectangular area) with optional borders, background color and html text string.
17193  	  * 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 />
17194  	  * If automatic page breaking is enabled and the cell goes beyond the limit, a page break is done before outputting.
17195  	  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17196  	  * 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
17197  	  * NOTE: all the HTML attributes must be enclosed in double-quote.
17198  	  * @param $w (float) Cell width. If 0, the cell extends up to the right margin.
17199  	  * @param $h (float) Cell minimum height. The cell extends automatically if needed.
17200  	  * @param $x (float) upper-left corner X coordinate
17201  	  * @param $y (float) upper-left corner Y coordinate
17202  	  * @param $html (string) html text to print. Default value: empty string.
17203  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
17204  	  * @param $ln (int) 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>
17205  Putting 1 is equivalent to putting 0 and calling Ln() just after. Default value: 0.
17206  	  * @param $fill (boolean) Indicates if the cell background must be painted (true) or transparent (false).
17207  	  * @param $reseth (boolean) if true reset the last cell height (default true).
17208  	  * @param $align (string) 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>
17209  	  * @param $autopadding (boolean) if true, uses internal padding and automatically adjust it to account for line width.
17210  	  * @see Multicell(), writeHTML()
17211  	  * @public
17212  	  */
17213  	public function writeHTMLCell($w, $h, $x, $y, $html='', $border=0, $ln=0, $fill=false, $reseth=true, $align='', $autopadding=true) {
17214  	 	 return $this->MultiCell($w, $h, $html, $border, $align, $fill, $ln, $x, $y, $reseth, 0, true, $autopadding, 0, 'T', false);
17215  	 }
17216  
17217  	 /**
17218  	  * Allows to preserve some HTML formatting (limited support).<br />
17219  	  * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
17220  	  * 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
17221  	  * NOTE: all the HTML attributes must be enclosed in double-quote.
17222  	  * @param $html (string) text to display
17223  	  * @param $ln (boolean) if true add a new line after text (default = true)
17224  	  * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
17225  	  * @param $reseth (boolean) if true reset the last cell height (default false).
17226  	  * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
17227  	  * @param $align (string) 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>
17228  	  * @public
17229  	  */
17230  	public function writeHTML($html, $ln=true, $fill=false, $reseth=false, $cell=false, $align='') {
17231  	 	 $gvars = $this->getGraphicVars();
17232  	 	 // store current values
17233  	 	 $prev_cell_margin = $this->cell_margin;
17234  	 	 $prev_cell_padding = $this->cell_padding;
17235  	 	 $prevPage = $this->page;
17236  	 	 $prevlMargin = $this->lMargin;
17237  	 	 $prevrMargin = $this->rMargin;
17238  	 	 $curfontname = $this->FontFamily;
17239  	 	 $curfontstyle = $this->FontStyle;
17240  	 	 $curfontsize = $this->FontSizePt;
17241  	 	 $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
17242  	 	 $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
17243  	 	 $curfontstretcing = $this->font_stretching;
17244  	 	 $curfonttracking = $this->font_spacing;
17245  	 	 $this->newline = true;
17246  	 	 $newline = true;
17247  	 	 $startlinepage = $this->page;
17248  	 	 $minstartliney = $this->y;
17249  	 	 $maxbottomliney = 0;
17250  	 	 $startlinex = $this->x;
17251  	 	 $startliney = $this->y;
17252  	 	 $yshift = 0;
17253  	 	 $loop = 0;
17254  	 	 $curpos = 0;
17255  	 	 $this_method_vars = array();
17256  	 	 $undo = false;
17257  	 	 $fontaligned = false;
17258  	 	 $reverse_dir = false; // true when the text direction is reversed
17259  	 	 $this->premode = false;
17260  	 	 if ($this->inxobj) {
17261  	 	 	 // we are inside an XObject template
17262  	 	 	 $pask = count($this->xobjects[$this->xobjid]['annotations']);
17263  	 	 } elseif (isset($this->PageAnnots[$this->page])) {
17264  	 	 	 $pask = count($this->PageAnnots[$this->page]);
17265  	 	 } else {
17266  	 	 	 $pask = 0;
17267  	 	 }
17268  	 	 if ($this->inxobj) {
17269  	 	 	 // we are inside an XObject template
17270  	 	 	 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
17271  	 	 } elseif (!$this->InFooter) {
17272  	 	 	 if (isset($this->footerlen[$this->page])) {
17273  	 	 	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
17274  	 	 	 } else {
17275  	 	 	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page];
17276  	 	 	 }
17277  	 	 	 $startlinepos = $this->footerpos[$this->page];
17278  	 	 } else {
17279  	 	 	 // we are inside the footer
17280  	 	 	 $startlinepos = $this->pagelen[$this->page];
17281  	 	 }
17282  	 	 $lalign = $align;
17283  	 	 $plalign = $align;
17284  	 	 if ($this->rtl) {
17285  	 	 	 $w = $this->x - $this->lMargin;
17286  	 	 } else {
17287  	 	 	 $w = $this->w - $this->rMargin - $this->x;
17288  	 	 }
17289  	 	 $w -= ($this->cell_padding['L'] + $this->cell_padding['R']);
17290  	 	 if ($cell) {
17291  	 	 	 if ($this->rtl) {
17292  	 	 	 	 $this->x -= $this->cell_padding['R'];
17293  	 	 	 	 $this->lMargin += $this->cell_padding['L'];
17294  	 	 	 } else {
17295  	 	 	 	 $this->x += $this->cell_padding['L'];
17296  	 	 	 	 $this->rMargin += $this->cell_padding['R'];
17297  	 	 	 }
17298  	 	 }
17299  	 	 if ($this->customlistindent >= 0) {
17300  	 	 	 $this->listindent = $this->customlistindent;
17301  	 	 } else {
17302  	 	 	 $this->listindent = $this->GetStringWidth('000000');
17303  	 	 }
17304  	 	 $this->listindentlevel = 0;
17305  	 	 // save previous states
17306  	 	 $prev_cell_height_ratio = $this->cell_height_ratio;
17307  	 	 $prev_listnum = $this->listnum;
17308  	 	 $prev_listordered = $this->listordered;
17309  	 	 $prev_listcount = $this->listcount;
17310  	 	 $prev_lispacer = $this->lispacer;
17311  	 	 $this->listnum = 0;
17312  	 	 $this->listordered = array();
17313  	 	 $this->listcount = array();
17314  	 	 $this->lispacer = '';
17315  	 	 if ((TCPDF_STATIC::empty_string($this->lasth)) OR ($reseth)) {
17316  	 	 	 // reset row height
17317  	 	 	 $this->resetLastH();
17318  	 	 }
17319  	 	 $dom = $this->getHtmlDomArray($html);
17320  	 	 $maxel = count($dom);
17321  	 	 $key = 0;
17322  	 	 while ($key < $maxel) {
17323  	 	 	 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND $dom[$key]['hide']) {
17324  	 	 	 	 // store the node key
17325  	 	 	 	 $hidden_node_key = $key;
17326  	 	 	 	 if ($dom[$key]['self']) {
17327  	 	 	 	 	 // skip just this self-closing tag
17328  	 	 	 	 	 ++$key;
17329  	 	 	 	 } else {
17330  	 	 	 	 	 // skip this and all children tags
17331  	 	 	 	 	 while (($key < $maxel) AND (!$dom[$key]['tag'] OR $dom[$key]['opening'] OR ($dom[$key]['parent'] != $hidden_node_key))) {
17332  	 	 	 	 	 	 // skip hidden objects
17333  	 	 	 	 	 	 ++$key;
17334  	 	 	 	 	 }
17335  	 	 	 	 	 ++$key;
17336  	 	 	 	 }
17337  	 	 	 }
17338  	 	 	 if ($dom[$key]['tag'] AND isset($dom[$key]['attribute']['pagebreak'])) {
17339  	 	 	 	 // check for pagebreak
17340  	 	 	 	 if (($dom[$key]['attribute']['pagebreak'] == 'true') OR ($dom[$key]['attribute']['pagebreak'] == 'left') OR ($dom[$key]['attribute']['pagebreak'] == 'right')) {
17341  	 	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17342  	 	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
17343  	 	 	 	 	 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17344  	 	 	 	 }
17345  	 	 	 	 if ((($dom[$key]['attribute']['pagebreak'] == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
17346  	 	 	 	 	 OR (($dom[$key]['attribute']['pagebreak'] == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
17347  	 	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17348  	 	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
17349  	 	 	 	 	 $this->htmlvspace = ($this->PageBreakTrigger + 1);
17350  	 	 	 	 }
17351  	 	 	 }
17352  	 	 	 if ($dom[$key]['tag'] AND $dom[$key]['opening'] AND isset($dom[$key]['attribute']['nobr']) AND ($dom[$key]['attribute']['nobr'] == 'true')) {
17353  	 	 	 	 if (isset($dom[($dom[$key]['parent'])]['attribute']['nobr']) AND ($dom[($dom[$key]['parent'])]['attribute']['nobr'] == 'true')) {
17354  	 	 	 	 	 $dom[$key]['attribute']['nobr'] = false;
17355  	 	 	 	 } else {
17356  	 	 	 	 	 // store current object
17357  	 	 	 	 	 $this->startTransaction();
17358  	 	 	 	 	 // save this method vars
17359  	 	 	 	 	 $this_method_vars['html'] = $html;
17360  	 	 	 	 	 $this_method_vars['ln'] = $ln;
17361  	 	 	 	 	 $this_method_vars['fill'] = $fill;
17362  	 	 	 	 	 $this_method_vars['reseth'] = $reseth;
17363  	 	 	 	 	 $this_method_vars['cell'] = $cell;
17364  	 	 	 	 	 $this_method_vars['align'] = $align;
17365  	 	 	 	 	 $this_method_vars['gvars'] = $gvars;
17366  	 	 	 	 	 $this_method_vars['prevPage'] = $prevPage;
17367  	 	 	 	 	 $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
17368  	 	 	 	 	 $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
17369  	 	 	 	 	 $this_method_vars['prevlMargin'] = $prevlMargin;
17370  	 	 	 	 	 $this_method_vars['prevrMargin'] = $prevrMargin;
17371  	 	 	 	 	 $this_method_vars['curfontname'] = $curfontname;
17372  	 	 	 	 	 $this_method_vars['curfontstyle'] = $curfontstyle;
17373  	 	 	 	 	 $this_method_vars['curfontsize'] = $curfontsize;
17374  	 	 	 	 	 $this_method_vars['curfontascent'] = $curfontascent;
17375  	 	 	 	 	 $this_method_vars['curfontdescent'] = $curfontdescent;
17376  	 	 	 	 	 $this_method_vars['curfontstretcing'] = $curfontstretcing;
17377  	 	 	 	 	 $this_method_vars['curfonttracking'] = $curfonttracking;
17378  	 	 	 	 	 $this_method_vars['minstartliney'] = $minstartliney;
17379  	 	 	 	 	 $this_method_vars['maxbottomliney'] = $maxbottomliney;
17380  	 	 	 	 	 $this_method_vars['yshift'] = $yshift;
17381  	 	 	 	 	 $this_method_vars['startlinepage'] = $startlinepage;
17382  	 	 	 	 	 $this_method_vars['startlinepos'] = $startlinepos;
17383  	 	 	 	 	 $this_method_vars['startlinex'] = $startlinex;
17384  	 	 	 	 	 $this_method_vars['startliney'] = $startliney;
17385  	 	 	 	 	 $this_method_vars['newline'] = $newline;
17386  	 	 	 	 	 $this_method_vars['loop'] = $loop;
17387  	 	 	 	 	 $this_method_vars['curpos'] = $curpos;
17388  	 	 	 	 	 $this_method_vars['pask'] = $pask;
17389  	 	 	 	 	 $this_method_vars['lalign'] = $lalign;
17390  	 	 	 	 	 $this_method_vars['plalign'] = $plalign;
17391  	 	 	 	 	 $this_method_vars['w'] = $w;
17392  	 	 	 	 	 $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
17393  	 	 	 	 	 $this_method_vars['prev_listnum'] = $prev_listnum;
17394  	 	 	 	 	 $this_method_vars['prev_listordered'] = $prev_listordered;
17395  	 	 	 	 	 $this_method_vars['prev_listcount'] = $prev_listcount;
17396  	 	 	 	 	 $this_method_vars['prev_lispacer'] = $prev_lispacer;
17397  	 	 	 	 	 $this_method_vars['fontaligned'] = $fontaligned;
17398  	 	 	 	 	 $this_method_vars['key'] = $key;
17399  	 	 	 	 	 $this_method_vars['dom'] = $dom;
17400  	 	 	 	 }
17401  	 	 	 }
17402  	 	 	 // print THEAD block
17403  	 	 	 if (($dom[$key]['value'] == 'tr') AND isset($dom[$key]['thead']) AND $dom[$key]['thead']) {
17404  	 	 	 	 if (isset($dom[$key]['parent']) AND isset($dom[$dom[$key]['parent']]['thead']) AND !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
17405  	 	 	 	 	 $this->inthead = true;
17406  	 	 	 	 	 // print table header (thead)
17407  	 	 	 	 	 $this->writeHTML($this->thead, false, false, false, false, '');
17408  	 	 	 	 	 // check if we are on a new page or on a new column
17409  	 	 	 	 	 if (($this->y < $this->start_transaction_y) OR ($this->checkPageBreak($this->lasth, '', false))) {
17410  	 	 	 	 	 	 // we are on a new page or on a new column and the total object height is less than the available vertical space.
17411  	 	 	 	 	 	 // restore previous object
17412  	 	 	 	 	 	 $this->rollbackTransaction(true);
17413  	 	 	 	 	 	 // restore previous values
17414  	 	 	 	 	 	 foreach ($this_method_vars as $vkey => $vval) {
17415  	 	 	 	 	 	 	 $$vkey = $vval;
17416  	 	 	 	 	 	 }
17417  	 	 	 	 	 	 // disable table header
17418  	 	 	 	 	 	 $tmp_thead = $this->thead;
17419  	 	 	 	 	 	 $this->thead = '';
17420  	 	 	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
17421  	 	 	 	 	 	 $pre_y = $this->y;
17422  	 	 	 	 	 	 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
17423  	 	 	 	 	 	 	 // fix for multicolumn mode
17424  	 	 	 	 	 	 	 $startliney = $this->y;
17425  	 	 	 	 	 	 }
17426  	 	 	 	 	 	 $this->start_transaction_page = $this->page;
17427  	 	 	 	 	 	 $this->start_transaction_y = $this->y;
17428  	 	 	 	 	 	 // restore table header
17429  	 	 	 	 	 	 $this->thead = $tmp_thead;
17430  	 	 	 	 	 	 // fix table border properties
17431  	 	 	 	 	 	 if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
17432  	 	 	 	 	 	 	 $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
17433  	 	 	 	 	 	 } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
17434  	 	 	 	 	 	 	 $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
17435  	 	 	 	 	 	 } else {
17436  	 	 	 	 	 	 	 $tmp_cellspacing = 0;
17437  	 	 	 	 	 	 }
17438  	 	 	 	 	 	 $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
17439  	 	 	 	 	 	 $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
17440  	 	 	 	 	 	 $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
17441  	 	 	 	 	 	 $xoffset = ($this->x - $dom[$dom[$key]['parent']]['borderposition']['x']);
17442  	 	 	 	 	 	 $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
17443  	 	 	 	 	 	 $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
17444  	 	 	 	 	 	 // print table header (thead)
17445  	 	 	 	 	 	 $this->writeHTML($this->thead, false, false, false, false, '');
17446  	 	 	 	 	 }
17447  	 	 	 	 }
17448  	 	 	 	 // move $key index forward to skip THEAD block
17449  	 	 	 	 while ( ($key < $maxel) AND (!(
17450  	 	 	 	 	 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'tr') AND (!isset($dom[$key]['thead']) OR !$dom[$key]['thead']))
17451  	 	 	 	 	 OR ($dom[$key]['tag'] AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == 'table'))) )) {
17452  	 	 	 	 	 ++$key;
17453  	 	 	 	 }
17454  	 	 	 }
17455  	 	 	 if ($dom[$key]['tag'] OR ($key == 0)) {
17456  	 	 	 	 if ((($dom[$key]['value'] == 'table') OR ($dom[$key]['value'] == 'tr')) AND (isset($dom[$key]['align']))) {
17457  	 	 	 	 	 $dom[$key]['align'] = ($this->rtl) ? 'R' : 'L';
17458  	 	 	 	 }
17459  	 	 	 	 // vertically align image in line
17460  	 	 	 	 if ((!$this->newline) AND ($dom[$key]['value'] == 'img') AND (isset($dom[$key]['height'])) AND ($dom[$key]['height'] > 0)) {
17461  	 	 	 	 	 // get image height
17462  	 	 	 	 	 $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], ($dom[$key]['fontsize'] / $this->k), 'px');
17463  	 	 	 	 	 $autolinebreak = false;
17464  	 	 	 	 	 if (!empty($dom[$key]['width'])) {
17465  	 	 	 	 	 	 $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], ($dom[$key]['fontsize'] / $this->k), 'px', false);
17466  	 	 	 	 	 	 if (($imgw <= ($this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R']))
17467  	 	 	 	 	 	 	 AND ((($this->rtl) AND (($this->x - $imgw) < ($this->lMargin + $this->cell_padding['L'])))
17468  	 	 	 	 	 	 	 OR ((!$this->rtl) AND (($this->x + $imgw) > ($this->w - $this->rMargin - $this->cell_padding['R']))))) {
17469  	 	 	 	 	 	 	 // add automatic line break
17470  	 	 	 	 	 	 	 $autolinebreak = true;
17471  	 	 	 	 	 	 	 $this->Ln('', $cell);
17472  	 	 	 	 	 	 	 if ((!$dom[($key-1)]['tag']) AND ($dom[($key-1)]['value'] == ' ')) {
17473  	 	 	 	 	 	 	 	 // go back to evaluate this line break
17474  	 	 	 	 	 	 	 	 --$key;
17475  	 	 	 	 	 	 	 }
17476  	 	 	 	 	 	 }
17477  	 	 	 	 	 }
17478  	 	 	 	 	 if (!$autolinebreak) {
17479  	 	 	 	 	 	 if ($this->inPageBody()) {
17480  	 	 	 	 	 	 	 $pre_y = $this->y;
17481  	 	 	 	 	 	 	 // check for page break
17482  	 	 	 	 	 	 	 if ((!$this->checkPageBreak($imgh)) AND ($this->y < $pre_y)) {
17483  	 	 	 	 	 	 	 	 // fix for multicolumn mode
17484  	 	 	 	 	 	 	 	 $startliney = $this->y;
17485  	 	 	 	 	 	 	 }
17486  	 	 	 	 	 	 }
17487  	 	 	 	 	 	 if ($this->page > $startlinepage) {
17488  	 	 	 	 	 	 	 // fix line splitted over two pages
17489  	 	 	 	 	 	 	 if (isset($this->footerlen[$startlinepage])) {
17490  	 	 	 	 	 	 	 	 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17491  	 	 	 	 	 	 	 }
17492  	 	 	 	 	 	 	 // line to be moved one page forward
17493  	 	 	 	 	 	 	 $pagebuff = $this->getPageBuffer($startlinepage);
17494  	 	 	 	 	 	 	 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17495  	 	 	 	 	 	 	 $tstart = substr($pagebuff, 0, $startlinepos);
17496  	 	 	 	 	 	 	 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17497  	 	 	 	 	 	 	 // remove line from previous page
17498  	 	 	 	 	 	 	 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17499  	 	 	 	 	 	 	 $pagebuff = $this->getPageBuffer($this->page);
17500  	 	 	 	 	 	 	 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17501  	 	 	 	 	 	 	 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17502  	 	 	 	 	 	 	 // add line start to current page
17503  	 	 	 	 	 	 	 $yshift = ($minstartliney - $this->y);
17504  	 	 	 	 	 	 	 if ($fontaligned) {
17505  	 	 	 	 	 	 	 	 $yshift += ($curfontsize / $this->k);
17506  	 	 	 	 	 	 	 }
17507  	 	 	 	 	 	 	 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17508  	 	 	 	 	 	 	 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17509  	 	 	 	 	 	 	 // shift the annotations and links
17510  	 	 	 	 	 	 	 if (isset($this->PageAnnots[$this->page])) {
17511  	 	 	 	 	 	 	 	 $next_pask = count($this->PageAnnots[$this->page]);
17512  	 	 	 	 	 	 	 } else {
17513  	 	 	 	 	 	 	 	 $next_pask = 0;
17514  	 	 	 	 	 	 	 }
17515  	 	 	 	 	 	 	 if (isset($this->PageAnnots[$startlinepage])) {
17516  	 	 	 	 	 	 	 	 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17517  	 	 	 	 	 	 	 	 	 if ($pak >= $pask) {
17518  	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][] = $pac;
17519  	 	 	 	 	 	 	 	 	 	 unset($this->PageAnnots[$startlinepage][$pak]);
17520  	 	 	 	 	 	 	 	 	 	 $npak = count($this->PageAnnots[$this->page]) - 1;
17521  	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17522  	 	 	 	 	 	 	 	 	 }
17523  	 	 	 	 	 	 	 	 }
17524  	 	 	 	 	 	 	 }
17525  	 	 	 	 	 	 	 $pask = $next_pask;
17526  	 	 	 	 	 	 	 $startlinepos = $this->cntmrk[$this->page];
17527  	 	 	 	 	 	 	 $startlinepage = $this->page;
17528  	 	 	 	 	 	 	 $startliney = $this->y;
17529  	 	 	 	 	 	 	 $this->newline = false;
17530  	 	 	 	 	 	 }
17531  	 	 	 	 	 	 $this->y += ($this->getCellHeight($curfontsize / $this->k) - ($curfontdescent * $this->cell_height_ratio) - $imgh);
17532  	 	 	 	 	 	 $minstartliney = min($this->y, $minstartliney);
17533  	 	 	 	 	 	 $maxbottomliney = ($startliney + $this->getCellHeight($curfontsize / $this->k));
17534  	 	 	 	 	 }
17535  	 	 	 	 } elseif (isset($dom[$key]['fontname']) OR isset($dom[$key]['fontstyle']) OR isset($dom[$key]['fontsize']) OR isset($dom[$key]['line-height'])) {
17536  	 	 	 	 	 // account for different font size
17537  	 	 	 	 	 $pfontname = $curfontname;
17538  	 	 	 	 	 $pfontstyle = $curfontstyle;
17539  	 	 	 	 	 $pfontsize = $curfontsize;
17540  	 	 	 	 	 $fontname = (isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname);
17541  	 	 	 	 	 $fontstyle = (isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle);
17542  	 	 	 	 	 $fontsize = (isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize);
17543  	 	 	 	 	 $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
17544  	 	 	 	 	 $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
17545  	 	 	 	 	 if (($fontname != $curfontname) OR ($fontstyle != $curfontstyle) OR ($fontsize != $curfontsize)
17546  	 	 	 	 	 	 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17547  	 	 	 	 	 	 OR ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li')) ) {
17548  	 	 	 	 	 	 if (($key < ($maxel - 1)) AND (
17549  	 	 	 	 	 	 	 	 ($dom[$key]['tag'] AND $dom[$key]['opening'] AND ($dom[$key]['value'] == 'li'))
17550  	 	 	 	 	 	 	 	 OR ($this->cell_height_ratio != $dom[$key]['line-height'])
17551  	 	 	 	 	 	 	 	 OR (!$this->newline AND is_numeric($fontsize) AND is_numeric($curfontsize)
17552  	 	 	 	 	 	 	 	 AND ($fontsize >= 0) AND ($curfontsize >= 0)
17553  	 	 	 	 	 	 	 	 AND (($fontsize != $curfontsize) OR ($fontstyle != $curfontstyle) OR ($fontname != $curfontname)))
17554  	 	 	 	 	 	 	 )) {
17555  	 	 	 	 	 	 	 if ($this->page > $startlinepage) {
17556  	 	 	 	 	 	 	 	 // fix lines splitted over two pages
17557  	 	 	 	 	 	 	 	 if (isset($this->footerlen[$startlinepage])) {
17558  	 	 	 	 	 	 	 	 	 $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17559  	 	 	 	 	 	 	 	 }
17560  	 	 	 	 	 	 	 	 // line to be moved one page forward
17561  	 	 	 	 	 	 	 	 $pagebuff = $this->getPageBuffer($startlinepage);
17562  	 	 	 	 	 	 	 	 $linebeg = substr($pagebuff, $startlinepos, ($curpos - $startlinepos));
17563  	 	 	 	 	 	 	 	 $tstart = substr($pagebuff, 0, $startlinepos);
17564  	 	 	 	 	 	 	 	 $tend = substr($this->getPageBuffer($startlinepage), $curpos);
17565  	 	 	 	 	 	 	 	 // remove line start from previous page
17566  	 	 	 	 	 	 	 	 $this->setPageBuffer($startlinepage, $tstart.''.$tend);
17567  	 	 	 	 	 	 	 	 $pagebuff = $this->getPageBuffer($this->page);
17568  	 	 	 	 	 	 	 	 $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
17569  	 	 	 	 	 	 	 	 $tend = substr($pagebuff, $this->cntmrk[$this->page]);
17570  	 	 	 	 	 	 	 	 // add line start to current page
17571  	 	 	 	 	 	 	 	 $yshift = ($minstartliney - $this->y);
17572  	 	 	 	 	 	 	 	 $try = sprintf('1 0 0 1 0 %F cm', ($yshift * $this->k));
17573  	 	 	 	 	 	 	 	 $this->setPageBuffer($this->page, $tstart."\nq\n".$try."\n".$linebeg."\nQ\n".$tend);
17574  	 	 	 	 	 	 	 	 // shift the annotations and links
17575  	 	 	 	 	 	 	 	 if (isset($this->PageAnnots[$this->page])) {
17576  	 	 	 	 	 	 	 	 	 $next_pask = count($this->PageAnnots[$this->page]);
17577  	 	 	 	 	 	 	 	 } else {
17578  	 	 	 	 	 	 	 	 	 $next_pask = 0;
17579  	 	 	 	 	 	 	 	 }
17580  	 	 	 	 	 	 	 	 if (isset($this->PageAnnots[$startlinepage])) {
17581  	 	 	 	 	 	 	 	 	 foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
17582  	 	 	 	 	 	 	 	 	 	 if ($pak >= $pask) {
17583  	 	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][] = $pac;
17584  	 	 	 	 	 	 	 	 	 	 	 unset($this->PageAnnots[$startlinepage][$pak]);
17585  	 	 	 	 	 	 	 	 	 	 	 $npak = count($this->PageAnnots[$this->page]) - 1;
17586  	 	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
17587  	 	 	 	 	 	 	 	 	 	 }
17588  	 	 	 	 	 	 	 	 	 }
17589  	 	 	 	 	 	 	 	 }
17590  	 	 	 	 	 	 	 	 $pask = $next_pask;
17591  	 	 	 	 	 	 	 	 $startlinepos = $this->cntmrk[$this->page];
17592  	 	 	 	 	 	 	 	 $startlinepage = $this->page;
17593  	 	 	 	 	 	 	 	 $startliney = $this->y;
17594  	 	 	 	 	 	 	 }
17595  	 	 	 	 	 	 	 if (!isset($dom[$key]['line-height'])) {
17596  	 	 	 	 	 	 	 	 $dom[$key]['line-height'] = $this->cell_height_ratio;
17597  	 	 	 	 	 	 	 }
17598  	 	 	 	 	 	 	 if (!$dom[$key]['block']) {
17599  	 	 	 	 	 	 	 	 if (!(isset($dom[($key + 1)]) AND $dom[($key + 1)]['tag'] AND (!$dom[($key + 1)]['opening']) AND ($dom[($key + 1)]['value'] != 'li') AND $dom[$key]['tag'] AND (!$dom[$key]['opening']))) {
17600  	 	 	 	 	 	 	 	 	 $this->y += (((($curfontsize * $this->cell_height_ratio) - ($fontsize * $dom[$key]['line-height'])) / $this->k) + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
17601  	 	 	 	 	 	 	 	 }
17602  	 	 	 	 	 	 	 	 if (($dom[$key]['value'] != 'sup') AND ($dom[$key]['value'] != 'sub')) {
17603  	 	 	 	 	 	 	 	 	 $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
17604  	 	 	 	 	 	 	 	 	 if (isset($line_align_data) AND (($line_align_data[0] == ($key - 1)) OR (($line_align_data[0] == ($key - 2)) AND (isset($dom[($key - 1)])) AND (preg_match('/^([\s]+)$/', $dom[($key - 1)]['value']) > 0)))) {
17605  	 	 	 	 	 	 	 	 	 	 $minstartliney = min($this->y, $line_align_data[1]);
17606  	 	 	 	 	 	 	 	 	 	 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $line_align_data[2]);
17607  	 	 	 	 	 	 	 	 	 } else {
17608  	 	 	 	 	 	 	 	 	 	 $minstartliney = min($this->y, $minstartliney);
17609  	 	 	 	 	 	 	 	 	 	 $maxbottomliney = max(($this->y + $this->getCellHeight($fontsize / $this->k)), $maxbottomliney);
17610  	 	 	 	 	 	 	 	 	 }
17611  	 	 	 	 	 	 	 	 	 $line_align_data = $current_line_align_data;
17612  	 	 	 	 	 	 	 	 }
17613  	 	 	 	 	 	 	 }
17614  	 	 	 	 	 	 	 $this->cell_height_ratio = $dom[$key]['line-height'];
17615  	 	 	 	 	 	 	 $fontaligned = true;
17616  	 	 	 	 	 	 }
17617  	 	 	 	 	 	 $this->SetFont($fontname, $fontstyle, $fontsize);
17618  	 	 	 	 	 	 // reset row height
17619  	 	 	 	 	 	 $this->resetLastH();
17620  	 	 	 	 	 	 $curfontname = $fontname;
17621  	 	 	 	 	 	 $curfontstyle = $fontstyle;
17622  	 	 	 	 	 	 $curfontsize = $fontsize;
17623  	 	 	 	 	 	 $curfontascent = $fontascent;
17624  	 	 	 	 	 	 $curfontdescent = $fontdescent;
17625  	 	 	 	 	 }
17626  	 	 	 	 }
17627  	 	 	 	 // set text rendering mode
17628  	 	 	 	 $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
17629  	 	 	 	 $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : (($this->textrendermode % 2) == 0);
17630  	 	 	 	 $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : ($this->textrendermode > 3);
17631  	 	 	 	 $this->setTextRenderingMode($textstroke, $textfill, $textclip);
17632  	 	 	 	 if (isset($dom[$key]['font-stretch']) AND ($dom[$key]['font-stretch'] !== false)) {
17633  	 	 	 	 	 $this->setFontStretching($dom[$key]['font-stretch']);
17634  	 	 	 	 }
17635  	 	 	 	 if (isset($dom[$key]['letter-spacing']) AND ($dom[$key]['letter-spacing'] !== false)) {
17636  	 	 	 	 	 $this->setFontSpacing($dom[$key]['letter-spacing']);
17637  	 	 	 	 }
17638  	 	 	 	 if (($plalign == 'J') AND $dom[$key]['block']) {
17639  	 	 	 	 	 $plalign = '';
17640  	 	 	 	 }
17641  	 	 	 	 // get current position on page buffer
17642  	 	 	 	 $curpos = $this->pagelen[$startlinepage];
17643  	 	 	 	 if (isset($dom[$key]['bgcolor']) AND ($dom[$key]['bgcolor'] !== false)) {
17644  	 	 	 	 	 $this->SetFillColorArray($dom[$key]['bgcolor']);
17645  	 	 	 	 	 $wfill = true;
17646  	 	 	 	 } else {
17647  	 	 	 	 	 $wfill = $fill | false;
17648  	 	 	 	 }
17649  	 	 	 	 if (isset($dom[$key]['fgcolor']) AND ($dom[$key]['fgcolor'] !== false)) {
17650  	 	 	 	 	 $this->SetTextColorArray($dom[$key]['fgcolor']);
17651  	 	 	 	 }
17652  	 	 	 	 if (isset($dom[$key]['strokecolor']) AND ($dom[$key]['strokecolor'] !== false)) {
17653  	 	 	 	 	 $this->SetDrawColorArray($dom[$key]['strokecolor']);
17654  	 	 	 	 }
17655  	 	 	 	 if (isset($dom[$key]['align'])) {
17656  	 	 	 	 	 $lalign = $dom[$key]['align'];
17657  	 	 	 	 }
17658  	 	 	 	 if (TCPDF_STATIC::empty_string($lalign)) {
17659  	 	 	 	 	 $lalign = $align;
17660  	 	 	 	 }
17661  	 	 	 }
17662  	 	 	 // align lines
17663  	 	 	 if ($this->newline AND (strlen($dom[$key]['value']) > 0) AND ($dom[$key]['value'] != 'td') AND ($dom[$key]['value'] != 'th')) {
17664  	 	 	 	 $newline = true;
17665  	 	 	 	 $fontaligned = false;
17666  	 	 	 	 // we are at the beginning of a new line
17667  	 	 	 	 if (isset($startlinex)) {
17668  	 	 	 	 	 $yshift = ($minstartliney - $startliney);
17669  	 	 	 	 	 if (($yshift > 0) OR ($this->page > $startlinepage)) {
17670  	 	 	 	 	 	 $yshift = 0;
17671  	 	 	 	 	 }
17672  	 	 	 	 	 $t_x = 0;
17673  	 	 	 	 	 // the last line must be shifted to be aligned as requested
17674  	 	 	 	 	 $linew = abs($this->endlinex - $startlinex);
17675  	 	 	 	 	 if ($this->inxobj) {
17676  	 	 	 	 	 	 // we are inside an XObject template
17677  	 	 	 	 	 	 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
17678  	 	 	 	 	 	 if (isset($opentagpos)) {
17679  	 	 	 	 	 	 	 $midpos = $opentagpos;
17680  	 	 	 	 	 	 } else {
17681  	 	 	 	 	 	 	 $midpos = 0;
17682  	 	 	 	 	 	 }
17683  	 	 	 	 	 	 if ($midpos > 0) {
17684  	 	 	 	 	 	 	 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
17685  	 	 	 	 	 	 	 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
17686  	 	 	 	 	 	 } else {
17687  	 	 	 	 	 	 	 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
17688  	 	 	 	 	 	 	 $pend = '';
17689  	 	 	 	 	 	 }
17690  	 	 	 	 	 } else {
17691  	 	 	 	 	 	 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
17692  	 	 	 	 	 	 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17693  	 	 	 	 	 	 	 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17694  	 	 	 	 	 	 	 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
17695  	 	 	 	 	 	 } elseif (isset($opentagpos)) {
17696  	 	 	 	 	 	 	 $midpos = $opentagpos;
17697  	 	 	 	 	 	 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
17698  	 	 	 	 	 	 	 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
17699  	 	 	 	 	 	 	 $midpos = $this->footerpos[$startlinepage];
17700  	 	 	 	 	 	 } else {
17701  	 	 	 	 	 	 	 $midpos = 0;
17702  	 	 	 	 	 	 }
17703  	 	 	 	 	 	 if ($midpos > 0) {
17704  	 	 	 	 	 	 	 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
17705  	 	 	 	 	 	 	 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
17706  	 	 	 	 	 	 } else {
17707  	 	 	 	 	 	 	 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
17708  	 	 	 	 	 	 	 $pend = '';
17709  	 	 	 	 	 	 }
17710  	 	 	 	 	 }
17711  	 	 	 	 	 if ((isset($plalign) AND ((($plalign == 'C') OR ($plalign == 'J') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
17712  	 	 	 	 	 	 // calculate shifting amount
17713  	 	 	 	 	 	 $tw = $w;
17714  	 	 	 	 	 	 if (($plalign == 'J') AND $this->isRTLTextDir() AND ($this->num_columns > 1)) {
17715  	 	 	 	 	 	 	 $tw += $this->cell_padding['R'];
17716  	 	 	 	 	 	 }
17717  	 	 	 	 	 	 if ($this->lMargin != $prevlMargin) {
17718  	 	 	 	 	 	 	 $tw += ($prevlMargin - $this->lMargin);
17719  	 	 	 	 	 	 }
17720  	 	 	 	 	 	 if ($this->rMargin != $prevrMargin) {
17721  	 	 	 	 	 	 	 $tw += ($prevrMargin - $this->rMargin);
17722  	 	 	 	 	 	 }
17723  	 	 	 	 	 	 $one_space_width = $this->GetStringWidth(chr(32));
17724  	 	 	 	 	 	 $no = 0; // number of spaces on a line contained on a single block
17725  	 	 	 	 	 	 if ($this->isRTLTextDir()) { // RTL
17726  	 	 	 	 	 	 	 // remove left space if exist
17727  	 	 	 	 	 	 	 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
17728  	 	 	 	 	 	 	 if ($pos1 > 0) {
17729  	 	 	 	 	 	 	 	 $pos1 = intval($pos1);
17730  	 	 	 	 	 	 	 	 if ($this->isUnicodeFont()) {
17731  	 	 	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
17732  	 	 	 	 	 	 	 	 	 $spacelen = 2;
17733  	 	 	 	 	 	 	 	 } else {
17734  	 	 	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
17735  	 	 	 	 	 	 	 	 	 $spacelen = 1;
17736  	 	 	 	 	 	 	 	 }
17737  	 	 	 	 	 	 	 	 if ($pos1 == $pos2) {
17738  	 	 	 	 	 	 	 	 	 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
17739  	 	 	 	 	 	 	 	 	 if (substr($pmid, $pos1, 4) == '[()]') {
17740  	 	 	 	 	 	 	 	 	 	 $linew -= $one_space_width;
17741  	 	 	 	 	 	 	 	 	 } elseif ($pos1 == strpos($pmid, '[(')) {
17742  	 	 	 	 	 	 	 	 	 	 $no = 1;
17743  	 	 	 	 	 	 	 	 	 }
17744  	 	 	 	 	 	 	 	 }
17745  	 	 	 	 	 	 	 }
17746  	 	 	 	 	 	 } else { // LTR
17747  	 	 	 	 	 	 	 // remove right space if exist
17748  	 	 	 	 	 	 	 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
17749  	 	 	 	 	 	 	 if ($pos1 > 0) {
17750  	 	 	 	 	 	 	 	 $pos1 = intval($pos1);
17751  	 	 	 	 	 	 	 	 if ($this->isUnicodeFont()) {
17752  	 	 	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
17753  	 	 	 	 	 	 	 	 	 $spacelen = 2;
17754  	 	 	 	 	 	 	 	 } else {
17755  	 	 	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
17756  	 	 	 	 	 	 	 	 	 $spacelen = 1;
17757  	 	 	 	 	 	 	 	 }
17758  	 	 	 	 	 	 	 	 if ($pos1 == $pos2) {
17759  	 	 	 	 	 	 	 	 	 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
17760  	 	 	 	 	 	 	 	 	 $linew -= $one_space_width;
17761  	 	 	 	 	 	 	 	 }
17762  	 	 	 	 	 	 	 }
17763  	 	 	 	 	 	 }
17764  	 	 	 	 	 	 $mdiff = ($tw - $linew);
17765  	 	 	 	 	 	 if ($plalign == 'C') {
17766  	 	 	 	 	 	 	 if ($this->rtl) {
17767  	 	 	 	 	 	 	 	 $t_x = -($mdiff / 2);
17768  	 	 	 	 	 	 	 } else {
17769  	 	 	 	 	 	 	 	 $t_x = ($mdiff / 2);
17770  	 	 	 	 	 	 	 }
17771  	 	 	 	 	 	 } elseif ($plalign == 'R') {
17772  	 	 	 	 	 	 	 // right alignment on LTR document
17773  	 	 	 	 	 	 	 $t_x = $mdiff;
17774  	 	 	 	 	 	 } elseif ($plalign == 'L') {
17775  	 	 	 	 	 	 	 // left alignment on RTL document
17776  	 	 	 	 	 	 	 $t_x = -$mdiff;
17777  	 	 	 	 	 	 } elseif (($plalign == 'J') AND ($plalign == $lalign)) {
17778  	 	 	 	 	 	 	 // Justification
17779  	 	 	 	 	 	 	 if ($this->isRTLTextDir()) {
17780  	 	 	 	 	 	 	 	 // align text on the left
17781  	 	 	 	 	 	 	 	 $t_x = -$mdiff;
17782  	 	 	 	 	 	 	 }
17783  	 	 	 	 	 	 	 $ns = 0; // number of spaces
17784  	 	 	 	 	 	 	 $pmidtemp = $pmid;
17785  	 	 	 	 	 	 	 // escape special characters
17786  	 	 	 	 	 	 	 $pmidtemp = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmidtemp);
17787  	 	 	 	 	 	 	 $pmidtemp = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmidtemp);
17788  	 	 	 	 	 	 	 // search spaces
17789  	 	 	 	 	 	 	 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
17790  	 	 	 	 	 	 	 	 $spacestr = $this->getSpaceString();
17791  	 	 	 	 	 	 	 	 $maxkk = count($lnstring[1]) - 1;
17792  	 	 	 	 	 	 	 	 for ($kk=0; $kk <= $maxkk; ++$kk) {
17793  	 	 	 	 	 	 	 	 	 // restore special characters
17794  	 	 	 	 	 	 	 	 	 $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
17795  	 	 	 	 	 	 	 	 	 $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
17796  	 	 	 	 	 	 	 	 	 // store number of spaces on the strings
17797  	 	 	 	 	 	 	 	 	 $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
17798  	 	 	 	 	 	 	 	 	 // count total spaces on line
17799  	 	 	 	 	 	 	 	 	 $ns += $lnstring[2][$kk];
17800  	 	 	 	 	 	 	 	 	 $lnstring[3][$kk] = $ns;
17801  	 	 	 	 	 	 	 	 }
17802  	 	 	 	 	 	 	 	 if ($ns == 0) {
17803  	 	 	 	 	 	 	 	 	 $ns = 1;
17804  	 	 	 	 	 	 	 	 }
17805  	 	 	 	 	 	 	 	 // calculate additional space to add to each existing space
17806  	 	 	 	 	 	 	 	 $spacewidth = ($mdiff / ($ns - $no)) * $this->k;
17807  	 	 	 	 	 	 	 	 if ($this->FontSize <= 0) {
17808  	 	 	 	 	 	 	 	 	 $this->FontSize = 1;
17809  	 	 	 	 	 	 	 	 }
17810  	 	 	 	 	 	 	 	 $spacewidthu = -1000 * ($mdiff + (($ns + $no) * $one_space_width)) / $ns / $this->FontSize;
17811  	 	 	 	 	 	 	 	 if ($this->font_spacing != 0) {
17812  	 	 	 	 	 	 	 	 	 // fixed spacing mode
17813  	 	 	 	 	 	 	 	 	 $osw = -1000 * $this->font_spacing / $this->FontSize;
17814  	 	 	 	 	 	 	 	 	 $spacewidthu += $osw;
17815  	 	 	 	 	 	 	 	 }
17816  	 	 	 	 	 	 	 	 $nsmax = $ns;
17817  	 	 	 	 	 	 	 	 $ns = 0;
17818  	 	 	 	 	 	 	 	 reset($lnstring);
17819  	 	 	 	 	 	 	 	 $offset = 0;
17820  	 	 	 	 	 	 	 	 $strcount = 0;
17821  	 	 	 	 	 	 	 	 $prev_epsposbeg = 0;
17822  	 	 	 	 	 	 	 	 $textpos = 0;
17823  	 	 	 	 	 	 	 	 if ($this->isRTLTextDir()) {
17824  	 	 	 	 	 	 	 	 	 $textpos = $this->wPt;
17825  	 	 	 	 	 	 	 	 }
17826  	 	 	 	 	 	 	 	 while (preg_match('/([0-9\.\+\-]*)[\s](Td|cm|m|l|c|re)[\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
17827  	 	 	 	 	 	 	 	 	 // check if we are inside a string section '[( ... )]'
17828  	 	 	 	 	 	 	 	 	 $stroffset = strpos($pmid, '[(', $offset);
17829  	 	 	 	 	 	 	 	 	 if (($stroffset !== false) AND ($stroffset <= $strpiece[2][1])) {
17830  	 	 	 	 	 	 	 	 	 	 // set offset to the end of string section
17831  	 	 	 	 	 	 	 	 	 	 $offset = strpos($pmid, ')]', $stroffset);
17832  	 	 	 	 	 	 	 	 	 	 while (($offset !== false) AND ($pmid[($offset - 1)] == '\\')) {
17833  	 	 	 	 	 	 	 	 	 	 	 $offset = strpos($pmid, ')]', ($offset + 1));
17834  	 	 	 	 	 	 	 	 	 	 }
17835  	 	 	 	 	 	 	 	 	 	 if ($offset === false) {
17836  	 	 	 	 	 	 	 	 	 	 	 $this->Error('HTML Justification: malformed PDF code.');
17837  	 	 	 	 	 	 	 	 	 	 }
17838  	 	 	 	 	 	 	 	 	 	 continue;
17839  	 	 	 	 	 	 	 	 	 }
17840  	 	 	 	 	 	 	 	 	 if ($this->isRTLTextDir()) {
17841  	 	 	 	 	 	 	 	 	 	 $spacew = ($spacewidth * ($nsmax - $ns));
17842  	 	 	 	 	 	 	 	 	 } else {
17843  	 	 	 	 	 	 	 	 	 	 $spacew = ($spacewidth * $ns);
17844  	 	 	 	 	 	 	 	 	 }
17845  	 	 	 	 	 	 	 	 	 $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
17846  	 	 	 	 	 	 	 	 	 $epsposend = strpos($pmid, $this->epsmarker.'Q', $offset);
17847  	 	 	 	 	 	 	 	 	 if ($epsposend !== null) {
17848  	 	 	 	 	 	 	 	 	 	 $epsposend += strlen($this->epsmarker.'Q');
17849  	 	 	 	 	 	 	 	 	 	 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, $offset);
17850  	 	 	 	 	 	 	 	 	 	 if ($epsposbeg === null) {
17851  	 	 	 	 	 	 	 	 	 	 	 $epsposbeg = strpos($pmid, 'q'.$this->epsmarker, ($prev_epsposbeg - 6));
17852  	 	 	 	 	 	 	 	 	 	 	 $prev_epsposbeg = $epsposbeg;
17853  	 	 	 	 	 	 	 	 	 	 }
17854  	 	 	 	 	 	 	 	 	 	 if (($epsposbeg > 0) AND ($epsposend > 0) AND ($offset > $epsposbeg) AND ($offset < $epsposend)) {
17855  	 	 	 	 	 	 	 	 	 	 	 // shift EPS images
17856  	 	 	 	 	 	 	 	 	 	 	 $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
17857  	 	 	 	 	 	 	 	 	 	 	 $pmid_b = substr($pmid, 0, $epsposbeg);
17858  	 	 	 	 	 	 	 	 	 	 	 $pmid_m = substr($pmid, $epsposbeg, ($epsposend - $epsposbeg));
17859  	 	 	 	 	 	 	 	 	 	 	 $pmid_e = substr($pmid, $epsposend);
17860  	 	 	 	 	 	 	 	 	 	 	 $pmid = $pmid_b."\nq\n".$trx."\n".$pmid_m."\nQ\n".$pmid_e;
17861  	 	 	 	 	 	 	 	 	 	 	 $offset = $epsposend;
17862  	 	 	 	 	 	 	 	 	 	 	 continue;
17863  	 	 	 	 	 	 	 	 	 	 }
17864  	 	 	 	 	 	 	 	 	 }
17865  	 	 	 	 	 	 	 	 	 $currentxpos = 0;
17866  	 	 	 	 	 	 	 	 	 // shift blocks of code
17867  	 	 	 	 	 	 	 	 	 switch ($strpiece[2][0]) {
17868  	 	 	 	 	 	 	 	 	 	 case 'Td':
17869  	 	 	 	 	 	 	 	 	 	 case 'cm':
17870  	 	 	 	 	 	 	 	 	 	 case 'm':
17871  	 	 	 	 	 	 	 	 	 	 case 'l': {
17872  	 	 	 	 	 	 	 	 	 	 	 // get current X position
17873  	 	 	 	 	 	 	 	 	 	 	 preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $xmatches);
17874  	 	 	 	 	 	 	 	 	 	 	 if (!isset($xmatches[1])) {
17875  	 	 	 	 	 	 	 	 	 	 	 	 break;
17876  	 	 	 	 	 	 	 	 	 	 	 }
17877  	 	 	 	 	 	 	 	 	 	 	 $currentxpos = $xmatches[1];
17878  	 	 	 	 	 	 	 	 	 	 	 $textpos = $currentxpos;
17879  	 	 	 	 	 	 	 	 	 	 	 if (($strcount <= $maxkk) AND ($strpiece[2][0] == 'Td')) {
17880  	 	 	 	 	 	 	 	 	 	 	 	 $ns = $lnstring[3][$strcount];
17881  	 	 	 	 	 	 	 	 	 	 	 	 if ($this->isRTLTextDir()) {
17882  	 	 	 	 	 	 	 	 	 	 	 	 	 $spacew = ($spacewidth * ($nsmax - $ns));
17883  	 	 	 	 	 	 	 	 	 	 	 	 }
17884  	 	 	 	 	 	 	 	 	 	 	 	 ++$strcount;
17885  	 	 	 	 	 	 	 	 	 	 	 }
17886  	 	 	 	 	 	 	 	 	 	 	 // justify block
17887  	 	 	 	 	 	 	 	 	 	 	 if (preg_match('/([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s]('.$strpiece[2][0].')([\s]*)/x', $pmid, $pmatch) == 1) {
17888  	 	 	 	 	 	 	 	 	 	 	 	 $newpmid = sprintf('%F',(floatval($pmatch[1]) + $spacew)).' '.$pmatch[2].' x*#!#*x'.$pmatch[3].$pmatch[4];
17889  	 	 	 	 	 	 	 	 	 	 	 	 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17890  	 	 	 	 	 	 	 	 	 	 	 	 unset($pmatch, $newpmid);
17891  	 	 	 	 	 	 	 	 	 	 	 }
17892  	 	 	 	 	 	 	 	 	 	 	 break;
17893  	 	 	 	 	 	 	 	 	 	 }
17894  	 	 	 	 	 	 	 	 	 	 case 're': {
17895  	 	 	 	 	 	 	 	 	 	 	 // justify block
17896  	 	 	 	 	 	 	 	 	 	 	 if (!TCPDF_STATIC::empty_string($this->lispacer)) {
17897  	 	 	 	 	 	 	 	 	 	 	 	 $this->lispacer = '';
17898  	 	 	 	 	 	 	 	 	 	 	 	 break;
17899  	 	 	 	 	 	 	 	 	 	 	 }
17900  	 	 	 	 	 	 	 	 	 	 	 preg_match('/([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]([0-9\.\+\-]*)[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $xmatches);
17901  	 	 	 	 	 	 	 	 	 	 	 if (!isset($xmatches[1])) {
17902  	 	 	 	 	 	 	 	 	 	 	 	 break;
17903  	 	 	 	 	 	 	 	 	 	 	 }
17904  	 	 	 	 	 	 	 	 	 	 	 $currentxpos = $xmatches[1];
17905  	 	 	 	 	 	 	 	 	 	 	 $x_diff = 0;
17906  	 	 	 	 	 	 	 	 	 	 	 $w_diff = 0;
17907  	 	 	 	 	 	 	 	 	 	 	 if ($this->isRTLTextDir()) { // RTL
17908  	 	 	 	 	 	 	 	 	 	 	 	 if ($currentxpos < $textpos) {
17909  	 	 	 	 	 	 	 	 	 	 	 	 	 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][$strcount]));
17910  	 	 	 	 	 	 	 	 	 	 	 	 	 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17911  	 	 	 	 	 	 	 	 	 	 	 	 } else {
17912  	 	 	 	 	 	 	 	 	 	 	 	 	 if ($strcount > 0) {
17913  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $x_diff = ($spacewidth * ($nsmax - $lnstring[3][($strcount - 1)]));
17914  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17915  	 	 	 	 	 	 	 	 	 	 	 	 	 }
17916  	 	 	 	 	 	 	 	 	 	 	 	 }
17917  	 	 	 	 	 	 	 	 	 	 	 } else { // LTR
17918  	 	 	 	 	 	 	 	 	 	 	 	 if ($currentxpos > $textpos) {
17919  	 	 	 	 	 	 	 	 	 	 	 	 	 if ($strcount > 0) {
17920  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $x_diff = ($spacewidth * $lnstring[3][($strcount - 1)]);
17921  	 	 	 	 	 	 	 	 	 	 	 	 	 }
17922  	 	 	 	 	 	 	 	 	 	 	 	 	 $w_diff = ($spacewidth * $lnstring[2][$strcount]);
17923  	 	 	 	 	 	 	 	 	 	 	 	 } else {
17924  	 	 	 	 	 	 	 	 	 	 	 	 	 if ($strcount > 1) {
17925  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $x_diff = ($spacewidth * $lnstring[3][($strcount - 2)]);
17926  	 	 	 	 	 	 	 	 	 	 	 	 	 }
17927  	 	 	 	 	 	 	 	 	 	 	 	 	 if ($strcount > 0) {
17928  	 	 	 	 	 	 	 	 	 	 	 	 	 	 $w_diff = ($spacewidth * $lnstring[2][($strcount - 1)]);
17929  	 	 	 	 	 	 	 	 	 	 	 	 	 }
17930  	 	 	 	 	 	 	 	 	 	 	 	 }
17931  	 	 	 	 	 	 	 	 	 	 	 }
17932  	 	 	 	 	 	 	 	 	 	 	 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$strpiece[1][0].')[\s](re)([\s]*)/x', $pmid, $pmatch) == 1) {
17933  	 	 	 	 	 	 	 	 	 	 	 	 $newx = sprintf('%F',(floatval($pmatch[1]) + $x_diff));
17934  	 	 	 	 	 	 	 	 	 	 	 	 $neww = sprintf('%F',(floatval($pmatch[3]) + $w_diff));
17935  	 	 	 	 	 	 	 	 	 	 	 	 $newpmid = $newx.' '.$pmatch[2].' '.$neww.' '.$pmatch[4].' x*#!#*x'.$pmatch[5].$pmatch[6];
17936  	 	 	 	 	 	 	 	 	 	 	 	 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17937  	 	 	 	 	 	 	 	 	 	 	 	 unset($pmatch, $newpmid, $newx, $neww);
17938  	 	 	 	 	 	 	 	 	 	 	 }
17939  	 	 	 	 	 	 	 	 	 	 	 break;
17940  	 	 	 	 	 	 	 	 	 	 }
17941  	 	 	 	 	 	 	 	 	 	 case 'c': {
17942  	 	 	 	 	 	 	 	 	 	 	 // get current X position
17943  	 	 	 	 	 	 	 	 	 	 	 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);
17944  	 	 	 	 	 	 	 	 	 	 	 if (!isset($xmatches[1])) {
17945  	 	 	 	 	 	 	 	 	 	 	 	 break;
17946  	 	 	 	 	 	 	 	 	 	 	 }
17947  	 	 	 	 	 	 	 	 	 	 	 $currentxpos = $xmatches[1];
17948  	 	 	 	 	 	 	 	 	 	 	 // justify block
17949  	 	 	 	 	 	 	 	 	 	 	 if (preg_match('/('.$xmatches[1].')[\s]('.$xmatches[2].')[\s]('.$xmatches[3].')[\s]('.$xmatches[4].')[\s]('.$xmatches[5].')[\s]('.$strpiece[1][0].')[\s](c)([\s]*)/x', $pmid, $pmatch) == 1) {
17950  	 	 	 	 	 	 	 	 	 	 	 	 $newx1 = sprintf('%F',(floatval($pmatch[1]) + $spacew));
17951  	 	 	 	 	 	 	 	 	 	 	 	 $newx2 = sprintf('%F',(floatval($pmatch[3]) + $spacew));
17952  	 	 	 	 	 	 	 	 	 	 	 	 $newx3 = sprintf('%F',(floatval($pmatch[5]) + $spacew));
17953  	 	 	 	 	 	 	 	 	 	 	 	 $newpmid = $newx1.' '.$pmatch[2].' '.$newx2.' '.$pmatch[4].' '.$newx3.' '.$pmatch[6].' x*#!#*x'.$pmatch[7].$pmatch[8];
17954  	 	 	 	 	 	 	 	 	 	 	 	 $pmid = str_replace($pmatch[0], $newpmid, $pmid);
17955  	 	 	 	 	 	 	 	 	 	 	 	 unset($pmatch, $newpmid, $newx1, $newx2, $newx3);
17956  	 	 	 	 	 	 	 	 	 	 	 }
17957  	 	 	 	 	 	 	 	 	 	 	 break;
17958  	 	 	 	 	 	 	 	 	 	 }
17959  	 	 	 	 	 	 	 	 	 }
17960  	 	 	 	 	 	 	 	 	 // shift the annotations and links
17961  	 	 	 	 	 	 	 	 	 $cxpos = ($currentxpos / $this->k);
17962  	 	 	 	 	 	 	 	 	 $lmpos = ($this->lMargin + $this->cell_padding['L'] + $this->feps);
17963  	 	 	 	 	 	 	 	 	 if ($this->inxobj) {
17964  	 	 	 	 	 	 	 	 	 	 // we are inside an XObject template
17965  	 	 	 	 	 	 	 	 	 	 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
17966  	 	 	 	 	 	 	 	 	 	 	 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17967  	 	 	 	 	 	 	 	 	 	 	 	 if ($cxpos > $lmpos) {
17968  	 	 	 	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += ($spacew / $this->k);
17969  	 	 	 	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17970  	 	 	 	 	 	 	 	 	 	 	 	 } else {
17971  	 	 	 	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17972  	 	 	 	 	 	 	 	 	 	 	 	 }
17973  	 	 	 	 	 	 	 	 	 	 	 	 break;
17974  	 	 	 	 	 	 	 	 	 	 	 }
17975  	 	 	 	 	 	 	 	 	 	 }
17976  	 	 	 	 	 	 	 	 	 } elseif (isset($this->PageAnnots[$this->page])) {
17977  	 	 	 	 	 	 	 	 	 	 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
17978  	 	 	 	 	 	 	 	 	 	 	 if (($pac['y'] >= $minstartliney) AND (($pac['x'] * $this->k) >= ($currentxpos - $this->feps)) AND (($pac['x'] * $this->k) <= ($currentxpos + $this->feps))) {
17979  	 	 	 	 	 	 	 	 	 	 	 	 if ($cxpos > $lmpos) {
17980  	 	 	 	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['x'] += ($spacew / $this->k);
17981  	 	 	 	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17982  	 	 	 	 	 	 	 	 	 	 	 	 } else {
17983  	 	 	 	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['w'] += (($spacewidth * $pac['numspaces']) / $this->k);
17984  	 	 	 	 	 	 	 	 	 	 	 	 }
17985  	 	 	 	 	 	 	 	 	 	 	 	 break;
17986  	 	 	 	 	 	 	 	 	 	 	 }
17987  	 	 	 	 	 	 	 	 	 	 }
17988  	 	 	 	 	 	 	 	 	 }
17989  	 	 	 	 	 	 	 	 } // end of while
17990  	 	 	 	 	 	 	 	 // remove markers
17991  	 	 	 	 	 	 	 	 $pmid = str_replace('x*#!#*x', '', $pmid);
17992  	 	 	 	 	 	 	 	 if ($this->isUnicodeFont()) {
17993  	 	 	 	 	 	 	 	 	 // multibyte characters
17994  	 	 	 	 	 	 	 	 	 $spacew = $spacewidthu;
17995  	 	 	 	 	 	 	 	 	 if ($this->font_stretching != 100) {
17996  	 	 	 	 	 	 	 	 	 	 // word spacing is affected by stretching
17997  	 	 	 	 	 	 	 	 	 	 $spacew /= ($this->font_stretching / 100);
17998  	 	 	 	 	 	 	 	 	 }
17999  	 	 	 	 	 	 	 	 	 // escape special characters
18000  	 	 	 	 	 	 	 	 	 $pos = 0;
18001  	 	 	 	 	 	 	 	 	 $pmid = preg_replace('/[\\\][\(]/x', '\\#!#OP#!#', $pmid);
18002  	 	 	 	 	 	 	 	 	 $pmid = preg_replace('/[\\\][\)]/x', '\\#!#CP#!#', $pmid);
18003  	 	 	 	 	 	 	 	 	 if (preg_match_all('/\[\(([^\)]*)\)\]/x', $pmid, $pamatch) > 0) {
18004  	 	 	 	 	 	 	 	 	 	 foreach($pamatch[0] as $pk => $pmatch) {
18005  	 	 	 	 	 	 	 	 	 	 	 $replace = $pamatch[1][$pk];
18006  	 	 	 	 	 	 	 	 	 	 	 $replace = str_replace('#!#OP#!#', '(', $replace);
18007  	 	 	 	 	 	 	 	 	 	 	 $replace = str_replace('#!#CP#!#', ')', $replace);
18008  	 	 	 	 	 	 	 	 	 	 	 $newpmid = '[('.str_replace(chr(0).chr(32), ') '.sprintf('%F', $spacew).' (', $replace).')]';
18009  	 	 	 	 	 	 	 	 	 	 	 $pos = strpos($pmid, $pmatch, $pos);
18010  	 	 	 	 	 	 	 	 	 	 	 if ($pos !== FALSE) {
18011  	 	 	 	 	 	 	 	 	 	 	 	 $pmid = substr_replace($pmid, $newpmid, $pos, strlen($pmatch));
18012  	 	 	 	 	 	 	 	 	 	 	 }
18013  	 	 	 	 	 	 	 	 	 	 	 ++$pos;
18014  	 	 	 	 	 	 	 	 	 	 }
18015  	 	 	 	 	 	 	 	 	 	 unset($pamatch);
18016  	 	 	 	 	 	 	 	 	 }
18017  	 	 	 	 	 	 	 	 	 if ($this->inxobj) {
18018  	 	 	 	 	 	 	 	 	 	 // we are inside an XObject template
18019  	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\n".$pend;
18020  	 	 	 	 	 	 	 	 	 } else {
18021  	 	 	 	 	 	 	 	 	 	 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\n".$pend);
18022  	 	 	 	 	 	 	 	 	 }
18023  	 	 	 	 	 	 	 	 	 $endlinepos = strlen($pstart."\n".$pmid."\n");
18024  	 	 	 	 	 	 	 	 } else {
18025  	 	 	 	 	 	 	 	 	 // non-unicode (single-byte characters)
18026  	 	 	 	 	 	 	 	 	 if ($this->font_stretching != 100) {
18027  	 	 	 	 	 	 	 	 	 	 // word spacing (Tw) is affected by stretching
18028  	 	 	 	 	 	 	 	 	 	 $spacewidth /= ($this->font_stretching / 100);
18029  	 	 	 	 	 	 	 	 	 }
18030  	 	 	 	 	 	 	 	 	 $rs = sprintf('%F Tw', $spacewidth);
18031  	 	 	 	 	 	 	 	 	 $pmid = preg_replace("/\[\(/x", $rs.' [(', $pmid);
18032  	 	 	 	 	 	 	 	 	 if ($this->inxobj) {
18033  	 	 	 	 	 	 	 	 	 	 // we are inside an XObject template
18034  	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend;
18035  	 	 	 	 	 	 	 	 	 } else {
18036  	 	 	 	 	 	 	 	 	 	 $this->setPageBuffer($startlinepage, $pstart."\n".$pmid."\nBT 0 Tw ET\n".$pend);
18037  	 	 	 	 	 	 	 	 	 }
18038  	 	 	 	 	 	 	 	 	 $endlinepos = strlen($pstart."\n".$pmid."\nBT 0 Tw ET\n");
18039  	 	 	 	 	 	 	 	 }
18040  	 	 	 	 	 	 	 }
18041  	 	 	 	 	 	 } // end of J
18042  	 	 	 	 	 } // end if $startlinex
18043  	 	 	 	 	 if (($t_x != 0) OR ($yshift < 0)) {
18044  	 	 	 	 	 	 // shift the line
18045  	 	 	 	 	 	 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18046  	 	 	 	 	 	 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18047  	 	 	 	 	 	 $endlinepos = strlen($pstart);
18048  	 	 	 	 	 	 if ($this->inxobj) {
18049  	 	 	 	 	 	 	 // we are inside an XObject template
18050  	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18051  	 	 	 	 	 	 	 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18052  	 	 	 	 	 	 	 	 if ($pak >= $pask) {
18053  	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18054  	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18055  	 	 	 	 	 	 	 	 }
18056  	 	 	 	 	 	 	 }
18057  	 	 	 	 	 	 } else {
18058  	 	 	 	 	 	 	 $this->setPageBuffer($startlinepage, $pstart.$pend);
18059  	 	 	 	 	 	 	 // shift the annotations and links
18060  	 	 	 	 	 	 	 if (isset($this->PageAnnots[$this->page])) {
18061  	 	 	 	 	 	 	 	 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18062  	 	 	 	 	 	 	 	 	 if ($pak >= $pask) {
18063  	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18064  	 	 	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18065  	 	 	 	 	 	 	 	 	 }
18066  	 	 	 	 	 	 	 	 }
18067  	 	 	 	 	 	 	 }
18068  	 	 	 	 	 	 }
18069  	 	 	 	 	 	 $this->y -= $yshift;
18070  	 	 	 	 	 }
18071  	 	 	 	 }
18072  	 	 	 	 $pbrk = $this->checkPageBreak($this->lasth);
18073  	 	 	 	 $this->newline = false;
18074  	 	 	 	 $startlinex = $this->x;
18075  	 	 	 	 $startliney = $this->y;
18076  	 	 	 	 if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
18077  	 	 	 	 	 $startliney -= ((0.3 * $this->FontSizePt) / $this->k);
18078  	 	 	 	 } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
18079  	 	 	 	 	 $startliney -= (($this->FontSizePt / 0.7) / $this->k);
18080  	 	 	 	 } else {
18081  	 	 	 	 	 $minstartliney = $startliney;
18082  	 	 	 	 	 $maxbottomliney = ($this->y + $this->getCellHeight($fontsize / $this->k));
18083  	 	 	 	 }
18084  	 	 	 	 $startlinepage = $this->page;
18085  	 	 	 	 if (isset($endlinepos) AND (!$pbrk)) {
18086  	 	 	 	 	 $startlinepos = $endlinepos;
18087  	 	 	 	 } else {
18088  	 	 	 	 	 if ($this->inxobj) {
18089  	 	 	 	 	 	 // we are inside an XObject template
18090  	 	 	 	 	 	 $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
18091  	 	 	 	 	 } elseif (!$this->InFooter) {
18092  	 	 	 	 	 	 if (isset($this->footerlen[$this->page])) {
18093  	 	 	 	 	 	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18094  	 	 	 	 	 	 } else {
18095  	 	 	 	 	 	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page];
18096  	 	 	 	 	 	 }
18097  	 	 	 	 	 	 $startlinepos = $this->footerpos[$this->page];
18098  	 	 	 	 	 } else {
18099  	 	 	 	 	 	 $startlinepos = $this->pagelen[$this->page];
18100  	 	 	 	 	 }
18101  	 	 	 	 }
18102  	 	 	 	 unset($endlinepos);
18103  	 	 	 	 $plalign = $lalign;
18104  	 	 	 	 if (isset($this->PageAnnots[$this->page])) {
18105  	 	 	 	 	 $pask = count($this->PageAnnots[$this->page]);
18106  	 	 	 	 } else {
18107  	 	 	 	 	 $pask = 0;
18108  	 	 	 	 }
18109  	 	 	 	 if (!($dom[$key]['tag'] AND !$dom[$key]['opening'] AND ($dom[$key]['value'] == 'table')
18110  	 	 	 	 	 AND (isset($this->emptypagemrk[$this->page]))
18111  	 	 	 	 	 AND ($this->emptypagemrk[$this->page] == $this->pagelen[$this->page]))) {
18112  	 	 	 	 	 $this->SetFont($fontname, $fontstyle, $fontsize);
18113  	 	 	 	 	 if ($wfill) {
18114  	 	 	 	 	 	 $this->SetFillColorArray($this->bgcolor);
18115  	 	 	 	 	 }
18116  	 	 	 	 }
18117  	 	 	 } // end newline
18118  	 	 	 if (isset($opentagpos)) {
18119  	 	 	 	 unset($opentagpos);
18120  	 	 	 }
18121  	 	 	 if ($dom[$key]['tag']) {
18122  	 	 	 	 if ($dom[$key]['opening']) {
18123  	 	 	 	 	 // get text indentation (if any)
18124  	 	 	 	 	 if (isset($dom[$key]['text-indent']) AND $dom[$key]['block']) {
18125  	 	 	 	 	 	 $this->textindent = $dom[$key]['text-indent'];
18126  	 	 	 	 	 	 $this->newline = true;
18127  	 	 	 	 	 }
18128  	 	 	 	 	 // table
18129  	 	 	 	 	 if (($dom[$key]['value'] == 'table') AND isset($dom[$key]['cols']) AND ($dom[$key]['cols'] > 0)) {
18130  	 	 	 	 	 	 // available page width
18131  	 	 	 	 	 	 if ($this->rtl) {
18132  	 	 	 	 	 	 	 $wtmp = $this->x - $this->lMargin;
18133  	 	 	 	 	 	 } else {
18134  	 	 	 	 	 	 	 $wtmp = $this->w - $this->rMargin - $this->x;
18135  	 	 	 	 	 	 }
18136  	 	 	 	 	 	 // get cell spacing
18137  	 	 	 	 	 	 if (isset($dom[$key]['attribute']['cellspacing'])) {
18138  	 	 	 	 	 	 	 $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
18139  	 	 	 	 	 	 	 $cellspacing = array('H' => $clsp, 'V' => $clsp);
18140  	 	 	 	 	 	 } elseif (isset($dom[$key]['border-spacing'])) {
18141  	 	 	 	 	 	 	 $cellspacing = $dom[$key]['border-spacing'];
18142  	 	 	 	 	 	 } else {
18143  	 	 	 	 	 	 	 $cellspacing = array('H' => 0, 'V' => 0);
18144  	 	 	 	 	 	 }
18145  	 	 	 	 	 	 // table width
18146  	 	 	 	 	 	 if (isset($dom[$key]['width'])) {
18147  	 	 	 	 	 	 	 $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
18148  	 	 	 	 	 	 } else {
18149  	 	 	 	 	 	 	 $table_width = $wtmp;
18150  	 	 	 	 	 	 }
18151  	 	 	 	 	 	 $table_width -= (2 * $cellspacing['H']);
18152  	 	 	 	 	 	 if (!$this->inthead) {
18153  	 	 	 	 	 	 	 $this->y += $cellspacing['V'];
18154  	 	 	 	 	 	 }
18155  	 	 	 	 	 	 if ($this->rtl) {
18156  	 	 	 	 	 	 	 $cellspacingx = -$cellspacing['H'];
18157  	 	 	 	 	 	 } else {
18158  	 	 	 	 	 	 	 $cellspacingx = $cellspacing['H'];
18159  	 	 	 	 	 	 }
18160  	 	 	 	 	 	 // total table width without cellspaces
18161  	 	 	 	 	 	 $table_columns_width = ($table_width - ($cellspacing['H'] * ($dom[$key]['cols'] - 1)));
18162  	 	 	 	 	 	 // minimum column width
18163  	 	 	 	 	 	 $table_min_column_width = ($table_columns_width / $dom[$key]['cols']);
18164  	 	 	 	 	 	 // array of custom column widths
18165  	 	 	 	 	 	 $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
18166  	 	 	 	 	 }
18167  	 	 	 	 	 // table row
18168  	 	 	 	 	 if ($dom[$key]['value'] == 'tr') {
18169  	 	 	 	 	 	 // reset column counter
18170  	 	 	 	 	 	 $colid = 0;
18171  	 	 	 	 	 }
18172  	 	 	 	 	 // table cell
18173  	 	 	 	 	 if (($dom[$key]['value'] == 'td') OR ($dom[$key]['value'] == 'th')) {
18174  	 	 	 	 	 	 $trid = $dom[$key]['parent'];
18175  	 	 	 	 	 	 $table_el = $dom[$trid]['parent'];
18176  	 	 	 	 	 	 if (!isset($dom[$table_el]['cols'])) {
18177  	 	 	 	 	 	 	 $dom[$table_el]['cols'] = $dom[$trid]['cols'];
18178  	 	 	 	 	 	 }
18179  	 	 	 	 	 	 // store border info
18180  	 	 	 	 	 	 $tdborder = 0;
18181  	 	 	 	 	 	 if (isset($dom[$key]['border']) AND !empty($dom[$key]['border'])) {
18182  	 	 	 	 	 	 	 $tdborder = $dom[$key]['border'];
18183  	 	 	 	 	 	 }
18184  	 	 	 	 	 	 $colspan = intval($dom[$key]['attribute']['colspan']);
18185  	 	 	 	 	 	 if ($colspan <= 0) {
18186  	 	 	 	 	 	 	 $colspan = 1;
18187  	 	 	 	 	 	 }
18188  	 	 	 	 	 	 $old_cell_padding = $this->cell_padding;
18189  	 	 	 	 	 	 if (isset($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'])) {
18190  	 	 	 	 	 	 	 $crclpd = $this->getHTMLUnitToUnits($dom[($dom[$trid]['parent'])]['attribute']['cellpadding'], 1, 'px');
18191  	 	 	 	 	 	 	 $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
18192  	 	 	 	 	 	 } elseif (isset($dom[($dom[$trid]['parent'])]['padding'])) {
18193  	 	 	 	 	 	 	 $current_cell_padding = $dom[($dom[$trid]['parent'])]['padding'];
18194  	 	 	 	 	 	 } else {
18195  	 	 	 	 	 	 	 $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
18196  	 	 	 	 	 	 }
18197  	 	 	 	 	 	 $this->cell_padding = $current_cell_padding;
18198  	 	 	 	 	 	 if (isset($dom[$key]['height'])) {
18199  	 	 	 	 	 	 	 // minimum cell height
18200  	 	 	 	 	 	 	 $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
18201  	 	 	 	 	 	 } else {
18202  	 	 	 	 	 	 	 $cellh = 0;
18203  	 	 	 	 	 	 }
18204  	 	 	 	 	 	 if (isset($dom[$key]['content'])) {
18205  	 	 	 	 	 	 	 $cell_content = $dom[$key]['content'];
18206  	 	 	 	 	 	 } else {
18207  	 	 	 	 	 	 	 $cell_content = '&nbsp;';
18208  	 	 	 	 	 	 }
18209  	 	 	 	 	 	 $tagtype = $dom[$key]['value'];
18210  	 	 	 	 	 	 $parentid = $key;
18211  	 	 	 	 	 	 while (($key < $maxel) AND (!(($dom[$key]['tag']) AND (!$dom[$key]['opening']) AND ($dom[$key]['value'] == $tagtype) AND ($dom[$key]['parent'] == $parentid)))) {
18212  	 	 	 	 	 	 	 // move $key index forward
18213  	 	 	 	 	 	 	 ++$key;
18214  	 	 	 	 	 	 }
18215  	 	 	 	 	 	 if (!isset($dom[$trid]['startpage'])) {
18216  	 	 	 	 	 	 	 $dom[$trid]['startpage'] = $this->page;
18217  	 	 	 	 	 	 } else {
18218  	 	 	 	 	 	 	 $this->setPage($dom[$trid]['startpage']);
18219  	 	 	 	 	 	 }
18220  	 	 	 	 	 	 if (!isset($dom[$trid]['startcolumn'])) {
18221  	 	 	 	 	 	 	 $dom[$trid]['startcolumn'] = $this->current_column;
18222  	 	 	 	 	 	 } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
18223  	 	 	 	 	 	 	 $tmpx = $this->x;
18224  	 	 	 	 	 	 	 $this->selectColumn($dom[$trid]['startcolumn']);
18225  	 	 	 	 	 	 	 $this->x = $tmpx;
18226  	 	 	 	 	 	 }
18227  	 	 	 	 	 	 if (!isset($dom[$trid]['starty'])) {
18228  	 	 	 	 	 	 	 $dom[$trid]['starty'] = $this->y;
18229  	 	 	 	 	 	 } else {
18230  	 	 	 	 	 	 	 $this->y = $dom[$trid]['starty'];
18231  	 	 	 	 	 	 }
18232  	 	 	 	 	 	 if (!isset($dom[$trid]['startx'])) {
18233  	 	 	 	 	 	 	 $dom[$trid]['startx'] = $this->x;
18234  	 	 	 	 	 	 	 $this->x += $cellspacingx;
18235  	 	 	 	 	 	 } else {
18236  	 	 	 	 	 	 	 $this->x += ($cellspacingx / 2);
18237  	 	 	 	 	 	 }
18238  	 	 	 	 	 	 if (isset($dom[$parentid]['attribute']['rowspan'])) {
18239  	 	 	 	 	 	 	 $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
18240  	 	 	 	 	 	 } else {
18241  	 	 	 	 	 	 	 $rowspan = 1;
18242  	 	 	 	 	 	 }
18243  	 	 	 	 	 	 // skip row-spanned cells started on the previous rows
18244  	 	 	 	 	 	 if (isset($dom[$table_el]['rowspans'])) {
18245  	 	 	 	 	 	 	 $rsk = 0;
18246  	 	 	 	 	 	 	 $rskmax = count($dom[$table_el]['rowspans']);
18247  	 	 	 	 	 	 	 while ($rsk < $rskmax) {
18248  	 	 	 	 	 	 	 	 $trwsp = $dom[$table_el]['rowspans'][$rsk];
18249  	 	 	 	 	 	 	 	 $rsstartx = $trwsp['startx'];
18250  	 	 	 	 	 	 	 	 $rsendx = $trwsp['endx'];
18251  	 	 	 	 	 	 	 	 // account for margin changes
18252  	 	 	 	 	 	 	 	 if ($trwsp['startpage'] < $this->page) {
18253  	 	 	 	 	 	 	 	 	 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm'])) {
18254  	 	 	 	 	 	 	 	 	 	 $dl = ($this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm']);
18255  	 	 	 	 	 	 	 	 	 	 $rsstartx -= $dl;
18256  	 	 	 	 	 	 	 	 	 	 $rsendx -= $dl;
18257  	 	 	 	 	 	 	 	 	 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm'])) {
18258  	 	 	 	 	 	 	 	 	 	 $dl = ($this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm']);
18259  	 	 	 	 	 	 	 	 	 	 $rsstartx += $dl;
18260  	 	 	 	 	 	 	 	 	 	 $rsendx += $dl;
18261  	 	 	 	 	 	 	 	 	 }
18262  	 	 	 	 	 	 	 	 }
18263  	 	 	 	 	 	 	 	 if (($trwsp['rowspan'] > 0)
18264  	 	 	 	 	 	 	 	 	 AND ($rsstartx > ($this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps))
18265  	 	 	 	 	 	 	 	 	 AND ($rsstartx < ($this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps))
18266  	 	 	 	 	 	 	 	 	 AND (($trwsp['starty'] < ($this->y - $this->feps)) OR ($trwsp['startpage'] < $this->page) OR ($trwsp['startcolumn'] < $this->current_column))) {
18267  	 	 	 	 	 	 	 	 	 // set the starting X position of the current cell
18268  	 	 	 	 	 	 	 	 	 $this->x = $rsendx + $cellspacingx;
18269  	 	 	 	 	 	 	 	 	 // increment column indicator
18270  	 	 	 	 	 	 	 	 	 $colid += $trwsp['colspan'];
18271  	 	 	 	 	 	 	 	 	 if (($trwsp['rowspan'] == 1)
18272  	 	 	 	 	 	 	 	 	 	 AND (isset($dom[$trid]['endy']))
18273  	 	 	 	 	 	 	 	 	 	 AND (isset($dom[$trid]['endpage']))
18274  	 	 	 	 	 	 	 	 	 	 AND (isset($dom[$trid]['endcolumn']))
18275  	 	 	 	 	 	 	 	 	 	 AND ($trwsp['endpage'] == $dom[$trid]['endpage'])
18276  	 	 	 	 	 	 	 	 	 	 AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18277  	 	 	 	 	 	 	 	 	 	 // set ending Y position for row
18278  	 	 	 	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18279  	 	 	 	 	 	 	 	 	 	 $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
18280  	 	 	 	 	 	 	 	 	 }
18281  	 	 	 	 	 	 	 	 	 $rsk = 0;
18282  	 	 	 	 	 	 	 	 } else {
18283  	 	 	 	 	 	 	 	 	 ++$rsk;
18284  	 	 	 	 	 	 	 	 }
18285  	 	 	 	 	 	 	 }
18286  	 	 	 	 	 	 }
18287  	 	 	 	 	 	 if (isset($dom[$parentid]['width'])) {
18288  	 	 	 	 	 	 	 // user specified width
18289  	 	 	 	 	 	 	 $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
18290  	 	 	 	 	 	 	 $tmpcw = ($cellw / $colspan);
18291  	 	 	 	 	 	 	 for ($i = 0; $i < $colspan; ++$i) {
18292  	 	 	 	 	 	 	 	 $table_colwidths[($colid + $i)] = $tmpcw;
18293  	 	 	 	 	 	 	 }
18294  	 	 	 	 	 	 } else {
18295  	 	 	 	 	 	 	 // inherit column width
18296  	 	 	 	 	 	 	 $cellw = 0;
18297  	 	 	 	 	 	 	 for ($i = 0; $i < $colspan; ++$i) {
18298  	 	 	 	 	 	 	 	 $cellw += (isset($table_colwidths[($colid + $i)]) ? $table_colwidths[($colid + $i)] : 0);
18299  	 	 	 	 	 	 	 }
18300  	 	 	 	 	 	 }
18301  	 	 	 	 	 	 $cellw += (($colspan - 1) * $cellspacing['H']);
18302  	 	 	 	 	 	 // increment column indicator
18303  	 	 	 	 	 	 $colid += $colspan;
18304  	 	 	 	 	 	 // add rowspan information to table element
18305  	 	 	 	 	 	 if ($rowspan > 1) {
18306  	 	 	 	 	 	 	 $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y));
18307  	 	 	 	 	 	 }
18308  	 	 	 	 	 	 $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
18309  	 	 	 	 	 	 if ($rowspan > 1) {
18310  	 	 	 	 	 	 	 $dom[$trid]['cellpos'][($cellid - 1)]['rowspanid'] = ($trsid - 1);
18311  	 	 	 	 	 	 }
18312  	 	 	 	 	 	 // push background colors
18313  	 	 	 	 	 	 if (isset($dom[$parentid]['bgcolor']) AND ($dom[$parentid]['bgcolor'] !== false)) {
18314  	 	 	 	 	 	 	 $dom[$trid]['cellpos'][($cellid - 1)]['bgcolor'] = $dom[$parentid]['bgcolor'];
18315  	 	 	 	 	 	 }
18316  	 	 	 	 	 	 // store border info
18317  	 	 	 	 	 	 if (isset($tdborder) AND !empty($tdborder)) {
18318  	 	 	 	 	 	 	 $dom[$trid]['cellpos'][($cellid - 1)]['border'] = $tdborder;
18319  	 	 	 	 	 	 }
18320  	 	 	 	 	 	 $prevLastH = $this->lasth;
18321  	 	 	 	 	 	 // store some info for multicolumn mode
18322  	 	 	 	 	 	 if ($this->rtl) {
18323  	 	 	 	 	 	 	 $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
18324  	 	 	 	 	 	 } else {
18325  	 	 	 	 	 	 	 $this->colxshift['x'] = $this->x - $this->lMargin;
18326  	 	 	 	 	 	 }
18327  	 	 	 	 	 	 $this->colxshift['s'] = $cellspacing;
18328  	 	 	 	 	 	 $this->colxshift['p'] = $current_cell_padding;
18329  	 	 	 	 	 	 // ****** write the cell content ******
18330  	 	 	 	 	 	 $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
18331  	 	 	 	 	 	 // restore some values
18332  	 	 	 	 	 	 $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
18333  	 	 	 	 	 	 $this->lasth = $prevLastH;
18334  	 	 	 	 	 	 $this->cell_padding = $old_cell_padding;
18335  	 	 	 	 	 	 $dom[$trid]['cellpos'][($cellid - 1)]['endx'] = $this->x;
18336  	 	 	 	 	 	 // update the end of row position
18337  	 	 	 	 	 	 if ($rowspan <= 1) {
18338  	 	 	 	 	 	 	 if (isset($dom[$trid]['endy'])) {
18339  	 	 	 	 	 	 	 	 if (($this->page == $dom[$trid]['endpage']) AND ($this->current_column == $dom[$trid]['endcolumn'])) {
18340  	 	 	 	 	 	 	 	 	 $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
18341  	 	 	 	 	 	 	 	 } elseif (($this->page > $dom[$trid]['endpage']) OR ($this->current_column > $dom[$trid]['endcolumn'])) {
18342  	 	 	 	 	 	 	 	 	 $dom[$trid]['endy'] = $this->y;
18343  	 	 	 	 	 	 	 	 }
18344  	 	 	 	 	 	 	 } else {
18345  	 	 	 	 	 	 	 	 $dom[$trid]['endy'] = $this->y;
18346  	 	 	 	 	 	 	 }
18347  	 	 	 	 	 	 	 if (isset($dom[$trid]['endpage'])) {
18348  	 	 	 	 	 	 	 	 $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
18349  	 	 	 	 	 	 	 } else {
18350  	 	 	 	 	 	 	 	 $dom[$trid]['endpage'] = $this->page;
18351  	 	 	 	 	 	 	 }
18352  	 	 	 	 	 	 	 if (isset($dom[$trid]['endcolumn'])) {
18353  	 	 	 	 	 	 	 	 $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
18354  	 	 	 	 	 	 	 } else {
18355  	 	 	 	 	 	 	 	 $dom[$trid]['endcolumn'] = $this->current_column;
18356  	 	 	 	 	 	 	 }
18357  	 	 	 	 	 	 } else {
18358  	 	 	 	 	 	 	 // account for row-spanned cells
18359  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][($trsid - 1)]['endx'] = $this->x;
18360  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][($trsid - 1)]['endy'] = $this->y;
18361  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][($trsid - 1)]['endpage'] = $this->page;
18362  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][($trsid - 1)]['endcolumn'] = $this->current_column;
18363  	 	 	 	 	 	 }
18364  	 	 	 	 	 	 if (isset($dom[$table_el]['rowspans'])) {
18365  	 	 	 	 	 	 	 // update endy and endpage on rowspanned cells
18366  	 	 	 	 	 	 	 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
18367  	 	 	 	 	 	 	 	 if ($trwsp['rowspan'] > 0) {
18368  	 	 	 	 	 	 	 	 	 if (isset($dom[$trid]['endpage'])) {
18369  	 	 	 	 	 	 	 	 	 	 if (($trwsp['endpage'] == $dom[$trid]['endpage']) AND ($trwsp['endcolumn'] == $dom[$trid]['endcolumn'])) {
18370  	 	 	 	 	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
18371  	 	 	 	 	 	 	 	 	 	 } elseif (($trwsp['endpage'] < $dom[$trid]['endpage']) OR ($trwsp['endcolumn'] < $dom[$trid]['endcolumn'])) {
18372  	 	 	 	 	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
18373  	 	 	 	 	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
18374  	 	 	 	 	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
18375  	 	 	 	 	 	 	 	 	 	 } else {
18376  	 	 	 	 	 	 	 	 	 	 	 $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
18377  	 	 	 	 	 	 	 	 	 	 }
18378  	 	 	 	 	 	 	 	 	 }
18379  	 	 	 	 	 	 	 	 }
18380  	 	 	 	 	 	 	 }
18381  	 	 	 	 	 	 }
18382  	 	 	 	 	 	 $this->x += ($cellspacingx / 2);
18383  	 	 	 	 	 } else {
18384  	 	 	 	 	 	 // opening tag (or self-closing tag)
18385  	 	 	 	 	 	 if (!isset($opentagpos)) {
18386  	 	 	 	 	 	 	 if ($this->inxobj) {
18387  	 	 	 	 	 	 	 	 // we are inside an XObject template
18388  	 	 	 	 	 	 	 	 $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
18389  	 	 	 	 	 	 	 } elseif (!$this->InFooter) {
18390  	 	 	 	 	 	 	 	 if (isset($this->footerlen[$this->page])) {
18391  	 	 	 	 	 	 	 	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
18392  	 	 	 	 	 	 	 	 } else {
18393  	 	 	 	 	 	 	 	 	 $this->footerpos[$this->page] = $this->pagelen[$this->page];
18394  	 	 	 	 	 	 	 	 }
18395  	 	 	 	 	 	 	 	 $opentagpos = $this->footerpos[$this->page];
18396  	 	 	 	 	 	 	 }
18397  	 	 	 	 	 	 }
18398  	 	 	 	 	 	 $dom = $this->openHTMLTagHandler($dom, $key, $cell);
18399  	 	 	 	 	 }
18400  	 	 	 	 } else { // closing tag
18401  	 	 	 	 	 $prev_numpages = $this->numpages;
18402  	 	 	 	 	 $old_bordermrk = $this->bordermrk[$this->page];
18403  	 	 	 	 	 $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
18404  	 	 	 	 	 if ($this->bordermrk[$this->page] > $old_bordermrk) {
18405  	 	 	 	 	 	 $startlinepos += ($this->bordermrk[$this->page] - $old_bordermrk);
18406  	 	 	 	 	 }
18407  	 	 	 	 	 if ($prev_numpages > $this->numpages) {
18408  	 	 	 	 	 	 $startlinepage = $this->page;
18409  	 	 	 	 	 }
18410  	 	 	 	 }
18411  	 	 	 } elseif (strlen($dom[$key]['value']) > 0) {
18412  	 	 	 	 // print list-item
18413  	 	 	 	 if (!TCPDF_STATIC::empty_string($this->lispacer) AND ($this->lispacer != '^')) {
18414  	 	 	 	 	 $this->SetFont($pfontname, $pfontstyle, $pfontsize);
18415  	 	 	 	 	 $this->resetLastH();
18416  	 	 	 	 	 $minstartliney = $this->y;
18417  	 	 	 	 	 $maxbottomliney = ($startliney + $this->getCellHeight($this->FontSize));
18418  	 	 	 	 	 if (is_numeric($pfontsize) AND ($pfontsize > 0)) {
18419  	 	 	 	 	 	 $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
18420  	 	 	 	 	 }
18421  	 	 	 	 	 $this->SetFont($curfontname, $curfontstyle, $curfontsize);
18422  	 	 	 	 	 $this->resetLastH();
18423  	 	 	 	 	 if (is_numeric($pfontsize) AND ($pfontsize > 0) AND is_numeric($curfontsize) AND ($curfontsize > 0) AND ($pfontsize != $curfontsize)) {
18424  	 	 	 	 	 	 $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
18425  	 	 	 	 	 	 $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
18426  	 	 	 	 	 	 $this->y += ($this->getCellHeight(($pfontsize - $curfontsize) / $this->k) + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
18427  	 	 	 	 	 	 $minstartliney = min($this->y, $minstartliney);
18428  	 	 	 	 	 	 $maxbottomliney = max(($this->y + $this->getCellHeight($pfontsize / $this->k)), $maxbottomliney);
18429  	 	 	 	 	 }
18430  	 	 	 	 }
18431  	 	 	 	 // text
18432  	 	 	 	 $this->htmlvspace = 0;
18433  	 	 	 	 $isRTLString = preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_RTL, $dom[$key]['value']) || preg_match(TCPDF_FONT_DATA::$uni_RE_PATTERN_ARABIC, $dom[$key]['value']);
18434  	 	 	 	 if ((!$this->premode) AND $this->isRTLTextDir() AND !$isRTLString) {
18435  	 	 	 	 	 // reverse spaces order
18436  	 	 	 	 	 $lsp = ''; // left spaces
18437  	 	 	 	 	 $rsp = ''; // right spaces
18438  	 	 	 	 	 if (preg_match('/^('.$this->re_space['p'].'+)/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18439  	 	 	 	 	 	 $lsp = $matches[1];
18440  	 	 	 	 	 }
18441  	 	 	 	 	 if (preg_match('/('.$this->re_space['p'].'+)$/'.$this->re_space['m'], $dom[$key]['value'], $matches)) {
18442  	 	 	 	 	 	 $rsp = $matches[1];
18443  	 	 	 	 	 }
18444  	 	 	 	 	 $dom[$key]['value'] = $rsp.$this->stringTrim($dom[$key]['value']).$lsp;
18445  	 	 	 	 }
18446  	 	 	 	 if ($newline) {
18447  	 	 	 	 	 if (!$this->premode) {
18448  	 	 	 	 	 	 $prelen = strlen($dom[$key]['value']);
18449  	 	 	 	 	 	 if ($this->isRTLTextDir() AND !$isRTLString) {
18450  	 	 	 	 	 	 	 // right trim except non-breaking space
18451  	 	 	 	 	 	 	 $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
18452  	 	 	 	 	 	 } else {
18453  	 	 	 	 	 	 	 // left trim except non-breaking space
18454  	 	 	 	 	 	 	 $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
18455  	 	 	 	 	 	 }
18456  	 	 	 	 	 	 $postlen = strlen($dom[$key]['value']);
18457  	 	 	 	 	 	 if (($postlen == 0) AND ($prelen > 0)) {
18458  	 	 	 	 	 	 	 $dom[$key]['trimmed_space'] = true;
18459  	 	 	 	 	 	 }
18460  	 	 	 	 	 }
18461  	 	 	 	 	 $newline = false;
18462  	 	 	 	 	 $firstblock = true;
18463  	 	 	 	 } else {
18464  	 	 	 	 	 $firstblock = false;
18465  	 	 	 	 	 // replace empty multiple spaces string with a single space
18466  	 	 	 	 	 $dom[$key]['value'] = preg_replace('/^'.$this->re_space['p'].'+$/'.$this->re_space['m'], chr(32), $dom[$key]['value']);
18467  	 	 	 	 }
18468  	 	 	 	 $strrest = '';
18469  	 	 	 	 if ($this->rtl) {
18470  	 	 	 	 	 $this->x -= $this->textindent;
18471  	 	 	 	 } else {
18472  	 	 	 	 	 $this->x += $this->textindent;
18473  	 	 	 	 }
18474  	 	 	 	 if (!isset($dom[$key]['trimmed_space']) OR !$dom[$key]['trimmed_space']) {
18475  	 	 	 	 	 $strlinelen = $this->GetStringWidth($dom[$key]['value']);
18476  	 	 	 	 	 if (!empty($this->HREF) AND (isset($this->HREF['url']))) {
18477  	 	 	 	 	 	 // HTML <a> Link
18478  	 	 	 	 	 	 $hrefcolor = '';
18479  	 	 	 	 	 	 if (isset($dom[($dom[$key]['parent'])]['fgcolor']) AND ($dom[($dom[$key]['parent'])]['fgcolor'] !== false)) {
18480  	 	 	 	 	 	 	 $hrefcolor = $dom[($dom[$key]['parent'])]['fgcolor'];
18481  	 	 	 	 	 	 }
18482  	 	 	 	 	 	 $hrefstyle = -1;
18483  	 	 	 	 	 	 if (isset($dom[($dom[$key]['parent'])]['fontstyle']) AND ($dom[($dom[$key]['parent'])]['fontstyle'] !== false)) {
18484  	 	 	 	 	 	 	 $hrefstyle = $dom[($dom[$key]['parent'])]['fontstyle'];
18485  	 	 	 	 	 	 }
18486  	 	 	 	 	 	 $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
18487  	 	 	 	 	 } else {
18488  	 	 	 	 	 	 $wadj = 0; // space to leave for block continuity
18489  	 	 	 	 	 	 if ($this->rtl) {
18490  	 	 	 	 	 	 	 $cwa = ($this->x - $this->lMargin);
18491  	 	 	 	 	 	 } else {
18492  	 	 	 	 	 	 	 $cwa = ($this->w - $this->rMargin - $this->x);
18493  	 	 	 	 	 	 }
18494  	 	 	 	 	 	 if (($strlinelen < $cwa) AND (isset($dom[($key + 1)])) AND ($dom[($key + 1)]['tag']) AND (!$dom[($key + 1)]['block'])) {
18495  	 	 	 	 	 	 	 // check the next text blocks for continuity
18496  	 	 	 	 	 	 	 $nkey = ($key + 1);
18497  	 	 	 	 	 	 	 $write_block = true;
18498  	 	 	 	 	 	 	 $same_textdir = true;
18499  	 	 	 	 	 	 	 $tmp_fontname = $this->FontFamily;
18500  	 	 	 	 	 	 	 $tmp_fontstyle = $this->FontStyle;
18501  	 	 	 	 	 	 	 $tmp_fontsize = $this->FontSizePt;
18502  	 	 	 	 	 	 	 while ($write_block AND isset($dom[$nkey])) {
18503  	 	 	 	 	 	 	 	 if ($dom[$nkey]['tag']) {
18504  	 	 	 	 	 	 	 	 	 if ($dom[$nkey]['block']) {
18505  	 	 	 	 	 	 	 	 	 	 // end of block
18506  	 	 	 	 	 	 	 	 	 	 $write_block = false;
18507  	 	 	 	 	 	 	 	 	 }
18508  	 	 	 	 	 	 	 	 	 $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
18509  	 	 	 	 	 	 	 	 	 $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
18510  	 	 	 	 	 	 	 	 	 $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
18511  	 	 	 	 	 	 	 	 	 $same_textdir = ($dom[$nkey]['dir'] == $dom[$key]['dir']);
18512  	 	 	 	 	 	 	 	 } else {
18513  	 	 	 	 	 	 	 	 	 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'+/', $this->re_space['m'], $dom[$nkey]['value']);
18514  	 	 	 	 	 	 	 	 	 if (isset($nextstr[0]) AND $same_textdir) {
18515  	 	 	 	 	 	 	 	 	 	 $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
18516  	 	 	 	 	 	 	 	 	 	 if (isset($nextstr[1])) {
18517  	 	 	 	 	 	 	 	 	 	 	 $write_block = false;
18518  	 	 	 	 	 	 	 	 	 	 }
18519  	 	 	 	 	 	 	 	 	 }
18520  	 	 	 	 	 	 	 	 }
18521  	 	 	 	 	 	 	 	 ++$nkey;
18522  	 	 	 	 	 	 	 }
18523  	 	 	 	 	 	 }
18524  	 	 	 	 	 	 if (($wadj > 0) AND (($strlinelen + $wadj) >= $cwa)) {
18525  	 	 	 	 	 	 	 $wadj = 0;
18526  	 	 	 	 	 	 	 $nextstr = TCPDF_STATIC::pregSplit('/'.$this->re_space['p'].'/', $this->re_space['m'], $dom[$key]['value']);
18527  	 	 	 	 	 	 	 $numblks = count($nextstr);
18528  	 	 	 	 	 	 	 if ($numblks > 1) {
18529  	 	 	 	 	 	 	 	 // try to split on blank spaces
18530  	 	 	 	 	 	 	 	 $wadj = ($cwa - $strlinelen + $this->GetStringWidth($nextstr[($numblks - 1)]));
18531  	 	 	 	 	 	 	 } else {
18532  	 	 	 	 	 	 	 	 // set the entire block on new line
18533  	 	 	 	 	 	 	 	 $wadj = $this->GetStringWidth($nextstr[0]);
18534  	 	 	 	 	 	 	 }
18535  	 	 	 	 	 	 }
18536  	 	 	 	 	 	 // check for reversed text direction
18537  	 	 	 	 	 	 if (($wadj > 0) AND (($this->rtl AND ($this->tmprtl === 'L')) OR (!$this->rtl AND ($this->tmprtl === 'R')))) {
18538  	 	 	 	 	 	 	 // LTR text on RTL direction or RTL text on LTR direction
18539  	 	 	 	 	 	 	 $reverse_dir = true;
18540  	 	 	 	 	 	 	 $this->rtl = !$this->rtl;
18541  	 	 	 	 	 	 	 $revshift = ($strlinelen + $wadj + 0.000001); // add little quantity for rounding problems
18542  	 	 	 	 	 	 	 if ($this->rtl) {
18543  	 	 	 	 	 	 	 	 $this->x += $revshift;
18544  	 	 	 	 	 	 	 } else {
18545  	 	 	 	 	 	 	 	 $this->x -= $revshift;
18546  	 	 	 	 	 	 	 }
18547  	 	 	 	 	 	 	 $xws = $this->x;
18548  	 	 	 	 	 	 }
18549  	 	 	 	 	 	 // ****** write only until the end of the line and get the rest ******
18550  	 	 	 	 	 	 $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
18551  	 	 	 	 	 	 // restore default direction
18552  	 	 	 	 	 	 if ($reverse_dir AND ($wadj == 0)) {
18553  	 	 	 	 	 	 	 $this->x = $xws;
18554  	 	 	 	 	 	 	 $this->rtl = !$this->rtl;
18555  	 	 	 	 	 	 	 $reverse_dir = false;
18556  	 	 	 	 	 	 }
18557  	 	 	 	 	 }
18558  	 	 	 	 }
18559  	 	 	 	 $this->textindent = 0;
18560  	 	 	 	 if (strlen($strrest) > 0) {
18561  	 	 	 	 	 // store the remaining string on the previous $key position
18562  	 	 	 	 	 $this->newline = true;
18563  	 	 	 	 	 if ($strrest == $dom[$key]['value']) {
18564  	 	 	 	 	 	 // used to avoid infinite loop
18565  	 	 	 	 	 	 ++$loop;
18566  	 	 	 	 	 } else {
18567  	 	 	 	 	 	 $loop = 0;
18568  	 	 	 	 	 }
18569  	 	 	 	 	 $dom[$key]['value'] = $strrest;
18570  	 	 	 	 	 if ($cell) {
18571  	 	 	 	 	 	 if ($this->rtl) {
18572  	 	 	 	 	 	 	 $this->x -= $this->cell_padding['R'];
18573  	 	 	 	 	 	 } else {
18574  	 	 	 	 	 	 	 $this->x += $this->cell_padding['L'];
18575  	 	 	 	 	 	 }
18576  	 	 	 	 	 }
18577  	 	 	 	 	 if ($loop < 3) {
18578  	 	 	 	 	 	 --$key;
18579  	 	 	 	 	 }
18580  	 	 	 	 } else {
18581  	 	 	 	 	 $loop = 0;
18582  	 	 	 	 	 // add the positive font spacing of the last character (if any)
18583  	 	 	 	 	  if ($this->font_spacing > 0) {
18584  	 	 	 	 	  	 if ($this->rtl) {
18585  	 	 	 	 	 	 	 $this->x -= $this->font_spacing;
18586  	 	 	 	 	 	 } else {
18587  	 	 	 	 	 	 	 $this->x += $this->font_spacing;
18588  	 	 	 	 	 	 }
18589  	 	 	 	 	 }
18590  	 	 	 	 }
18591  	 	 	 }
18592  	 	 	 ++$key;
18593  	 	 	 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')) {
18594  	 	 	 	 // check if we are on a new page or on a new column
18595  	 	 	 	 if ((!$undo) AND (($this->y < $this->start_transaction_y) OR (($dom[$key]['value'] == 'tr') AND ($dom[($dom[$key]['parent'])]['endy'] < $this->start_transaction_y)))) {
18596  	 	 	 	 	 // we are on a new page or on a new column and the total object height is less than the available vertical space.
18597  	 	 	 	 	 // restore previous object
18598  	 	 	 	 	 $this->rollbackTransaction(true);
18599  	 	 	 	 	 // restore previous values
18600  	 	 	 	 	 foreach ($this_method_vars as $vkey => $vval) {
18601  	 	 	 	 	 	 $$vkey = $vval;
18602  	 	 	 	 	 }
18603  	 	 	 	 	 if (!empty($dom[$key]['thead'])) {
18604  	 	 	 	 	 	 $this->inthead = true;
18605  	 	 	 	 	 }
18606  	 	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18607  	 	 	 	 	 $pre_y = $this->y;
18608  	 	 	 	 	 if ((!$this->checkPageBreak($this->PageBreakTrigger + 1)) AND ($this->y < $pre_y)) {
18609  	 	 	 	 	 	 $startliney = $this->y;
18610  	 	 	 	 	 }
18611  	 	 	 	 	 $undo = true; // avoid infinite loop
18612  	 	 	 	 } else {
18613  	 	 	 	 	 $undo = false;
18614  	 	 	 	 }
18615  	 	 	 }
18616  	 	 } // end for each $key
18617  	 	 // align the last line
18618  	 	 if (isset($startlinex)) {
18619  	 	 	 $yshift = ($minstartliney - $startliney);
18620  	 	 	 if (($yshift > 0) OR ($this->page > $startlinepage)) {
18621  	 	 	 	 $yshift = 0;
18622  	 	 	 }
18623  	 	 	 $t_x = 0;
18624  	 	 	 // the last line must be shifted to be aligned as requested
18625  	 	 	 $linew = abs($this->endlinex - $startlinex);
18626  	 	 	 if ($this->inxobj) {
18627  	 	 	 	 // we are inside an XObject template
18628  	 	 	 	 $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
18629  	 	 	 	 if (isset($opentagpos)) {
18630  	 	 	 	 	 $midpos = $opentagpos;
18631  	 	 	 	 } else {
18632  	 	 	 	 	 $midpos = 0;
18633  	 	 	 	 }
18634  	 	 	 	 if ($midpos > 0) {
18635  	 	 	 	 	 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, ($midpos - $startlinepos));
18636  	 	 	 	 	 $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
18637  	 	 	 	 } else {
18638  	 	 	 	 	 $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
18639  	 	 	 	 	 $pend = '';
18640  	 	 	 	 }
18641  	 	 	 } else {
18642  	 	 	 	 $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
18643  	 	 	 	 if (isset($opentagpos) AND isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18644  	 	 	 	 	 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18645  	 	 	 	 	 $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
18646  	 	 	 	 } elseif (isset($opentagpos)) {
18647  	 	 	 	 	 $midpos = $opentagpos;
18648  	 	 	 	 } elseif (isset($this->footerlen[$startlinepage]) AND (!$this->InFooter)) {
18649  	 	 	 	 	 $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
18650  	 	 	 	 	 $midpos = $this->footerpos[$startlinepage];
18651  	 	 	 	 } else {
18652  	 	 	 	 	 $midpos = 0;
18653  	 	 	 	 }
18654  	 	 	 	 if ($midpos > 0) {
18655  	 	 	 	 	 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, ($midpos - $startlinepos));
18656  	 	 	 	 	 $pend = substr($this->getPageBuffer($startlinepage), $midpos);
18657  	 	 	 	 } else {
18658  	 	 	 	 	 $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
18659  	 	 	 	 	 $pend = '';
18660  	 	 	 	 }
18661  	 	 	 }
18662  	 	 	 if ((isset($plalign) AND ((($plalign == 'C') OR (($plalign == 'R') AND (!$this->rtl)) OR (($plalign == 'L') AND ($this->rtl)))))) {
18663  	 	 	 	 // calculate shifting amount
18664  	 	 	 	 $tw = $w;
18665  	 	 	 	 if ($this->lMargin != $prevlMargin) {
18666  	 	 	 	 	 $tw += ($prevlMargin - $this->lMargin);
18667  	 	 	 	 }
18668  	 	 	 	 if ($this->rMargin != $prevrMargin) {
18669  	 	 	 	 	 $tw += ($prevrMargin - $this->rMargin);
18670  	 	 	 	 }
18671  	 	 	 	 $one_space_width = $this->GetStringWidth(chr(32));
18672  	 	 	 	 $no = 0; // number of spaces on a line contained on a single block
18673  	 	 	 	 if ($this->isRTLTextDir()) { // RTL
18674  	 	 	 	 	 // remove left space if exist
18675  	 	 	 	 	 $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
18676  	 	 	 	 	 if ($pos1 > 0) {
18677  	 	 	 	 	 	 $pos1 = intval($pos1);
18678  	 	 	 	 	 	 if ($this->isUnicodeFont()) {
18679  	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(0).chr(32)));
18680  	 	 	 	 	 	 	 $spacelen = 2;
18681  	 	 	 	 	 	 } else {
18682  	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[('.chr(32)));
18683  	 	 	 	 	 	 	 $spacelen = 1;
18684  	 	 	 	 	 	 }
18685  	 	 	 	 	 	 if ($pos1 == $pos2) {
18686  	 	 	 	 	 	 	 $pmid = substr($pmid, 0, ($pos1 + 2)).substr($pmid, ($pos1 + 2 + $spacelen));
18687  	 	 	 	 	 	 	 if (substr($pmid, $pos1, 4) == '[()]') {
18688  	 	 	 	 	 	 	 	 $linew -= $one_space_width;
18689  	 	 	 	 	 	 	 } elseif ($pos1 == strpos($pmid, '[(')) {
18690  	 	 	 	 	 	 	 	 $no = 1;
18691  	 	 	 	 	 	 	 }
18692  	 	 	 	 	 	 }
18693  	 	 	 	 	 }
18694  	 	 	 	 } else { // LTR
18695  	 	 	 	 	 // remove right space if exist
18696  	 	 	 	 	 $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
18697  	 	 	 	 	 if ($pos1 > 0) {
18698  	 	 	 	 	 	 $pos1 = intval($pos1);
18699  	 	 	 	 	 	 if ($this->isUnicodeFont()) {
18700  	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0).chr(32).')]')) + 2;
18701  	 	 	 	 	 	 	 $spacelen = 2;
18702  	 	 	 	 	 	 } else {
18703  	 	 	 	 	 	 	 $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32).')]')) + 1;
18704  	 	 	 	 	 	 	 $spacelen = 1;
18705  	 	 	 	 	 	 }
18706  	 	 	 	 	 	 if ($pos1 == $pos2) {
18707  	 	 	 	 	 	 	 $pmid = substr($pmid, 0, ($pos1 - $spacelen)).substr($pmid, $pos1);
18708  	 	 	 	 	 	 	 $linew -= $one_space_width;
18709  	 	 	 	 	 	 }
18710  	 	 	 	 	 }
18711  	 	 	 	 }
18712  	 	 	 	 $mdiff = ($tw - $linew);
18713  	 	 	 	 if ($plalign == 'C') {
18714  	 	 	 	 	 if ($this->rtl) {
18715  	 	 	 	 	 	 $t_x = -($mdiff / 2);
18716  	 	 	 	 	 } else {
18717  	 	 	 	 	 	 $t_x = ($mdiff / 2);
18718  	 	 	 	 	 }
18719  	 	 	 	 } elseif ($plalign == 'R') {
18720  	 	 	 	 	 // right alignment on LTR document
18721  	 	 	 	 	 $t_x = $mdiff;
18722  	 	 	 	 } elseif ($plalign == 'L') {
18723  	 	 	 	 	 // left alignment on RTL document
18724  	 	 	 	 	 $t_x = -$mdiff;
18725  	 	 	 	 }
18726  	 	 	 } // end if startlinex
18727  	 	 	 if (($t_x != 0) OR ($yshift < 0)) {
18728  	 	 	 	 // shift the line
18729  	 	 	 	 $trx = sprintf('1 0 0 1 %F %F cm', ($t_x * $this->k), ($yshift * $this->k));
18730  	 	 	 	 $pstart .= "\nq\n".$trx."\n".$pmid."\nQ\n";
18731  	 	 	 	 $endlinepos = strlen($pstart);
18732  	 	 	 	 if ($this->inxobj) {
18733  	 	 	 	 	 // we are inside an XObject template
18734  	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$pend;
18735  	 	 	 	 	 foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
18736  	 	 	 	 	 	 if ($pak >= $pask) {
18737  	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
18738  	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
18739  	 	 	 	 	 	 }
18740  	 	 	 	 	 }
18741  	 	 	 	 } else {
18742  	 	 	 	 	 $this->setPageBuffer($startlinepage, $pstart.$pend);
18743  	 	 	 	 	 // shift the annotations and links
18744  	 	 	 	 	 if (isset($this->PageAnnots[$this->page])) {
18745  	 	 	 	 	 	 foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
18746  	 	 	 	 	 	 	 if ($pak >= $pask) {
18747  	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
18748  	 	 	 	 	 	 	 	 $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
18749  	 	 	 	 	 	 	 }
18750  	 	 	 	 	 	 }
18751  	 	 	 	 	 }
18752  	 	 	 	 }
18753  	 	 	 	 $this->y -= $yshift;
18754  	 	 	 	 $yshift = 0;
18755  	 	 	 }
18756  	 	 }
18757  	 	 // restore previous values
18758  	 	 $this->setGraphicVars($gvars);
18759  	 	 if ($this->num_columns > 1) {
18760  	 	 	 $this->selectColumn();
18761  	 	 } elseif ($this->page > $prevPage) {
18762  	 	 	 $this->lMargin = $this->pagedim[$this->page]['olm'];
18763  	 	 	 $this->rMargin = $this->pagedim[$this->page]['orm'];
18764  	 	 }
18765  	 	 // restore previous list state
18766  	 	 $this->cell_height_ratio = $prev_cell_height_ratio;
18767  	 	 $this->listnum = $prev_listnum;
18768  	 	 $this->listordered = $prev_listordered;
18769  	 	 $this->listcount = $prev_listcount;
18770  	 	 $this->lispacer = $prev_lispacer;
18771  	 	 if ($ln AND (!($cell AND ($dom[$key-1]['value'] == 'table')))) {
18772  	 	 	 $this->Ln($this->lasth);
18773  	 	 	 if (($this->y < $maxbottomliney) AND ($startlinepage == $this->page)) {
18774  	 	 	 	 $this->y = $maxbottomliney;
18775  	 	 	 }
18776  	 	 }
18777  	 	 unset($dom);
18778  	 }
18779  
18780  	 /**
18781  	  * Process opening tags.
18782  	  * @param $dom (array) html dom array
18783  	  * @param $key (int) current element id
18784  	  * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
18785  	  * @return $dom array
18786  	  * @protected
18787  	  */
18788  	protected function openHTMLTagHandler($dom, $key, $cell) {
18789  	 	 $tag = $dom[$key];
18790  	 	 $parent = $dom[($dom[$key]['parent'])];
18791  	 	 $firsttag = ($key == 1);
18792  	 	 // check for text direction attribute
18793  	 	 if (isset($tag['dir'])) {
18794  	 	 	 $this->setTempRTL($tag['dir']);
18795  	 	 } else {
18796  	 	 	 $this->tmprtl = false;
18797  	 	 }
18798  	 	 if ($tag['block']) {
18799  	 	 	 $hbz = 0; // distance from y to line bottom
18800  	 	 	 $hb = 0; // vertical space between block tags
18801  	 	 	 // calculate vertical space for block tags
18802  	 	 	 if (isset($this->tagvspaces[$tag['value']][0]['h']) && !empty($this->tagvspaces[$tag['value']][0]['h']) && ($this->tagvspaces[$tag['value']][0]['h'] >= 0)) {
18803  	 	 	 	 $cur_h = $this->tagvspaces[$tag['value']][0]['h'];
18804  	 	 	 } elseif (isset($tag['fontsize'])) {
18805  	 	 	 	 $cur_h = $this->getCellHeight($tag['fontsize'] / $this->k);
18806  	 	 	 } else {
18807  	 	 	 	 $cur_h = $this->getCellHeight($this->FontSize);
18808  	 	 	 }
18809  	 	 	 if (isset($this->tagvspaces[$tag['value']][0]['n'])) {
18810  	 	 	 	 $on = $this->tagvspaces[$tag['value']][0]['n'];
18811  	 	 	 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18812  	 	 	 	 $on = 0.6;
18813  	 	 	 } else {
18814  	 	 	 	 $on = 1;
18815  	 	 	 }
18816  	 	 	 if ((!isset($this->tagvspaces[$tag['value']])) AND (in_array($tag['value'], array('div', 'dt', 'dd', 'li', 'br', 'hr')))) {
18817  	 	 	 	 $hb = 0;
18818  	 	 	 } else {
18819  	 	 	 	 $hb = ($on * $cur_h);
18820  	 	 	 }
18821  	 	 	 if (($this->htmlvspace <= 0) AND ($on > 0)) {
18822  	 	 	 	 if (isset($parent['fontsize'])) {
18823  	 	 	 	 	 $hbz = (($parent['fontsize'] / $this->k) * $this->cell_height_ratio);
18824  	 	 	 	 } else {
18825  	 	 	 	 	 $hbz = $this->getCellHeight($this->FontSize);
18826  	 	 	 	 }
18827  	 	 	 }
18828  	 	 	 if (isset($dom[($key - 1)]) AND ($dom[($key - 1)]['value'] == 'table')) {
18829  	 	 	 	 // fix vertical space after table
18830  	 	 	 	 $hbz = 0;
18831  	 	 	 }
18832  	 	 	 // closing vertical space
18833  	 	 	 $hbc = 0;
18834  	 	 	 if (isset($this->tagvspaces[$tag['value']][1]['h']) && !empty($this->tagvspaces[$tag['value']][1]['h']) && ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
18835  	 	 	 	 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
18836  	 	 	 } elseif (isset($parent['fontsize'])) {
18837  	 	 	 	 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
18838  	 	 	 } else {
18839  	 	 	 	 $pre_h = $this->getCellHeight($this->FontSize);
18840  	 	 	 }
18841  	 	 	 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
18842  	 	 	 	 $cn = $this->tagvspaces[$tag['value']][1]['n'];
18843  	 	 	 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
18844  	 	 	 	 $cn = 0.6;
18845  	 	 	 } else {
18846  	 	 	 	 $cn = 1;
18847  	 	 	 }
18848  	 	 	 if (isset($this->tagvspaces[$tag['value']][1])) {
18849  	 	 	 	 $hbc = ($cn * $pre_h);
18850  	 	 	 }
18851  	 	 }
18852  	 	 // Opening tag
18853  	 	 switch($tag['value']) {
18854  	 	 	 case 'table': {
18855  	 	 	 	 $cp = 0;
18856  	 	 	 	 $cs = 0;
18857  	 	 	 	 $dom[$key]['rowspans'] = array();
18858  	 	 	 	 if (!isset($dom[$key]['attribute']['nested']) OR ($dom[$key]['attribute']['nested'] != 'true')) {
18859  	 	 	 	 	 $this->htmlvspace = 0;
18860  	 	 	 	 	 // set table header
18861  	 	 	 	 	 if (!TCPDF_STATIC::empty_string($dom[$key]['thead'])) {
18862  	 	 	 	 	 	 // set table header
18863  	 	 	 	 	 	 $this->thead = $dom[$key]['thead'];
18864  	 	 	 	 	 	 if (!isset($this->theadMargins) OR (empty($this->theadMargins))) {
18865  	 	 	 	 	 	 	 $this->theadMargins = array();
18866  	 	 	 	 	 	 	 $this->theadMargins['cell_padding'] = $this->cell_padding;
18867  	 	 	 	 	 	 	 $this->theadMargins['lmargin'] = $this->lMargin;
18868  	 	 	 	 	 	 	 $this->theadMargins['rmargin'] = $this->rMargin;
18869  	 	 	 	 	 	 	 $this->theadMargins['page'] = $this->page;
18870  	 	 	 	 	 	 	 $this->theadMargins['cell'] = $cell;
18871  	 	 	 	 	 	 	 $this->theadMargins['gvars'] = $this->getGraphicVars();
18872  	 	 	 	 	 	 }
18873  	 	 	 	 	 }
18874  	 	 	 	 }
18875  	 	 	 	 // store current margins and page
18876  	 	 	 	 $dom[$key]['old_cell_padding'] = $this->cell_padding;
18877  	 	 	 	 if (isset($tag['attribute']['cellpadding'])) {
18878  	 	 	 	 	 $pad = $this->getHTMLUnitToUnits($tag['attribute']['cellpadding'], 1, 'px');
18879  	 	 	 	 	 $this->SetCellPadding($pad);
18880  	 	 	 	 } elseif (isset($tag['padding'])) {
18881  	 	 	 	 	 $this->cell_padding = $tag['padding'];
18882  	 	 	 	 }
18883  	 	 	 	 if (isset($tag['attribute']['cellspacing'])) {
18884  	 	 	 	 	 $cs = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
18885  	 	 	 	 } elseif (isset($tag['border-spacing'])) {
18886  	 	 	 	 	 $cs = $tag['border-spacing']['V'];
18887  	 	 	 	 }
18888  	 	 	 	 $prev_y = $this->y;
18889  	 	 	 	 if ($this->checkPageBreak(((2 * $cp) + (2 * $cs) + $this->lasth), '', false) OR ($this->y < $prev_y)) {
18890  	 	 	 	 	 $this->inthead = true;
18891  	 	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
18892  	 	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
18893  	 	 	 	 }
18894  	 	 	 	 break;
18895  	 	 	 }
18896  	 	 	 case 'tr': {
18897  	 	 	 	 // array of columns positions
18898  	 	 	 	 $dom[$key]['cellpos'] = array();
18899  	 	 	 	 break;
18900  	 	 	 }
18901  	 	 	 case 'hr': {
18902  	 	 	 	 if ((isset($tag['height'])) AND ($tag['height'] != '')) {
18903  	 	 	 	 	 $hrHeight = $this->getHTMLUnitToUnits($tag['height'], 1, 'px');
18904  	 	 	 	 } else {
18905  	 	 	 	 	 $hrHeight = $this->GetLineWidth();
18906  	 	 	 	 }
18907  	 	 	 	 $this->addHTMLVertSpace($hbz, max($hb, ($hrHeight / 2)), $cell, $firsttag);
18908  	 	 	 	 $x = $this->GetX();
18909  	 	 	 	 $y = $this->GetY();
18910  	 	 	 	 $wtmp = $this->w - $this->lMargin - $this->rMargin;
18911  	 	 	 	 if ($cell) {
18912  	 	 	 	 	 $wtmp -= ($this->cell_padding['L'] + $this->cell_padding['R']);
18913  	 	 	 	 }
18914  	 	 	 	 if ((isset($tag['width'])) AND ($tag['width'] != '')) {
18915  	 	 	 	 	 $hrWidth = $this->getHTMLUnitToUnits($tag['width'], $wtmp, 'px');
18916  	 	 	 	 } else {
18917  	 	 	 	 	 $hrWidth = $wtmp;
18918  	 	 	 	 }
18919  	 	 	 	 $prevlinewidth = $this->GetLineWidth();
18920  	 	 	 	 $this->SetLineWidth($hrHeight);
18921  
18922  	 	 	 	 $lineStyle = array(
18923  	 	 	 	 	 'color' => $tag['fgcolor'],
18924  	 	 	 	 	 'cap'   => $tag['style']['cap'],
18925  	 	 	 	 	 'join'  => $tag['style']['join'],
18926  	 	 	 	 	 'dash'  => $tag['style']['dash'],
18927  	 	 	 	 	 'phase' => $tag['style']['phase'],
18928  	 	 	 	 );
18929  
18930  	 	 	 	 $lineStyle = array_filter($lineStyle);
18931  
18932  	 	 	 	 $this->Line($x, $y, $x + $hrWidth, $y, $lineStyle);
18933  	 	 	 	 $this->SetLineWidth($prevlinewidth);
18934  	 	 	 	 $this->addHTMLVertSpace(max($hbc, ($hrHeight / 2)), 0, $cell, !isset($dom[($key + 1)]));
18935  	 	 	 	 break;
18936  	 	 	 }
18937  	 	 	 case 'a': {
18938  	 	 	 	 if (array_key_exists('href', $tag['attribute'])) {
18939  	 	 	 	 	 $this->HREF['url'] = $tag['attribute']['href'];
18940  	 	 	 	 }
18941  	 	 	 	 break;
18942  	 	 	 }
18943  	 	 	 case 'img': {
18944  	 	 	 	 if (empty($tag['attribute']['src'])) {
18945  	 	 	 	 	 break;
18946  	 	 	 	 }
18947  	 	 	 	 $imgsrc = $tag['attribute']['src'];
18948  	 	 	 	 if ($imgsrc[0] === '@') {
18949  	 	 	 	 	 // data stream
18950  	 	 	 	 	 $imgsrc = '@'.base64_decode(substr($imgsrc, 1));
18951  	 	 	 	 	 $type = '';
18952  	 	 	 	 } elseif ( $this->allowLocalFiles && substr($imgsrc, 0, 7) === 'file://') {
18953                      // get image type from a local file path
18954                      $imgsrc = substr($imgsrc, 7);
18955                      $type = TCPDF_IMAGES::getImageFileType($imgsrc);
18956                  } else {
18957  	 	 	 	 	 if (($imgsrc[0] === '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
18958  	 	 	 	 	 	 // fix image path
18959  	 	 	 	 	 	 $findroot = strpos($imgsrc, $_SERVER['DOCUMENT_ROOT']);
18960  	 	 	 	 	 	 if (($findroot === false) OR ($findroot > 1)) {
18961  	 	 	 	 	 	 	 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
18962  	 	 	 	 	 	 	 	 $imgsrc = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$imgsrc;
18963  	 	 	 	 	 	 	 } else {
18964  	 	 	 	 	 	 	 	 $imgsrc = $_SERVER['DOCUMENT_ROOT'].$imgsrc;
18965  	 	 	 	 	 	 	 }
18966  	 	 	 	 	 	 }
18967  	 	 	 	 	 	 $imgsrc = urldecode($imgsrc);
18968  	 	 	 	 	 	 $testscrtype = @parse_url($imgsrc);
18969  	 	 	 	 	 	 if (empty($testscrtype['query'])) {
18970  	 	 	 	 	 	 	 // convert URL to server path
18971  	 	 	 	 	 	 	 $imgsrc = str_replace(K_PATH_URL, K_PATH_MAIN, $imgsrc);
18972  	 	 	 	 	 	 } elseif (preg_match('|^https?://|', $imgsrc) !== 1) {
18973  	 	 	 	 	 	 	 // convert URL to server path
18974  	 	 	 	 	 	 	 $imgsrc = str_replace(K_PATH_MAIN, K_PATH_URL, $imgsrc);
18975  	 	 	 	 	 	 }
18976  	 	 	 	 	 }
18977  	 	 	 	 	 // get image type
18978  	 	 	 	 	 $type = TCPDF_IMAGES::getImageFileType($imgsrc);
18979  	 	 	 	 }
18980  	 	 	 	 if (!isset($tag['width'])) {
18981  	 	 	 	 	 $tag['width'] = 0;
18982  	 	 	 	 }
18983  	 	 	 	 if (!isset($tag['height'])) {
18984  	 	 	 	 	 $tag['height'] = 0;
18985  	 	 	 	 }
18986  	 	 	 	 //if (!isset($tag['attribute']['align'])) {
18987  	 	 	 	 	 // the only alignment supported is "bottom"
18988  	 	 	 	 	 // further development is required for other modes.
18989  	 	 	 	 	 $tag['attribute']['align'] = 'bottom';
18990  	 	 	 	 //}
18991  	 	 	 	 switch($tag['attribute']['align']) {
18992  	 	 	 	 	 case 'top': {
18993  	 	 	 	 	 	 $align = 'T';
18994  	 	 	 	 	 	 break;
18995  	 	 	 	 	 }
18996  	 	 	 	 	 case 'middle': {
18997  	 	 	 	 	 	 $align = 'M';
18998  	 	 	 	 	 	 break;
18999  	 	 	 	 	 }
19000  	 	 	 	 	 case 'bottom': {
19001  	 	 	 	 	 	 $align = 'B';
19002  	 	 	 	 	 	 break;
19003  	 	 	 	 	 }
19004  	 	 	 	 	 default: {
19005  	 	 	 	 	 	 $align = 'B';
19006  	 	 	 	 	 	 break;
19007  	 	 	 	 	 }
19008  	 	 	 	 }
19009  	 	 	 	 $prevy = $this->y;
19010  	 	 	 	 $xpos = $this->x;
19011  	 	 	 	 $imglink = '';
19012  	 	 	 	 if (isset($this->HREF['url']) AND !TCPDF_STATIC::empty_string($this->HREF['url'])) {
19013  	 	 	 	 	 $imglink = $this->HREF['url'];
19014  	 	 	 	 	 if ($imglink[0] == '#') {
19015  	 	 	 	 	 	 // convert url to internal link
19016  	 	 	 	 	 	 $lnkdata = explode(',', $imglink);
19017  	 	 	 	 	 	 if (isset($lnkdata[0])) {
19018  	 	 	 	 	 	 	 $page = intval(substr($lnkdata[0], 1));
19019  	 	 	 	 	 	 	 if (empty($page) OR ($page <= 0)) {
19020  	 	 	 	 	 	 	 	 $page = $this->page;
19021  	 	 	 	 	 	 	 }
19022  	 	 	 	 	 	 	 if (isset($lnkdata[1]) AND (strlen($lnkdata[1]) > 0)) {
19023  	 	 	 	 	 	 	 	 $lnky = floatval($lnkdata[1]);
19024  	 	 	 	 	 	 	 } else {
19025  	 	 	 	 	 	 	 	 $lnky = 0;
19026  	 	 	 	 	 	 	 }
19027  	 	 	 	 	 	 	 $imglink = $this->AddLink();
19028  	 	 	 	 	 	 	 $this->SetLink($imglink, $lnky, $page);
19029  	 	 	 	 	 	 }
19030  	 	 	 	 	 }
19031  	 	 	 	 }
19032  	 	 	 	 $border = 0;
19033  	 	 	 	 if (isset($tag['border']) AND !empty($tag['border'])) {
19034  	 	 	 	 	 // currently only support 1 (frame) or a combination of 'LTRB'
19035  	 	 	 	 	 $border = $tag['border'];
19036  	 	 	 	 }
19037  	 	 	 	 $iw = '';
19038  	 	 	 	 if (isset($tag['width'])) {
19039  	 	 	 	 	 $iw = $this->getHTMLUnitToUnits($tag['width'], ($tag['fontsize'] / $this->k), 'px', false);
19040  	 	 	 	 }
19041  	 	 	 	 $ih = '';
19042  	 	 	 	 if (isset($tag['height'])) {
19043  	 	 	 	 	 $ih = $this->getHTMLUnitToUnits($tag['height'], ($tag['fontsize'] / $this->k), 'px', false);
19044  	 	 	 	 }
19045  	 	 	 	 if (($type == 'eps') OR ($type == 'ai')) {
19046  	 	 	 	 	 $this->ImageEps($imgsrc, $xpos, $this->y, $iw, $ih, $imglink, true, $align, '', $border, true);
19047  	 	 	 	 } elseif ($type == 'svg') {
19048  	 	 	 	 	 $this->ImageSVG($imgsrc, $xpos, $this->y, $iw, $ih, $imglink, $align, '', $border, true);
19049  	 	 	 	 } else {
19050  	 	 	 	 	 $this->Image($imgsrc, $xpos, $this->y, $iw, $ih, '', $imglink, $align, false, 300, '', false, false, $border, false, false, true);
19051  	 	 	 	 }
19052  	 	 	 	 switch($align) {
19053  	 	 	 	 	 case 'T': {
19054  	 	 	 	 	 	 $this->y = $prevy;
19055  	 	 	 	 	 	 break;
19056  	 	 	 	 	 }
19057  	 	 	 	 	 case 'M': {
19058  	 	 	 	 	 	 $this->y = (($this->img_rb_y + $prevy - ($this->getCellHeight($tag['fontsize'] / $this->k))) / 2);
19059  	 	 	 	 	 	 break;
19060  	 	 	 	 	 }
19061  	 	 	 	 	 case 'B': {
19062  	 	 	 	 	 	 $this->y = $this->img_rb_y - ($this->getCellHeight($tag['fontsize'] / $this->k) - ($this->getFontDescent($tag['fontname'], $tag['fontstyle'], $tag['fontsize']) * $this->cell_height_ratio));
19063  	 	 	 	 	 	 break;
19064  	 	 	 	 	 }
19065  	 	 	 	 }
19066  	 	 	 	 break;
19067  	 	 	 }
19068  	 	 	 case 'dl': {
19069  	 	 	 	 ++$this->listnum;
19070  	 	 	 	 if ($this->listnum == 1) {
19071  	 	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19072  	 	 	 	 } else {
19073  	 	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19074  	 	 	 	 }
19075  	 	 	 	 break;
19076  	 	 	 }
19077  	 	 	 case 'dt': {
19078  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19079  	 	 	 	 break;
19080  	 	 	 }
19081  	 	 	 case 'dd': {
19082  	 	 	 	 if ($this->rtl) {
19083  	 	 	 	 	 $this->rMargin += $this->listindent;
19084  	 	 	 	 } else {
19085  	 	 	 	 	 $this->lMargin += $this->listindent;
19086  	 	 	 	 }
19087  	 	 	 	 ++$this->listindentlevel;
19088  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19089  	 	 	 	 break;
19090  	 	 	 }
19091  	 	 	 case 'ul':
19092  	 	 	 case 'ol': {
19093  	 	 	 	 ++$this->listnum;
19094  	 	 	 	 if ($tag['value'] == 'ol') {
19095  	 	 	 	 	 $this->listordered[$this->listnum] = true;
19096  	 	 	 	 } else {
19097  	 	 	 	 	 $this->listordered[$this->listnum] = false;
19098  	 	 	 	 }
19099  	 	 	 	 if (isset($tag['attribute']['start'])) {
19100  	 	 	 	 	 $this->listcount[$this->listnum] = intval($tag['attribute']['start']) - 1;
19101  	 	 	 	 } else {
19102  	 	 	 	 	 $this->listcount[$this->listnum] = 0;
19103  	 	 	 	 }
19104  	 	 	 	 if ($this->rtl) {
19105  	 	 	 	 	 $this->rMargin += $this->listindent;
19106  	 	 	 	 	 $this->x -= $this->listindent;
19107  	 	 	 	 } else {
19108  	 	 	 	 	 $this->lMargin += $this->listindent;
19109  	 	 	 	 	 $this->x += $this->listindent;
19110  	 	 	 	 }
19111  	 	 	 	 ++$this->listindentlevel;
19112  	 	 	 	 if ($this->listnum == 1) {
19113  	 	 	 	 	 if ($key > 1) {
19114  	 	 	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19115  	 	 	 	 	 }
19116  	 	 	 	 } else {
19117  	 	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, $firsttag);
19118  	 	 	 	 }
19119  	 	 	 	 break;
19120  	 	 	 }
19121  	 	 	 case 'li': {
19122  	 	 	 	 if ($key > 2) {
19123  	 	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19124  	 	 	 	 }
19125  	 	 	 	 if ($this->listordered[$this->listnum]) {
19126  	 	 	 	 	 // ordered item
19127  	 	 	 	 	 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19128  	 	 	 	 	 	 $this->lispacer = $parent['attribute']['type'];
19129  	 	 	 	 	 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19130  	 	 	 	 	 	 $this->lispacer = $parent['listtype'];
19131  	 	 	 	 	 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19132  	 	 	 	 	 	 $this->lispacer = $this->lisymbol;
19133  	 	 	 	 	 } else {
19134  	 	 	 	 	 	 $this->lispacer = '#';
19135  	 	 	 	 	 }
19136  	 	 	 	 	 ++$this->listcount[$this->listnum];
19137  	 	 	 	 	 if (isset($tag['attribute']['value'])) {
19138  	 	 	 	 	 	 $this->listcount[$this->listnum] = intval($tag['attribute']['value']);
19139  	 	 	 	 	 }
19140  	 	 	 	 } else {
19141  	 	 	 	 	 // unordered item
19142  	 	 	 	 	 if (isset($parent['attribute']['type']) AND !TCPDF_STATIC::empty_string($parent['attribute']['type'])) {
19143  	 	 	 	 	 	 $this->lispacer = $parent['attribute']['type'];
19144  	 	 	 	 	 } elseif (isset($parent['listtype']) AND !TCPDF_STATIC::empty_string($parent['listtype'])) {
19145  	 	 	 	 	 	 $this->lispacer = $parent['listtype'];
19146  	 	 	 	 	 } elseif (isset($this->lisymbol) AND !TCPDF_STATIC::empty_string($this->lisymbol)) {
19147  	 	 	 	 	 	 $this->lispacer = $this->lisymbol;
19148  	 	 	 	 	 } else {
19149  	 	 	 	 	 	 $this->lispacer = '!';
19150  	 	 	 	 	 }
19151  	 	 	 	 }
19152  	 	 	 	 break;
19153  	 	 	 }
19154  	 	 	 case 'blockquote': {
19155  	 	 	 	 if ($this->rtl) {
19156  	 	 	 	 	 $this->rMargin += $this->listindent;
19157  	 	 	 	 } else {
19158  	 	 	 	 	 $this->lMargin += $this->listindent;
19159  	 	 	 	 }
19160  	 	 	 	 ++$this->listindentlevel;
19161  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19162  	 	 	 	 break;
19163  	 	 	 }
19164  	 	 	 case 'br': {
19165  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19166  	 	 	 	 break;
19167  	 	 	 }
19168  	 	 	 case 'div': {
19169  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19170  	 	 	 	 break;
19171  	 	 	 }
19172  	 	 	 case 'p': {
19173  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19174  	 	 	 	 break;
19175  	 	 	 }
19176  	 	 	 case 'pre': {
19177  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19178  	 	 	 	 $this->premode = true;
19179  	 	 	 	 break;
19180  	 	 	 }
19181  	 	 	 case 'sup': {
19182  	 	 	 	 $this->SetXY($this->GetX(), $this->GetY() - ((0.7 * $this->FontSizePt) / $this->k));
19183  	 	 	 	 break;
19184  	 	 	 }
19185  	 	 	 case 'sub': {
19186  	 	 	 	 $this->SetXY($this->GetX(), $this->GetY() + ((0.3 * $this->FontSizePt) / $this->k));
19187  	 	 	 	 break;
19188  	 	 	 }
19189  	 	 	 case 'h1':
19190  	 	 	 case 'h2':
19191  	 	 	 case 'h3':
19192  	 	 	 case 'h4':
19193  	 	 	 case 'h5':
19194  	 	 	 case 'h6': {
19195  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, $firsttag);
19196  	 	 	 	 break;
19197  	 	 	 }
19198  	 	 	 // Form fields (since 4.8.000 - 2009-09-07)
19199  	 	 	 case 'form': {
19200  	 	 	 	 if (isset($tag['attribute']['action'])) {
19201  	 	 	 	 	 $this->form_action = $tag['attribute']['action'];
19202  	 	 	 	 } else {
19203  	 	 	 	 	 $this->Error('Please explicitly set action attribute path!');
19204  	 	 	 	 }
19205  	 	 	 	 if (isset($tag['attribute']['enctype'])) {
19206  	 	 	 	 	 $this->form_enctype = $tag['attribute']['enctype'];
19207  	 	 	 	 } else {
19208  	 	 	 	 	 $this->form_enctype = 'application/x-www-form-urlencoded';
19209  	 	 	 	 }
19210  	 	 	 	 if (isset($tag['attribute']['method'])) {
19211  	 	 	 	 	 $this->form_mode = $tag['attribute']['method'];
19212  	 	 	 	 } else {
19213  	 	 	 	 	 $this->form_mode = 'post';
19214  	 	 	 	 }
19215  	 	 	 	 break;
19216  	 	 	 }
19217  	 	 	 case 'input': {
19218  	 	 	 	 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19219  	 	 	 	 	 $name = $tag['attribute']['name'];
19220  	 	 	 	 } else {
19221  	 	 	 	 	 break;
19222  	 	 	 	 }
19223  	 	 	 	 $prop = array();
19224  	 	 	 	 $opt = array();
19225  	 	 	 	 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19226  	 	 	 	 	 $prop['readonly'] = true;
19227  	 	 	 	 }
19228  	 	 	 	 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19229  	 	 	 	 	 $value = $tag['attribute']['value'];
19230  	 	 	 	 }
19231  	 	 	 	 if (isset($tag['attribute']['maxlength']) AND !TCPDF_STATIC::empty_string($tag['attribute']['maxlength'])) {
19232  	 	 	 	 	 $opt['maxlen'] = intval($tag['attribute']['maxlength']);
19233  	 	 	 	 }
19234  	 	 	 	 $h = $this->getCellHeight($this->FontSize);
19235  	 	 	 	 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19236  	 	 	 	 	 $w = intval($tag['attribute']['size']) * $this->GetStringWidth(chr(32)) * 2;
19237  	 	 	 	 } else {
19238  	 	 	 	 	 $w = $h;
19239  	 	 	 	 }
19240  	 	 	 	 if (isset($tag['attribute']['checked']) AND (($tag['attribute']['checked'] == 'checked') OR ($tag['attribute']['checked'] == 'true'))) {
19241  	 	 	 	 	 $checked = true;
19242  	 	 	 	 } else {
19243  	 	 	 	 	 $checked = false;
19244  	 	 	 	 }
19245  	 	 	 	 if (isset($tag['align'])) {
19246  	 	 	 	 	 switch ($tag['align']) {
19247  	 	 	 	 	 	 case 'C': {
19248  	 	 	 	 	 	 	 $opt['q'] = 1;
19249  	 	 	 	 	 	 	 break;
19250  	 	 	 	 	 	 }
19251  	 	 	 	 	 	 case 'R': {
19252  	 	 	 	 	 	 	 $opt['q'] = 2;
19253  	 	 	 	 	 	 	 break;
19254  	 	 	 	 	 	 }
19255  	 	 	 	 	 	 case 'L':
19256  	 	 	 	 	 	 default: {
19257  	 	 	 	 	 	 	 break;
19258  	 	 	 	 	 	 }
19259  	 	 	 	 	 }
19260  	 	 	 	 }
19261  	 	 	 	 switch ($tag['attribute']['type']) {
19262  	 	 	 	 	 case 'text': {
19263  	 	 	 	 	 	 if (isset($value)) {
19264  	 	 	 	 	 	 	 $opt['v'] = $value;
19265  	 	 	 	 	 	 }
19266  	 	 	 	 	 	 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19267  	 	 	 	 	 	 break;
19268  	 	 	 	 	 }
19269  	 	 	 	 	 case 'password': {
19270  	 	 	 	 	 	 if (isset($value)) {
19271  	 	 	 	 	 	 	 $opt['v'] = $value;
19272  	 	 	 	 	 	 }
19273  	 	 	 	 	 	 $prop['password'] = 'true';
19274  	 	 	 	 	 	 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19275  	 	 	 	 	 	 break;
19276  	 	 	 	 	 }
19277  	 	 	 	 	 case 'checkbox': {
19278  	 	 	 	 	 	 if (!isset($value)) {
19279  	 	 	 	 	 	 	 break;
19280  	 	 	 	 	 	 }
19281  	 	 	 	 	 	 $this->CheckBox($name, $w, $checked, $prop, $opt, $value, '', '', false);
19282  	 	 	 	 	 	 break;
19283  	 	 	 	 	 }
19284  	 	 	 	 	 case 'radio': {
19285  	 	 	 	 	 	 if (!isset($value)) {
19286  	 	 	 	 	 	 	 break;
19287  	 	 	 	 	 	 }
19288  	 	 	 	 	 	 $this->RadioButton($name, $w, $prop, $opt, $value, $checked, '', '', false);
19289  	 	 	 	 	 	 break;
19290  	 	 	 	 	 }
19291  	 	 	 	 	 case 'submit': {
19292  	 	 	 	 	 	 if (!isset($value)) {
19293  	 	 	 	 	 	 	 $value = 'submit';
19294  	 	 	 	 	 	 }
19295  	 	 	 	 	 	 $w = $this->GetStringWidth($value) * 1.5;
19296  	 	 	 	 	 	 $h *= 1.6;
19297  	 	 	 	 	 	 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19298  	 	 	 	 	 	 $action = array();
19299  	 	 	 	 	 	 $action['S'] = 'SubmitForm';
19300  	 	 	 	 	 	 $action['F'] = $this->form_action;
19301  	 	 	 	 	 	 if ($this->form_enctype != 'FDF') {
19302  	 	 	 	 	 	 	 $action['Flags'] = array('ExportFormat');
19303  	 	 	 	 	 	 }
19304  	 	 	 	 	 	 if ($this->form_mode == 'get') {
19305  	 	 	 	 	 	 	 $action['Flags'] = array('GetMethod');
19306  	 	 	 	 	 	 }
19307  	 	 	 	 	 	 $this->Button($name, $w, $h, $value, $action, $prop, $opt, '', '', false);
19308  	 	 	 	 	 	 break;
19309  	 	 	 	 	 }
19310  	 	 	 	 	 case 'reset': {
19311  	 	 	 	 	 	 if (!isset($value)) {
19312  	 	 	 	 	 	 	 $value = 'reset';
19313  	 	 	 	 	 	 }
19314  	 	 	 	 	 	 $w = $this->GetStringWidth($value) * 1.5;
19315  	 	 	 	 	 	 $h *= 1.6;
19316  	 	 	 	 	 	 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19317  	 	 	 	 	 	 $this->Button($name, $w, $h, $value, array('S'=>'ResetForm'), $prop, $opt, '', '', false);
19318  	 	 	 	 	 	 break;
19319  	 	 	 	 	 }
19320  	 	 	 	 	 case 'file': {
19321  	 	 	 	 	 	 $prop['fileSelect'] = 'true';
19322  	 	 	 	 	 	 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19323  	 	 	 	 	 	 if (!isset($value)) {
19324  	 	 	 	 	 	 	 $value = '*';
19325  	 	 	 	 	 	 }
19326  	 	 	 	 	 	 $w = $this->GetStringWidth($value) * 2;
19327  	 	 	 	 	 	 $h *= 1.2;
19328  	 	 	 	 	 	 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19329  	 	 	 	 	 	 $jsaction = 'var f=this.getField(\''.$name.'\'); f.browseForFileToSubmit();';
19330  	 	 	 	 	 	 $this->Button('FB_'.$name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19331  	 	 	 	 	 	 break;
19332  	 	 	 	 	 }
19333  	 	 	 	 	 case 'hidden': {
19334  	 	 	 	 	 	 if (isset($value)) {
19335  	 	 	 	 	 	 	 $opt['v'] = $value;
19336  	 	 	 	 	 	 }
19337  	 	 	 	 	 	 $opt['f'] = array('invisible', 'hidden');
19338  	 	 	 	 	 	 $this->TextField($name, 0, 0, $prop, $opt, '', '', false);
19339  	 	 	 	 	 	 break;
19340  	 	 	 	 	 }
19341  	 	 	 	 	 case 'image': {
19342  	 	 	 	 	 	 // THIS TYPE MUST BE FIXED
19343  	 	 	 	 	 	 if (isset($tag['attribute']['src']) AND !TCPDF_STATIC::empty_string($tag['attribute']['src'])) {
19344  	 	 	 	 	 	 	 $img = $tag['attribute']['src'];
19345  	 	 	 	 	 	 } else {
19346  	 	 	 	 	 	 	 break;
19347  	 	 	 	 	 	 }
19348  	 	 	 	 	 	 $value = 'img';
19349  	 	 	 	 	 	 //$opt['mk'] = array('i'=>$img, 'tp'=>1, 'if'=>array('sw'=>'A', 's'=>'A', 'fb'=>false));
19350  	 	 	 	 	 	 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19351  	 	 	 	 	 	 	 $jsaction = $tag['attribute']['onclick'];
19352  	 	 	 	 	 	 } else {
19353  	 	 	 	 	 	 	 $jsaction = '';
19354  	 	 	 	 	 	 }
19355  	 	 	 	 	 	 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19356  	 	 	 	 	 	 break;
19357  	 	 	 	 	 }
19358  	 	 	 	 	 case 'button': {
19359  	 	 	 	 	 	 if (!isset($value)) {
19360  	 	 	 	 	 	 	 $value = ' ';
19361  	 	 	 	 	 	 }
19362  	 	 	 	 	 	 $w = $this->GetStringWidth($value) * 1.5;
19363  	 	 	 	 	 	 $h *= 1.6;
19364  	 	 	 	 	 	 $prop = array('lineWidth'=>1, 'borderStyle'=>'beveled', 'fillColor'=>array(196, 196, 196), 'strokeColor'=>array(255, 255, 255));
19365  	 	 	 	 	 	 if (isset($tag['attribute']['onclick']) AND !empty($tag['attribute']['onclick'])) {
19366  	 	 	 	 	 	 	 $jsaction = $tag['attribute']['onclick'];
19367  	 	 	 	 	 	 } else {
19368  	 	 	 	 	 	 	 $jsaction = '';
19369  	 	 	 	 	 	 }
19370  	 	 	 	 	 	 $this->Button($name, $w, $h, $value, $jsaction, $prop, $opt, '', '', false);
19371  	 	 	 	 	 	 break;
19372  	 	 	 	 	 }
19373  	 	 	 	 }
19374  	 	 	 	 break;
19375  	 	 	 }
19376  	 	 	 case 'textarea': {
19377  	 	 	 	 $prop = array();
19378  	 	 	 	 $opt = array();
19379  	 	 	 	 if (isset($tag['attribute']['readonly']) AND !TCPDF_STATIC::empty_string($tag['attribute']['readonly'])) {
19380  	 	 	 	 	 $prop['readonly'] = true;
19381  	 	 	 	 }
19382  	 	 	 	 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19383  	 	 	 	 	 $name = $tag['attribute']['name'];
19384  	 	 	 	 } else {
19385  	 	 	 	 	 break;
19386  	 	 	 	 }
19387  	 	 	 	 if (isset($tag['attribute']['value']) AND !TCPDF_STATIC::empty_string($tag['attribute']['value'])) {
19388  	 	 	 	 	 $opt['v'] = $tag['attribute']['value'];
19389  	 	 	 	 }
19390  	 	 	 	 if (isset($tag['attribute']['cols']) AND !TCPDF_STATIC::empty_string($tag['attribute']['cols'])) {
19391  	 	 	 	 	 $w = intval($tag['attribute']['cols']) * $this->GetStringWidth(chr(32)) * 2;
19392  	 	 	 	 } else {
19393  	 	 	 	 	 $w = 40;
19394  	 	 	 	 }
19395  	 	 	 	 if (isset($tag['attribute']['rows']) AND !TCPDF_STATIC::empty_string($tag['attribute']['rows'])) {
19396  	 	 	 	 	 $h = intval($tag['attribute']['rows']) * $this->getCellHeight($this->FontSize);
19397  	 	 	 	 } else {
19398  	 	 	 	 	 $h = 10;
19399  	 	 	 	 }
19400  	 	 	 	 $prop['multiline'] = 'true';
19401  	 	 	 	 $this->TextField($name, $w, $h, $prop, $opt, '', '', false);
19402  	 	 	 	 break;
19403  	 	 	 }
19404  	 	 	 case 'select': {
19405  	 	 	 	 $h = $this->getCellHeight($this->FontSize);
19406  	 	 	 	 if (isset($tag['attribute']['size']) AND !TCPDF_STATIC::empty_string($tag['attribute']['size'])) {
19407  	 	 	 	 	 $h *= ($tag['attribute']['size'] + 1);
19408  	 	 	 	 }
19409  	 	 	 	 $prop = array();
19410  	 	 	 	 $opt = array();
19411  	 	 	 	 if (isset($tag['attribute']['name']) AND !TCPDF_STATIC::empty_string($tag['attribute']['name'])) {
19412  	 	 	 	 	 $name = $tag['attribute']['name'];
19413  	 	 	 	 } else {
19414  	 	 	 	 	 break;
19415  	 	 	 	 }
19416  	 	 	 	 $w = 0;
19417  	 	 	 	 if (isset($tag['attribute']['opt']) AND !TCPDF_STATIC::empty_string($tag['attribute']['opt'])) {
19418  	 	 	 	 	 $options = explode('#!NwL!#', $tag['attribute']['opt']);
19419  	 	 	 	 	 $values = array();
19420  	 	 	 	 	 foreach ($options as $val) {
19421  	 	 	 	 	 	 if (strpos($val, '#!TaB!#') !== false) {
19422  	 	 	 	 	 	 	 $opts = explode('#!TaB!#', $val);
19423  	 	 	 	 	 	 	 $values[] = $opts;
19424  	 	 	 	 	 	 	 $w = max($w, $this->GetStringWidth($opts[1]));
19425  	 	 	 	 	 	 } else {
19426  	 	 	 	 	 	 	 $values[] = $val;
19427  	 	 	 	 	 	 	 $w = max($w, $this->GetStringWidth($val));
19428  	 	 	 	 	 	 }
19429  	 	 	 	 	 }
19430  	 	 	 	 } else {
19431  	 	 	 	 	 break;
19432  	 	 	 	 }
19433  	 	 	 	 $w *= 2;
19434  	 	 	 	 if (isset($tag['attribute']['multiple']) AND ($tag['attribute']['multiple']='multiple')) {
19435  	 	 	 	 	 $prop['multipleSelection'] = 'true';
19436  	 	 	 	 	 $this->ListBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19437  	 	 	 	 } else {
19438  	 	 	 	 	 $this->ComboBox($name, $w, $h, $values, $prop, $opt, '', '', false);
19439  	 	 	 	 }
19440  	 	 	 	 break;
19441  	 	 	 }
19442  	 	 	 case 'tcpdf': {
19443  	 	 	 	 if (defined('K_TCPDF_CALLS_IN_HTML') AND (K_TCPDF_CALLS_IN_HTML === true)) {
19444  	 	 	 	 	 // Special tag used to call TCPDF methods
19445  	 	 	 	 	 if (isset($tag['attribute']['method'])) {
19446  	 	 	 	 	 	 $tcpdf_method = $tag['attribute']['method'];
19447  	 	 	 	 	 	 if (method_exists($this, $tcpdf_method)) {
19448  	 	 	 	 	 	 	 if (isset($tag['attribute']['params']) AND (!empty($tag['attribute']['params']))) {
19449  	 	 	 	 	 	 	 	 $params = $this->unserializeTCPDFtagParameters($tag['attribute']['params']);
19450  	 	 	 	 	 	 	 	 call_user_func_array(array($this, $tcpdf_method), $params);
19451  	 	 	 	 	 	 	 } else {
19452  	 	 	 	 	 	 	 	 $this->$tcpdf_method();
19453  	 	 	 	 	 	 	 }
19454  	 	 	 	 	 	 	 $this->newline = true;
19455  	 	 	 	 	 	 }
19456  	 	 	 	 	 }
19457  	 	 	 	 }
19458  	 	 	 	 break;
19459  	 	 	 }
19460  	 	 	 default: {
19461  	 	 	 	 break;
19462  	 	 	 }
19463  	 	 }
19464  	 	 // define tags that support borders and background colors
19465  	 	 $bordertags = array('blockquote','br','dd','dl','div','dt','h1','h2','h3','h4','h5','h6','hr','li','ol','p','pre','ul','tcpdf','table');
19466  	 	 if (in_array($tag['value'], $bordertags)) {
19467  	 	 	 // set border
19468  	 	 	 $dom[$key]['borderposition'] = $this->getBorderStartPosition();
19469  	 	 }
19470  	 	 if ($dom[$key]['self'] AND isset($dom[$key]['attribute']['pagebreakafter'])) {
19471  	 	 	 $pba = $dom[$key]['attribute']['pagebreakafter'];
19472  	 	 	 // check for pagebreak
19473  	 	 	 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
19474  	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19475  	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
19476  	 	 	 }
19477  	 	 	 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
19478  	 	 	 	 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
19479  	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
19480  	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
19481  	 	 	 }
19482  	 	 }
19483  	 	 return $dom;
19484  	 }
19485  
19486  	 /**
19487  	  * Process closing tags.
19488  	  * @param $dom (array) html dom array
19489  	  * @param $key (int) current element id
19490  	  * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
19491  	  * @param $maxbottomliney (int) maximum y value of current line
19492  	  * @return $dom array
19493  	  * @protected
19494  	  */
19495  	protected function closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney=0) {
19496  	 	 $tag = $dom[$key];
19497  	 	 $parent = $dom[($dom[$key]['parent'])];
19498  	 	 $lasttag = ((!isset($dom[($key + 1)])) OR ((!isset($dom[($key + 2)])) AND ($dom[($key + 1)]['value'] == 'marker')));
19499  	 	 $in_table_head = false;
19500  	 	 // maximum x position (used to draw borders)
19501  	 	 if ($this->rtl) {
19502  	 	 	 $xmax = $this->w;
19503  	 	 } else {
19504  	 	 	 $xmax = 0;
19505  	 	 }
19506  	 	 if ($tag['block']) {
19507  	 	 	 $hbz = 0; // distance from y to line bottom
19508  	 	 	 $hb = 0; // vertical space between block tags
19509  	 	 	 // calculate vertical space for block tags
19510  	 	 	 if (isset($this->tagvspaces[$tag['value']][1]['h']) && !empty($this->tagvspaces[$tag['value']][1]['h']) && ($this->tagvspaces[$tag['value']][1]['h'] >= 0)) {
19511  	 	 	 	 $pre_h = $this->tagvspaces[$tag['value']][1]['h'];
19512  	 	 	 } elseif (isset($parent['fontsize'])) {
19513  	 	 	 	 $pre_h = $this->getCellHeight($parent['fontsize'] / $this->k);
19514  	 	 	 } else {
19515  	 	 	 	 $pre_h = $this->getCellHeight($this->FontSize);
19516  	 	 	 }
19517  	 	 	 if (isset($this->tagvspaces[$tag['value']][1]['n'])) {
19518  	 	 	 	 $cn = $this->tagvspaces[$tag['value']][1]['n'];
19519  	 	 	 } elseif (preg_match('/[h][0-9]/', $tag['value']) > 0) {
19520  	 	 	 	 $cn = 0.6;
19521  	 	 	 } else {
19522  	 	 	 	 $cn = 1;
19523  	 	 	 }
19524  	 	 	 if ((!isset($this->tagvspaces[$tag['value']])) AND ($tag['value'] == 'div')) {
19525  	 	 	 	 $hb = 0;
19526  	 	 	 } else {
19527  	 	 	 	 $hb = ($cn * $pre_h);
19528  	 	 	 }
19529  	 	 	 if ($maxbottomliney > $this->PageBreakTrigger) {
19530  	 	 	 	 $hbz = $this->getCellHeight($this->FontSize);
19531  	 	 	 } elseif ($this->y < $maxbottomliney) {
19532  	 	 	 	 $hbz = ($maxbottomliney - $this->y);
19533  	 	 	 }
19534  	 	 }
19535  	 	 // Closing tag
19536  	 	 switch($tag['value']) {
19537  	 	 	 case 'tr': {
19538  	 	 	 	 $table_el = $dom[($dom[$key]['parent'])]['parent'];
19539  	 	 	 	 if (!isset($parent['endy'])) {
19540  	 	 	 	 	 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19541  	 	 	 	 	 $parent['endy'] = $this->y;
19542  	 	 	 	 }
19543  	 	 	 	 if (!isset($parent['endpage'])) {
19544  	 	 	 	 	 $dom[($dom[$key]['parent'])]['endpage'] = $this->page;
19545  	 	 	 	 	 $parent['endpage'] = $this->page;
19546  	 	 	 	 }
19547  	 	 	 	 if (!isset($parent['endcolumn'])) {
19548  	 	 	 	 	 $dom[($dom[$key]['parent'])]['endcolumn'] = $this->current_column;
19549  	 	 	 	 	 $parent['endcolumn'] = $this->current_column;
19550  	 	 	 	 }
19551  	 	 	 	 // update row-spanned cells
19552  	 	 	 	 if (isset($dom[$table_el]['rowspans'])) {
19553  	 	 	 	 	 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19554  	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['rowspan'] -= 1;
19555  	 	 	 	 	 	 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19556  	 	 	 	 	 	 	 if (($dom[$table_el]['rowspans'][$k]['endpage'] == $parent['endpage']) AND ($dom[$table_el]['rowspans'][$k]['endcolumn'] == $parent['endcolumn'])) {
19557  	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $parent['endy']);
19558  	 	 	 	 	 	 	 } elseif (($dom[$table_el]['rowspans'][$k]['endpage'] > $parent['endpage']) OR ($dom[$table_el]['rowspans'][$k]['endcolumn'] > $parent['endcolumn'])) {
19559  	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19560  	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19561  	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19562  	 	 	 	 	 	 	 }
19563  	 	 	 	 	 	 }
19564  	 	 	 	 	 }
19565  	 	 	 	 	 // report new endy and endpage to the rowspanned cells
19566  	 	 	 	 	 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19567  	 	 	 	 	 	 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19568  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endpage'] = max($dom[$table_el]['rowspans'][$k]['endpage'], $dom[($dom[$key]['parent'])]['endpage']);
19569  	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endpage'] = $dom[$table_el]['rowspans'][$k]['endpage'];
19570  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endcolumn'] = max($dom[$table_el]['rowspans'][$k]['endcolumn'], $dom[($dom[$key]['parent'])]['endcolumn']);
19571  	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endcolumn'] = $dom[$table_el]['rowspans'][$k]['endcolumn'];
19572  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$table_el]['rowspans'][$k]['endy'], $dom[($dom[$key]['parent'])]['endy']);
19573  	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endy'] = $dom[$table_el]['rowspans'][$k]['endy'];
19574  	 	 	 	 	 	 }
19575  	 	 	 	 	 }
19576  	 	 	 	 	 // update remaining rowspanned cells
19577  	 	 	 	 	 foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
19578  	 	 	 	 	 	 if ($dom[$table_el]['rowspans'][$k]['rowspan'] == 0) {
19579  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[($dom[$key]['parent'])]['endpage'];
19580  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[($dom[$key]['parent'])]['endcolumn'];
19581  	 	 	 	 	 	 	 $dom[$table_el]['rowspans'][$k]['endy'] = $dom[($dom[$key]['parent'])]['endy'];
19582  	 	 	 	 	 	 }
19583  	 	 	 	 	 }
19584  	 	 	 	 }
19585  	 	 	 	 $prev_page = $this->page;
19586  	 	 	 	 $this->setPage($dom[($dom[$key]['parent'])]['endpage']);
19587  	 	 	 	 if ($this->num_columns > 1) {
19588  	 	 	 	 	 if (($prev_page < $this->page)
19589  	 	 	 	 	 	 AND ((($this->current_column == 0) AND ($dom[($dom[$key]['parent'])]['endcolumn'] == ($this->num_columns - 1)))
19590  	 	 	 	 	 	 	 OR ($this->current_column == $dom[($dom[$key]['parent'])]['endcolumn']))) {
19591  	 	 	 	 	 	 // page jump
19592  	 	 	 	 	 	 $this->selectColumn(0);
19593  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endcolumn'] = 0;
19594  	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['endy'] = $this->y;
19595  	 	 	 	 	 } else {
19596  	 	 	 	 	 	 $this->selectColumn($dom[($dom[$key]['parent'])]['endcolumn']);
19597  	 	 	 	 	 	 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19598  	 	 	 	 	 }
19599  	 	 	 	 } else {
19600  	 	 	 	 	 $this->y = $dom[($dom[$key]['parent'])]['endy'];
19601  	 	 	 	 }
19602  	 	 	 	 if (isset($dom[$table_el]['attribute']['cellspacing'])) {
19603  	 	 	 	 	 $this->y += $this->getHTMLUnitToUnits($dom[$table_el]['attribute']['cellspacing'], 1, 'px');
19604  	 	 	 	 } elseif (isset($dom[$table_el]['border-spacing'])) {
19605  	 	 	 	 	 $this->y += $dom[$table_el]['border-spacing']['V'];
19606  	 	 	 	 }
19607  	 	 	 	 $this->Ln(0, $cell);
19608  	 	 	 	 if ($this->current_column == $parent['startcolumn']) {
19609  	 	 	 	 	 $this->x = $parent['startx'];
19610  	 	 	 	 }
19611  	 	 	 	 // account for booklet mode
19612  	 	 	 	 if ($this->page > $parent['startpage']) {
19613  	 	 	 	 	 if (($this->rtl) AND ($this->pagedim[$this->page]['orm'] != $this->pagedim[$parent['startpage']]['orm'])) {
19614  	 	 	 	 	 	 $this->x -= ($this->pagedim[$this->page]['orm'] - $this->pagedim[$parent['startpage']]['orm']);
19615  	 	 	 	 	 } elseif ((!$this->rtl) AND ($this->pagedim[$this->page]['olm'] != $this->pagedim[$parent['startpage']]['olm'])) {
19616  	 	 	 	 	 	 $this->x += ($this->pagedim[$this->page]['olm'] - $this->pagedim[$parent['startpage']]['olm']);
19617  	 	 	 	 	 }
19618  	 	 	 	 }
19619  	 	 	 	 break;
19620  	 	 	 }
19621  	 	 	 case 'tablehead':
19622  	 	 	 	 // closing tag used for the thead part
19623  	 	 	 	 $in_table_head = true;
19624  	 	 	 	 $this->inthead = false;
19625  	 	 	 case 'table': {
19626  	 	 	 	 $table_el = $parent;
19627  	 	 	 	 // set default border
19628  	 	 	 	 if (isset($table_el['attribute']['border']) AND ($table_el['attribute']['border'] > 0)) {
19629  	 	 	 	 	 // set default border
19630  	 	 	 	 	 $border = array('LTRB' => array('width' => $this->getCSSBorderWidth($table_el['attribute']['border']), 'cap'=>'square', 'join'=>'miter', 'dash'=> 0, 'color'=>array(0,0,0)));
19631  	 	 	 	 } else {
19632  	 	 	 	 	 $border = 0;
19633  	 	 	 	 }
19634  	 	 	 	 $default_border = $border;
19635  	 	 	 	 // fix bottom line alignment of last line before page break
19636  	 	 	 	 foreach ($dom[($dom[$key]['parent'])]['trids'] as $j => $trkey) {
19637  	 	 	 	 	 // update row-spanned cells
19638  	 	 	 	 	 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19639  	 	 	 	 	 	 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19640  	 	 	 	 	 	 	 if (isset($prevtrkey) AND ($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] > 0)) {
19641  	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] = $trkey;
19642  	 	 	 	 	 	 	 }
19643  	 	 	 	 	 	 	 if ($dom[($dom[$key]['parent'])]['rowspans'][$k]['trid'] == $trkey) {
19644  	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] -= 1;
19645  	 	 	 	 	 	 	 }
19646  	 	 	 	 	 	 }
19647  	 	 	 	 	 }
19648  	 	 	 	 	 if (isset($prevtrkey) AND ($dom[$trkey]['startpage'] > $dom[$prevtrkey]['endpage'])) {
19649  	 	 	 	 	 	 $pgendy = $this->pagedim[$dom[$prevtrkey]['endpage']]['hk'] - $this->pagedim[$dom[$prevtrkey]['endpage']]['bm'];
19650  	 	 	 	 	 	 $dom[$prevtrkey]['endy'] = $pgendy;
19651  	 	 	 	 	 	 // update row-spanned cells
19652  	 	 	 	 	 	 if (isset($dom[($dom[$key]['parent'])]['rowspans'])) {
19653  	 	 	 	 	 	 	 foreach ($dom[($dom[$key]['parent'])]['rowspans'] as $k => $trwsp) {
19654  	 	 	 	 	 	 	 	 if (($trwsp['trid'] == $prevtrkey) AND ($trwsp['mrowspan'] >= 0) AND ($trwsp['endpage'] == $dom[$prevtrkey]['endpage'])) {
19655  	 	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['rowspans'][$k]['endy'] = $pgendy;
19656  	 	 	 	 	 	 	 	 	 $dom[($dom[$key]['parent'])]['rowspans'][$k]['mrowspan'] = -1;
19657  	 	 	 	 	 	 	 	 }
19658  	 	 	 	 	 	 	 }
19659  	 	 	 	 	 	 }
19660  	 	 	 	 	 }
19661  	 	 	 	 	 $prevtrkey = $trkey;
19662  	 	 	 	 	 $table_el = $dom[($dom[$key]['parent'])];
19663  	 	 	 	 }
19664  	 	 	 	 // for each row
19665  	 	 	 	 if (count($table_el['trids']) > 0) {
19666  	 	 	 	 	 unset($xmax);
19667  	 	 	 	 }
19668  	 	 	 	 foreach ($table_el['trids'] as $j => $trkey) {
19669  	 	 	 	 	 $parent = $dom[$trkey];
19670  	 	 	 	 	 if (!isset($xmax)) {
19671  	 	 	 	 	 	 $xmax = $parent['cellpos'][(count($parent['cellpos']) - 1)]['endx'];
19672  	 	 	 	 	 }
19673  	 	 	 	 	 // for each cell on the row
19674  	 	 	 	 	 foreach ($parent['cellpos'] as $k => $cellpos) {
19675  	 	 	 	 	 	 if (isset($cellpos['rowspanid']) AND ($cellpos['rowspanid'] >= 0)) {
19676  	 	 	 	 	 	 	 $cellpos['startx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['startx'];
19677  	 	 	 	 	 	 	 $cellpos['endx'] = $table_el['rowspans'][($cellpos['rowspanid'])]['endx'];
19678  	 	 	 	 	 	 	 $endy = $table_el['rowspans'][($cellpos['rowspanid'])]['endy'];
19679  	 	 	 	 	 	 	 $startpage = $table_el['rowspans'][($cellpos['rowspanid'])]['startpage'];
19680  	 	 	 	 	 	 	 $endpage = $table_el['rowspans'][($cellpos['rowspanid'])]['endpage'];
19681  	 	 	 	 	 	 	 $startcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['startcolumn'];
19682  	 	 	 	 	 	 	 $endcolumn = $table_el['rowspans'][($cellpos['rowspanid'])]['endcolumn'];
19683  	 	 	 	 	 	 } else {
19684  	 	 	 	 	 	 	 $endy = $parent['endy'];
19685  	 	 	 	 	 	 	 $startpage = $parent['startpage'];
19686  	 	 	 	 	 	 	 $endpage = $parent['endpage'];
19687  	 	 	 	 	 	 	 $startcolumn = $parent['startcolumn'];
19688  	 	 	 	 	 	 	 $endcolumn = $parent['endcolumn'];
19689  	 	 	 	 	 	 }
19690  	 	 	 	 	 	 if ($this->num_columns == 0) {
19691  	 	 	 	 	 	 	 $this->num_columns = 1;
19692  	 	 	 	 	 	 }
19693  	 	 	 	 	 	 if (isset($cellpos['border'])) {
19694  	 	 	 	 	 	 	 $border = $cellpos['border'];
19695  	 	 	 	 	 	 }
19696  	 	 	 	 	 	 if (isset($cellpos['bgcolor']) AND ($cellpos['bgcolor']) !== false) {
19697  	 	 	 	 	 	 	 $this->SetFillColorArray($cellpos['bgcolor']);
19698  	 	 	 	 	 	 	 $fill = true;
19699  	 	 	 	 	 	 } else {
19700  	 	 	 	 	 	 	 $fill = false;
19701  	 	 	 	 	 	 }
19702  	 	 	 	 	 	 $x = $cellpos['startx'];
19703  	 	 	 	 	 	 $y = $parent['starty'];
19704  	 	 	 	 	 	 $starty = $y;
19705  	 	 	 	 	 	 $w = abs($cellpos['endx'] - $cellpos['startx']);
19706  	 	 	 	 	 	 // get border modes
19707  	 	 	 	 	 	 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
19708  	 	 	 	 	 	 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
19709  	 	 	 	 	 	 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
19710  	 	 	 	 	 	 // design borders around HTML cells.
19711  	 	 	 	 	 	 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
19712  	 	 	 	 	 	 	 $ccode = '';
19713  	 	 	 	 	 	 	 $this->setPage($page);
19714  	 	 	 	 	 	 	 if ($this->num_columns < 2) {
19715  	 	 	 	 	 	 	 	 // single-column mode
19716  	 	 	 	 	 	 	 	 $this->x = $x;
19717  	 	 	 	 	 	 	 	 $this->y = $this->tMargin;
19718  	 	 	 	 	 	 	 }
19719  	 	 	 	 	 	 	 // account for margin changes
19720  	 	 	 	 	 	 	 if ($page > $startpage) {
19721  	 	 	 	 	 	 	 	 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
19722  	 	 	 	 	 	 	 	 	 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
19723  	 	 	 	 	 	 	 	 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
19724  	 	 	 	 	 	 	 	 	 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
19725  	 	 	 	 	 	 	 	 }
19726  	 	 	 	 	 	 	 }
19727  	 	 	 	 	 	 	 if ($startpage == $endpage) { // single page
19728  	 	 	 	 	 	 	 	 $deltacol = 0;
19729  	 	 	 	 	 	 	 	 $deltath = 0;
19730  	 	 	 	 	 	 	 	 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
19731  	 	 	 	 	 	 	 	 	 $this->selectColumn($column);
19732  	 	 	 	 	 	 	 	 	 if ($startcolumn == $endcolumn) { // single column
19733  	 	 	 	 	 	 	 	 	 	 $cborder = $border;
19734  	 	 	 	 	 	 	 	 	 	 $h = $endy - $parent['starty'];
19735  	 	 	 	 	 	 	 	 	 	 $this->y = $y;
19736  	 	 	 	 	 	 	 	 	 	 $this->x = $x;
19737  	 	 	 	 	 	 	 	 	 } elseif ($column == $startcolumn) { // first column
19738  	 	 	 	 	 	 	 	 	 	 $cborder = $border_start;
19739  	 	 	 	 	 	 	 	 	 	 $this->y = $starty;
19740  	 	 	 	 	 	 	 	 	 	 $this->x = $x;
19741  	 	 	 	 	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
19742  	 	 	 	 	 	 	 	 	 	 if ($this->rtl) {
19743  	 	 	 	 	 	 	 	 	 	 	 $deltacol = $this->x + $this->rMargin - $this->w;
19744  	 	 	 	 	 	 	 	 	 	 } else {
19745  	 	 	 	 	 	 	 	 	 	 	 $deltacol = $this->x - $this->lMargin;
19746  	 	 	 	 	 	 	 	 	 	 }
19747  	 	 	 	 	 	 	 	 	 } elseif ($column == $endcolumn) { // end column
19748  	 	 	 	 	 	 	 	 	 	 $cborder = $border_end;
19749  	 	 	 	 	 	 	 	 	 	 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19750  	 	 	 	 	 	 	 	 	 	 	 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19751  	 	 	 	 	 	 	 	 	 	 }
19752  	 	 	 	 	 	 	 	 	 	 $this->x += $deltacol;
19753  	 	 	 	 	 	 	 	 	 	 $h = $endy - $this->y;
19754  	 	 	 	 	 	 	 	 	 } else { // middle column
19755  	 	 	 	 	 	 	 	 	 	 $cborder = $border_middle;
19756  	 	 	 	 	 	 	 	 	 	 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19757  	 	 	 	 	 	 	 	 	 	 	 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19758  	 	 	 	 	 	 	 	 	 	 }
19759  	 	 	 	 	 	 	 	 	 	 $this->x += $deltacol;
19760  	 	 	 	 	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
19761  	 	 	 	 	 	 	 	 	 }
19762  	 	 	 	 	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19763  	 	 	 	 	 	 	 	 } // end for each column
19764  	 	 	 	 	 	 	 } elseif ($page == $startpage) { // first page
19765  	 	 	 	 	 	 	 	 $deltacol = 0;
19766  	 	 	 	 	 	 	 	 $deltath = 0;
19767  	 	 	 	 	 	 	 	 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
19768  	 	 	 	 	 	 	 	 	 $this->selectColumn($column);
19769  	 	 	 	 	 	 	 	 	 if ($column == $startcolumn) { // first column
19770  	 	 	 	 	 	 	 	 	 	 $cborder = $border_start;
19771  	 	 	 	 	 	 	 	 	 	 $this->y = $starty;
19772  	 	 	 	 	 	 	 	 	 	 $this->x = $x;
19773  	 	 	 	 	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
19774  	 	 	 	 	 	 	 	 	 	 if ($this->rtl) {
19775  	 	 	 	 	 	 	 	 	 	 	 $deltacol = $this->x + $this->rMargin - $this->w;
19776  	 	 	 	 	 	 	 	 	 	 } else {
19777  	 	 	 	 	 	 	 	 	 	 	 $deltacol = $this->x - $this->lMargin;
19778  	 	 	 	 	 	 	 	 	 	 }
19779  	 	 	 	 	 	 	 	 	 } else { // middle column
19780  	 	 	 	 	 	 	 	 	 	 $cborder = $border_middle;
19781  	 	 	 	 	 	 	 	 	 	 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19782  	 	 	 	 	 	 	 	 	 	 	 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19783  	 	 	 	 	 	 	 	 	 	 }
19784  	 	 	 	 	 	 	 	 	 	 $this->x += $deltacol;
19785  	 	 	 	 	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
19786  	 	 	 	 	 	 	 	 	 }
19787  	 	 	 	 	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19788  	 	 	 	 	 	 	 	 } // end for each column
19789  	 	 	 	 	 	 	 } elseif ($page == $endpage) { // last page
19790  	 	 	 	 	 	 	 	 $deltacol = 0;
19791  	 	 	 	 	 	 	 	 $deltath = 0;
19792  	 	 	 	 	 	 	 	 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
19793  	 	 	 	 	 	 	 	 	 $this->selectColumn($column);
19794  	 	 	 	 	 	 	 	 	 if ($column == $endcolumn) { // end column
19795  	 	 	 	 	 	 	 	 	 	 $cborder = $border_end;
19796  	 	 	 	 	 	 	 	 	 	 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19797  	 	 	 	 	 	 	 	 	 	 	 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19798  	 	 	 	 	 	 	 	 	 	 }
19799  	 	 	 	 	 	 	 	 	 	 $this->x += $deltacol;
19800  	 	 	 	 	 	 	 	 	 	 $h = $endy - $this->y;
19801  	 	 	 	 	 	 	 	 	 } else { // middle column
19802  	 	 	 	 	 	 	 	 	 	 $cborder = $border_middle;
19803  	 	 	 	 	 	 	 	 	 	 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19804  	 	 	 	 	 	 	 	 	 	 	 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19805  	 	 	 	 	 	 	 	 	 	 }
19806  	 	 	 	 	 	 	 	 	 	 $this->x += $deltacol;
19807  	 	 	 	 	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
19808  	 	 	 	 	 	 	 	 	 }
19809  	 	 	 	 	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19810  	 	 	 	 	 	 	 	 } // end for each column
19811  	 	 	 	 	 	 	 } else { // middle page
19812  	 	 	 	 	 	 	 	 $deltacol = 0;
19813  	 	 	 	 	 	 	 	 $deltath = 0;
19814  	 	 	 	 	 	 	 	 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
19815  	 	 	 	 	 	 	 	 	 $this->selectColumn($column);
19816  	 	 	 	 	 	 	 	 	 $cborder = $border_middle;
19817  	 	 	 	 	 	 	 	 	 if (isset($this->columns[$column]['th']['\''.$page.'\''])) {
19818  	 	 	 	 	 	 	 	 	 	 $this->y = $this->columns[$column]['th']['\''.$page.'\''];
19819  	 	 	 	 	 	 	 	 	 }
19820  	 	 	 	 	 	 	 	 	 $this->x += $deltacol;
19821  	 	 	 	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
19822  	 	 	 	 	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
19823  	 	 	 	 	 	 	 	 } // end for each column
19824  	 	 	 	 	 	 	 }
19825  	 	 	 	 	 	 	 if (!empty($cborder) OR !empty($fill)) {
19826  	 	 	 	 	 	 	 	 $offsetlen = strlen($ccode);
19827  	 	 	 	 	 	 	 	 // draw border and fill
19828  	 	 	 	 	 	 	 	 if ($this->inxobj) {
19829  	 	 	 	 	 	 	 	 	 // we are inside an XObject template
19830  	 	 	 	 	 	 	 	 	 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
19831  	 	 	 	 	 	 	 	 	 	 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
19832  	 	 	 	 	 	 	 	 	 	 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
19833  	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
19834  	 	 	 	 	 	 	 	 	 } else {
19835  	 	 	 	 	 	 	 	 	 	 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
19836  	 	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
19837  	 	 	 	 	 	 	 	 	 }
19838  	 	 	 	 	 	 	 	 	 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
19839  	 	 	 	 	 	 	 	 	 $pstart = substr($pagebuff, 0, $pagemark);
19840  	 	 	 	 	 	 	 	 	 $pend = substr($pagebuff, $pagemark);
19841  	 	 	 	 	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
19842  	 	 	 	 	 	 	 	 } else {
19843  	 	 	 	 	 	 	 	 	 // draw border and fill
19844  	 	 	 	 	 	 	 	 	 if (end($this->transfmrk[$this->page]) !== false) {
19845  	 	 	 	 	 	 	 	 	 	 $pagemarkkey = key($this->transfmrk[$this->page]);
19846  	 	 	 	 	 	 	 	 	 	 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
19847  	 	 	 	 	 	 	 	 	 } elseif ($this->InFooter) {
19848  	 	 	 	 	 	 	 	 	 	 $pagemark = $this->footerpos[$this->page];
19849  	 	 	 	 	 	 	 	 	 } else {
19850  	 	 	 	 	 	 	 	 	 	 $pagemark = $this->intmrk[$this->page];
19851  	 	 	 	 	 	 	 	 	 }
19852  	 	 	 	 	 	 	 	 	 $pagebuff = $this->getPageBuffer($this->page);
19853  	 	 	 	 	 	 	 	 	 $pstart = substr($pagebuff, 0, $pagemark);
19854  	 	 	 	 	 	 	 	 	 $pend = substr($pagebuff, $pagemark);
19855  	 	 	 	 	 	 	 	 	 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
19856  	 	 	 	 	 	 	 	 }
19857  	 	 	 	 	 	 	 }
19858  	 	 	 	 	 	 } // end for each page
19859  	 	 	 	 	 	 // restore default border
19860  	 	 	 	 	 	 $border = $default_border;
19861  	 	 	 	 	 } // end for each cell on the row
19862  	 	 	 	 	 if (isset($table_el['attribute']['cellspacing'])) {
19863  	 	 	 	 	 	 $this->y += $this->getHTMLUnitToUnits($table_el['attribute']['cellspacing'], 1, 'px');
19864  	 	 	 	 	 } elseif (isset($table_el['border-spacing'])) {
19865  	 	 	 	 	 	 $this->y += $table_el['border-spacing']['V'];
19866  	 	 	 	 	 }
19867  	 	 	 	 	 $this->Ln(0, $cell);
19868  	 	 	 	 	 $this->x = $parent['startx'];
19869  	 	 	 	 	 if ($endpage > $startpage) {
19870  	 	 	 	 	 	 if (($this->rtl) AND ($this->pagedim[$endpage]['orm'] != $this->pagedim[$startpage]['orm'])) {
19871  	 	 	 	 	 	 	 $this->x += ($this->pagedim[$endpage]['orm'] - $this->pagedim[$startpage]['orm']);
19872  	 	 	 	 	 	 } elseif ((!$this->rtl) AND ($this->pagedim[$endpage]['olm'] != $this->pagedim[$startpage]['olm'])) {
19873  	 	 	 	 	 	 	 $this->x += ($this->pagedim[$endpage]['olm'] - $this->pagedim[$startpage]['olm']);
19874  	 	 	 	 	 	 }
19875  	 	 	 	 	 }
19876  	 	 	 	 }
19877  	 	 	 	 if (!$in_table_head) { // we are not inside a thead section
19878  	 	 	 	 	 $this->cell_padding = isset($table_el['old_cell_padding']) ? $table_el['old_cell_padding'] : null;
19879  	 	 	 	 	 // reset row height
19880  	 	 	 	 	 $this->resetLastH();
19881  	 	 	 	 	 if (($this->page == ($this->numpages - 1)) AND ($this->pageopen[$this->numpages])) {
19882  	 	 	 	 	 	 $plendiff = ($this->pagelen[$this->numpages] - $this->emptypagemrk[$this->numpages]);
19883  	 	 	 	 	 	 if (($plendiff > 0) AND ($plendiff < 60)) {
19884  	 	 	 	 	 	 	 $pagediff = substr($this->getPageBuffer($this->numpages), $this->emptypagemrk[$this->numpages], $plendiff);
19885  	 	 	 	 	 	 	 if (substr($pagediff, 0, 5) == 'BT /F') {
19886  	 	 	 	 	 	 	 	 // the difference is only a font setting
19887  	 	 	 	 	 	 	 	 $plendiff = 0;
19888  	 	 	 	 	 	 	 }
19889  	 	 	 	 	 	 }
19890  	 	 	 	 	 	 if ($plendiff == 0) {
19891  	 	 	 	 	 	 	 // remove last blank page
19892  	 	 	 	 	 	 	 $this->deletePage($this->numpages);
19893  	 	 	 	 	 	 }
19894  	 	 	 	 	 }
19895  	 	 	 	 	 if (isset($this->theadMargins['top'])) {
19896  	 	 	 	 	 	 // restore top margin
19897  	 	 	 	 	 	 $this->tMargin = $this->theadMargins['top'];
19898  	 	 	 	 	 }
19899  	 	 	 	 	 if (!isset($table_el['attribute']['nested']) OR ($table_el['attribute']['nested'] != 'true')) {
19900  	 	 	 	 	 	 // reset main table header
19901  	 	 	 	 	 	 $this->thead = '';
19902  	 	 	 	 	 	 $this->theadMargins = array();
19903  	 	 	 	 	 	 $this->pagedim[$this->page]['tm'] = $this->tMargin;
19904  	 	 	 	 	 }
19905  	 	 	 	 }
19906  	 	 	 	 $parent = $table_el;
19907  	 	 	 	 break;
19908  	 	 	 }
19909  	 	 	 case 'a': {
19910  	 	 	 	 $this->HREF = array();
19911  	 	 	 	 break;
19912  	 	 	 }
19913  	 	 	 case 'sup': {
19914  	 	 	 	 $this->SetXY($this->GetX(), $this->GetY() + ((0.7 * $parent['fontsize']) / $this->k));
19915  	 	 	 	 break;
19916  	 	 	 }
19917  	 	 	 case 'sub': {
19918  	 	 	 	 $this->SetXY($this->GetX(), $this->GetY() - ((0.3 * $parent['fontsize']) / $this->k));
19919  	 	 	 	 break;
19920  	 	 	 }
19921  	 	 	 case 'div': {
19922  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19923  	 	 	 	 break;
19924  	 	 	 }
19925  	 	 	 case 'blockquote': {
19926  	 	 	 	 if ($this->rtl) {
19927  	 	 	 	 	 $this->rMargin -= $this->listindent;
19928  	 	 	 	 } else {
19929  	 	 	 	 	 $this->lMargin -= $this->listindent;
19930  	 	 	 	 }
19931  	 	 	 	 --$this->listindentlevel;
19932  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19933  	 	 	 	 break;
19934  	 	 	 }
19935  	 	 	 case 'p': {
19936  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19937  	 	 	 	 break;
19938  	 	 	 }
19939  	 	 	 case 'pre': {
19940  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19941  	 	 	 	 $this->premode = false;
19942  	 	 	 	 break;
19943  	 	 	 }
19944  	 	 	 case 'dl': {
19945  	 	 	 	 --$this->listnum;
19946  	 	 	 	 if ($this->listnum <= 0) {
19947  	 	 	 	 	 $this->listnum = 0;
19948  	 	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19949  	 	 	 	 } else {
19950  	 	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19951  	 	 	 	 }
19952  	 	 	 	 $this->resetLastH();
19953  	 	 	 	 break;
19954  	 	 	 }
19955  	 	 	 case 'dt': {
19956  	 	 	 	 $this->lispacer = '';
19957  	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19958  	 	 	 	 break;
19959  	 	 	 }
19960  	 	 	 case 'dd': {
19961  	 	 	 	 $this->lispacer = '';
19962  	 	 	 	 if ($this->rtl) {
19963  	 	 	 	 	 $this->rMargin -= $this->listindent;
19964  	 	 	 	 } else {
19965  	 	 	 	 	 $this->lMargin -= $this->listindent;
19966  	 	 	 	 }
19967  	 	 	 	 --$this->listindentlevel;
19968  	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19969  	 	 	 	 break;
19970  	 	 	 }
19971  	 	 	 case 'ul':
19972  	 	 	 case 'ol': {
19973  	 	 	 	 --$this->listnum;
19974  	 	 	 	 $this->lispacer = '';
19975  	 	 	 	 if ($this->rtl) {
19976  	 	 	 	 	 $this->rMargin -= $this->listindent;
19977  	 	 	 	 } else {
19978  	 	 	 	 	 $this->lMargin -= $this->listindent;
19979  	 	 	 	 }
19980  	 	 	 	 --$this->listindentlevel;
19981  	 	 	 	 if ($this->listnum <= 0) {
19982  	 	 	 	 	 $this->listnum = 0;
19983  	 	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
19984  	 	 	 	 } else {
19985  	 	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19986  	 	 	 	 }
19987  	 	 	 	 $this->resetLastH();
19988  	 	 	 	 break;
19989  	 	 	 }
19990  	 	 	 case 'li': {
19991  	 	 	 	 $this->lispacer = '';
19992  	 	 	 	 $this->addHTMLVertSpace(0, 0, $cell, false, $lasttag);
19993  	 	 	 	 break;
19994  	 	 	 }
19995  	 	 	 case 'h1':
19996  	 	 	 case 'h2':
19997  	 	 	 case 'h3':
19998  	 	 	 case 'h4':
19999  	 	 	 case 'h5':
20000  	 	 	 case 'h6': {
20001  	 	 	 	 $this->addHTMLVertSpace($hbz, $hb, $cell, false, $lasttag);
20002  	 	 	 	 break;
20003  	 	 	 }
20004  	 	 	 // Form fields (since 4.8.000 - 2009-09-07)
20005  	 	 	 case 'form': {
20006  	 	 	 	 $this->form_action = '';
20007  	 	 	 	 $this->form_enctype = 'application/x-www-form-urlencoded';
20008  	 	 	 	 break;
20009  	 	 	 }
20010  	 	 	 default : {
20011  	 	 	 	 break;
20012  	 	 	 }
20013  	 	 }
20014  	 	 // draw border and background (if any)
20015  	 	 $this->drawHTMLTagBorder($parent, $xmax);
20016  	 	 if (isset($dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'])) {
20017  	 	 	 $pba = $dom[($dom[$key]['parent'])]['attribute']['pagebreakafter'];
20018  	 	 	 // check for pagebreak
20019  	 	 	 if (($pba == 'true') OR ($pba == 'left') OR ($pba == 'right')) {
20020  	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
20021  	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
20022  	 	 	 }
20023  	 	 	 if ((($pba == 'left') AND (((!$this->rtl) AND (($this->page % 2) == 0)) OR (($this->rtl) AND (($this->page % 2) != 0))))
20024  	 	 	 	 OR (($pba == 'right') AND (((!$this->rtl) AND (($this->page % 2) != 0)) OR (($this->rtl) AND (($this->page % 2) == 0))))) {
20025  	 	 	 	 // add a page (or trig AcceptPageBreak() for multicolumn mode)
20026  	 	 	 	 $this->checkPageBreak($this->PageBreakTrigger + 1);
20027  	 	 	 }
20028  	 	 }
20029  	 	 $this->tmprtl = false;
20030  	 	 return $dom;
20031  	 }
20032  
20033  	 /**
20034  	  * Add vertical spaces if needed.
20035  	  * @param $hbz (string) Distance between current y and line bottom.
20036  	  * @param $hb (string) The height of the break.
20037  	  * @param $cell (boolean) if true add the default left (or right if RTL) padding to each new line (default false).
20038  	  * @param $firsttag (boolean) set to true when the tag is the first.
20039  	  * @param $lasttag (boolean) set to true when the tag is the last.
20040  	  * @protected
20041  	  */
20042  	protected function addHTMLVertSpace($hbz=0, $hb=0, $cell=false, $firsttag=false, $lasttag=false) {
20043  	 	 if ($firsttag) {
20044  	 	 	 $this->Ln(0, $cell);
20045  	 	 	 $this->htmlvspace = 0;
20046  	 	 	 return;
20047  	 	 }
20048  	 	 if ($lasttag) {
20049  	 	 	 $this->Ln($hbz, $cell);
20050  	 	 	 $this->htmlvspace = 0;
20051  	 	 	 return;
20052  	 	 }
20053  	 	 if ($hb < $this->htmlvspace) {
20054  	 	 	 $hd = 0;
20055  	 	 } else {
20056  	 	 	 $hd = $hb - $this->htmlvspace;
20057  	 	 	 $this->htmlvspace = $hb;
20058  	 	 }
20059  	 	 $this->Ln(($hbz + $hd), $cell);
20060  	 }
20061  
20062  	 /**
20063  	  * Return the starting coordinates to draw an html border
20064  	  * @return array containing top-left border coordinates
20065  	  * @protected
20066  	  * @since 5.7.000 (2010-08-03)
20067  	  */
20068  	protected function getBorderStartPosition() {
20069  	 	 if ($this->rtl) {
20070  	 	 	 $xmax = $this->lMargin;
20071  	 	 } else {
20072  	 	 	 $xmax = $this->w - $this->rMargin;
20073  	 	 }
20074  	 	 return array('page' => $this->page, 'column' => $this->current_column, 'x' => $this->x, 'y' => $this->y, 'xmax' => $xmax);
20075  	 }
20076  
20077  	 /**
20078  	  * Draw an HTML block border and fill
20079  	  * @param $tag (array) array of tag properties.
20080  	  * @param $xmax (int) end X coordinate for border.
20081  	  * @protected
20082  	  * @since 5.7.000 (2010-08-03)
20083  	  */
20084  	protected function drawHTMLTagBorder($tag, $xmax) {
20085  	 	 if (!isset($tag['borderposition'])) {
20086  	 	 	 // nothing to draw
20087  	 	 	 return;
20088  	 	 }
20089  	 	 $prev_x = $this->x;
20090  	 	 $prev_y = $this->y;
20091  	 	 $prev_lasth = $this->lasth;
20092  	 	 $border = 0;
20093  	 	 $fill = false;
20094  	 	 $this->lasth = 0;
20095  	 	 if (isset($tag['border']) AND !empty($tag['border'])) {
20096  	 	 	 // get border style
20097  	 	 	 $border = $tag['border'];
20098  	 	 	 if (!TCPDF_STATIC::empty_string($this->thead) AND (!$this->inthead)) {
20099  	 	 	 	 // border for table header
20100  	 	 	 	 $border = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20101  	 	 	 }
20102  	 	 }
20103  	 	 if (isset($tag['bgcolor']) AND ($tag['bgcolor'] !== false)) {
20104  	 	 	 // get background color
20105  	 	 	 $old_bgcolor = $this->bgcolor;
20106  	 	 	 $this->SetFillColorArray($tag['bgcolor']);
20107  	 	 	 $fill = true;
20108  	 	 }
20109  	 	 if (!$border AND !$fill) {
20110  	 	 	 // nothing to draw
20111  	 	 	 return;
20112  	 	 }
20113  	 	 if (isset($tag['attribute']['cellspacing'])) {
20114  	 	 	 $clsp = $this->getHTMLUnitToUnits($tag['attribute']['cellspacing'], 1, 'px');
20115  	 	 	 $cellspacing = array('H' => $clsp, 'V' => $clsp);
20116  	 	 } elseif (isset($tag['border-spacing'])) {
20117  	 	 	 $cellspacing = $tag['border-spacing'];
20118  	 	 } else {
20119  	 	 	 $cellspacing = array('H' => 0, 'V' => 0);
20120  	 	 }
20121  	 	 if (($tag['value'] != 'table') AND (is_array($border)) AND (!empty($border))) {
20122  	 	 	 // draw the border externally respect the sqare edge.
20123  	 	 	 $border['mode'] = 'ext';
20124  	 	 }
20125  	 	 if ($this->rtl) {
20126  	 	 	 if ($xmax >= $tag['borderposition']['x']) {
20127  	 	 	 	 $xmax = $tag['borderposition']['xmax'];
20128  	 	 	 }
20129  	 	 	 $w = ($tag['borderposition']['x'] - $xmax);
20130  	 	 } else {
20131  	 	 	 if ($xmax <= $tag['borderposition']['x']) {
20132  	 	 	 	 $xmax = $tag['borderposition']['xmax'];
20133  	 	 	 }
20134  	 	 	 $w = ($xmax - $tag['borderposition']['x']);
20135  	 	 }
20136  	 	 if ($w <= 0) {
20137  	 	 	 return;
20138  	 	 }
20139  	 	 $w += $cellspacing['H'];
20140  	 	 $startpage = $tag['borderposition']['page'];
20141  	 	 $startcolumn = $tag['borderposition']['column'];
20142  	 	 $x = $tag['borderposition']['x'];
20143  	 	 $y = $tag['borderposition']['y'];
20144  	 	 $endpage = $this->page;
20145  	 	 $starty = $tag['borderposition']['y'] - $cellspacing['V'];
20146  	 	 $currentY = $this->y;
20147  	 	 $this->x = $x;
20148  	 	 // get latest column
20149  	 	 $endcolumn = $this->current_column;
20150  	 	 if ($this->num_columns == 0) {
20151  	 	 	 $this->num_columns = 1;
20152  	 	 }
20153  	 	 // get border modes
20154  	 	 $border_start = TCPDF_STATIC::getBorderMode($border, $position='start', $this->opencell);
20155  	 	 $border_end = TCPDF_STATIC::getBorderMode($border, $position='end', $this->opencell);
20156  	 	 $border_middle = TCPDF_STATIC::getBorderMode($border, $position='middle', $this->opencell);
20157  	 	 // temporary disable page regions
20158  	 	 $temp_page_regions = $this->page_regions;
20159  	 	 $this->page_regions = array();
20160  	 	 // design borders around HTML cells.
20161  	 	 for ($page = $startpage; $page <= $endpage; ++$page) { // for each page
20162  	 	 	 $ccode = '';
20163  	 	 	 $this->setPage($page);
20164  	 	 	 if ($this->num_columns < 2) {
20165  	 	 	 	 // single-column mode
20166  	 	 	 	 $this->x = $x;
20167  	 	 	 	 $this->y = $this->tMargin;
20168  	 	 	 }
20169  	 	 	 // account for margin changes
20170  	 	 	 if ($page > $startpage) {
20171  	 	 	 	 if (($this->rtl) AND ($this->pagedim[$page]['orm'] != $this->pagedim[$startpage]['orm'])) {
20172  	 	 	 	 	 $this->x -= ($this->pagedim[$page]['orm'] - $this->pagedim[$startpage]['orm']);
20173  	 	 	 	 } elseif ((!$this->rtl) AND ($this->pagedim[$page]['olm'] != $this->pagedim[$startpage]['olm'])) {
20174  	 	 	 	 	 $this->x += ($this->pagedim[$page]['olm'] - $this->pagedim[$startpage]['olm']);
20175  	 	 	 	 }
20176  	 	 	 }
20177  	 	 	 if ($startpage == $endpage) {
20178  	 	 	 	 // single page
20179  	 	 	 	 for ($column = $startcolumn; $column <= $endcolumn; ++$column) { // for each column
20180  	 	 	 	 	 $this->selectColumn($column);
20181  	 	 	 	 	 if ($startcolumn == $endcolumn) { // single column
20182  	 	 	 	 	 	 $cborder = $border;
20183  	 	 	 	 	 	 $h = ($currentY - $y) + $cellspacing['V'];
20184  	 	 	 	 	 	 $this->y = $starty;
20185  	 	 	 	 	 } elseif ($column == $startcolumn) { // first column
20186  	 	 	 	 	 	 $cborder = $border_start;
20187  	 	 	 	 	 	 $this->y = $starty;
20188  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
20189  	 	 	 	 	 } elseif ($column == $endcolumn) { // end column
20190  	 	 	 	 	 	 $cborder = $border_end;
20191  	 	 	 	 	 	 $h = $currentY - $this->y;
20192  	 	 	 	 	 } else { // middle column
20193  	 	 	 	 	 	 $cborder = $border_middle;
20194  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
20195  	 	 	 	 	 }
20196  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20197  	 	 	 	 } // end for each column
20198  	 	 	 } elseif ($page == $startpage) { // first page
20199  	 	 	 	 for ($column = $startcolumn; $column < $this->num_columns; ++$column) { // for each column
20200  	 	 	 	 	 $this->selectColumn($column);
20201  	 	 	 	 	 if ($column == $startcolumn) { // first column
20202  	 	 	 	 	 	 $cborder = $border_start;
20203  	 	 	 	 	 	 $this->y = $starty;
20204  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
20205  	 	 	 	 	 } else { // middle column
20206  	 	 	 	 	 	 $cborder = $border_middle;
20207  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
20208  	 	 	 	 	 }
20209  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20210  	 	 	 	 } // end for each column
20211  	 	 	 } elseif ($page == $endpage) { // last page
20212  	 	 	 	 for ($column = 0; $column <= $endcolumn; ++$column) { // for each column
20213  	 	 	 	 	 $this->selectColumn($column);
20214  	 	 	 	 	 if ($column == $endcolumn) {
20215  	 	 	 	 	 	 // end column
20216  	 	 	 	 	 	 $cborder = $border_end;
20217  	 	 	 	 	 	 $h = $currentY - $this->y;
20218  	 	 	 	 	 } else {
20219  	 	 	 	 	 	 // middle column
20220  	 	 	 	 	 	 $cborder = $border_middle;
20221  	 	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
20222  	 	 	 	 	 }
20223  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20224  	 	 	 	 } // end for each column
20225  	 	 	 } else { // middle page
20226  	 	 	 	 for ($column = 0; $column < $this->num_columns; ++$column) { // for each column
20227  	 	 	 	 	 $this->selectColumn($column);
20228  	 	 	 	 	 $cborder = $border_middle;
20229  	 	 	 	 	 $h = $this->h - $this->y - $this->bMargin;
20230  	 	 	 	 	 $ccode .= $this->getCellCode($w, $h, '', $cborder, 1, '', $fill, '', 0, true)."\n";
20231  	 	 	 	 } // end for each column
20232  	 	 	 }
20233  	 	 	 if ($cborder OR $fill) {
20234  	 	 	 	 $offsetlen = strlen($ccode);
20235  	 	 	 	 // draw border and fill
20236  	 	 	 	 if ($this->inxobj) {
20237  	 	 	 	 	 // we are inside an XObject template
20238  	 	 	 	 	 if (end($this->xobjects[$this->xobjid]['transfmrk']) !== false) {
20239  	 	 	 	 	 	 $pagemarkkey = key($this->xobjects[$this->xobjid]['transfmrk']);
20240  	 	 	 	 	 	 $pagemark = $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey];
20241  	 	 	 	 	 	 $this->xobjects[$this->xobjid]['transfmrk'][$pagemarkkey] += $offsetlen;
20242  	 	 	 	 	 } else {
20243  	 	 	 	 	 	 $pagemark = $this->xobjects[$this->xobjid]['intmrk'];
20244  	 	 	 	 	 	 $this->xobjects[$this->xobjid]['intmrk'] += $offsetlen;
20245  	 	 	 	 	 }
20246  	 	 	 	 	 $pagebuff = $this->xobjects[$this->xobjid]['outdata'];
20247  	 	 	 	 	 $pstart = substr($pagebuff, 0, $pagemark);
20248  	 	 	 	 	 $pend = substr($pagebuff, $pagemark);
20249  	 	 	 	 	 $this->xobjects[$this->xobjid]['outdata'] = $pstart.$ccode.$pend;
20250  	 	 	 	 } else {
20251  	 	 	 	 	 if (end($this->transfmrk[$this->page]) !== false) {
20252  	 	 	 	 	 	 $pagemarkkey = key($this->transfmrk[$this->page]);
20253  	 	 	 	 	 	 $pagemark = $this->transfmrk[$this->page][$pagemarkkey];
20254  	 	 	 	 	 } elseif ($this->InFooter) {
20255  	 	 	 	 	 	 $pagemark = $this->footerpos[$this->page];
20256  	 	 	 	 	 } else {
20257  	 	 	 	 	 	 $pagemark = $this->intmrk[$this->page];
20258  	 	 	 	 	 }
20259  	 	 	 	 	 $pagebuff = $this->getPageBuffer($this->page);
20260  	 	 	 	 	 $pstart = substr($pagebuff, 0, $pagemark);
20261  	 	 	 	 	 $pend = substr($pagebuff, $pagemark);
20262  	 	 	 	 	 $this->setPageBuffer($this->page, $pstart.$ccode.$pend);
20263  	 	 	 	 	 $this->bordermrk[$this->page] += $offsetlen;
20264  	 	 	 	 	 $this->cntmrk[$this->page] += $offsetlen;
20265  	 	 	 	 }
20266  	 	 	 }
20267  	 	 } // end for each page
20268  	 	 // restore page regions
20269  	 	 $this->page_regions = $temp_page_regions;
20270  	 	 if (isset($old_bgcolor)) {
20271  	 	 	 // restore background color
20272  	 	 	 $this->SetFillColorArray($old_bgcolor);
20273  	 	 }
20274  	 	 // restore pointer position
20275  	 	 $this->x = $prev_x;
20276  	 	 $this->y = $prev_y;
20277  	 	 $this->lasth = $prev_lasth;
20278  	 }
20279  
20280  	 /**
20281  	  * Set the default bullet to be used as LI bullet symbol
20282  	  * @param $symbol (string) 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', 'img|type|width|height|image.ext')
20283  	  * @public
20284  	  * @since 4.0.028 (2008-09-26)
20285  	  */
20286  	public function setLIsymbol($symbol='!') {
20287  	 	 // check for custom image symbol
20288  	 	 if (substr($symbol, 0, 4) == 'img|') {
20289  	 	 	 $this->lisymbol = $symbol;
20290  	 	 	 return;
20291  	 	 }
20292  	 	 $symbol = strtolower($symbol);
20293  	 	 $valid_symbols = array('!', '#', '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');
20294  	 	 if (in_array($symbol, $valid_symbols)) {
20295  	 	 	 $this->lisymbol = $symbol;
20296  	 	 } else {
20297  	 	 	 $this->lisymbol = '';
20298  	 	 }
20299  	 }
20300  
20301  	 /**
20302  	  * Set the booklet mode for double-sided pages.
20303  	  * @param $booklet (boolean) true set the booklet mode on, false otherwise.
20304  	  * @param $inner (float) Inner page margin.
20305  	  * @param $outer (float) Outer page margin.
20306  	  * @public
20307  	  * @since 4.2.000 (2008-10-29)
20308  	  */
20309  	public function SetBooklet($booklet=true, $inner=-1, $outer=-1) {
20310  	 	 $this->booklet = $booklet;
20311  	 	 if ($inner >= 0) {
20312  	 	 	 $this->lMargin = $inner;
20313  	 	 }
20314  	 	 if ($outer >= 0) {
20315  	 	 	 $this->rMargin = $outer;
20316  	 	 }
20317  	 }
20318  
20319  	 /**
20320  	  * Swap the left and right margins.
20321  	  * @param $reverse (boolean) if true swap left and right margins.
20322  	  * @protected
20323  	  * @since 4.2.000 (2008-10-29)
20324  	  */
20325  	protected function swapMargins($reverse=true) {
20326  	 	 if ($reverse) {
20327  	 	 	 // swap left and right margins
20328  	 	 	 $mtemp = $this->original_lMargin;
20329  	 	 	 $this->original_lMargin = $this->original_rMargin;
20330  	 	 	 $this->original_rMargin = $mtemp;
20331  	 	 	 $deltam = $this->original_lMargin - $this->original_rMargin;
20332  	 	 	 $this->lMargin += $deltam;
20333  	 	 	 $this->rMargin -= $deltam;
20334  	 	 }
20335  	 }
20336  
20337  	 /**
20338  	  * Set the vertical spaces for HTML tags.
20339  	  * The array must have the following structure (example):
20340  	  * $tagvs = array('h1' => array(0 => array('h' => '', 'n' => 2), 1 => array('h' => 1.3, 'n' => 1)));
20341  	  * The first array level contains the tag names,
20342  	  * the second level contains 0 for opening tags or 1 for closing tags,
20343  	  * the third level contains the vertical space unit (h) and the number spaces to add (n).
20344  	  * If the h parameter is not specified, default values are used.
20345  	  * @param $tagvs (array) array of tags and relative vertical spaces.
20346  	  * @public
20347  	  * @since 4.2.001 (2008-10-30)
20348  	  */
20349  	public function setHtmlVSpace($tagvs) {
20350  	 	 $this->tagvspaces = $tagvs;
20351  	 }
20352  
20353  	 /**
20354  	  * Set custom width for list indentation.
20355  	  * @param $width (float) width of the indentation. Use negative value to disable it.
20356  	  * @public
20357  	  * @since 4.2.007 (2008-11-12)
20358  	  */
20359  	public function setListIndentWidth($width) {
20360  	 	 return $this->customlistindent = floatval($width);
20361  	 }
20362  
20363  	 /**
20364  	  * Set the top/bottom cell sides to be open or closed when the cell cross the page.
20365  	  * @param $isopen (boolean) if true keeps the top/bottom border open for the cell sides that cross the page.
20366  	  * @public
20367  	  * @since 4.2.010 (2008-11-14)
20368  	  */
20369  	public function setOpenCell($isopen) {
20370  	 	 $this->opencell = $isopen;
20371  	 }
20372  
20373  	 /**
20374  	  * Set the color and font style for HTML links.
20375  	  * @param $color (array) RGB array of colors
20376  	  * @param $fontstyle (string) additional font styles to add
20377  	  * @public
20378  	  * @since 4.4.003 (2008-12-09)
20379  	  */
20380  	public function setHtmlLinksStyle($color=array(0,0,255), $fontstyle='U') {
20381  	 	 $this->htmlLinkColorArray = $color;
20382  	 	 $this->htmlLinkFontStyle = $fontstyle;
20383  	 }
20384  
20385  	 /**
20386  	  * Convert HTML string containing value and unit of measure to user's units or points.
20387  	  * @param $htmlval (string) String containing values and unit.
20388  	  * @param $refsize (string) Reference value in points.
20389  	  * @param $defaultunit (string) Default unit (can be one of the following: %, em, ex, px, in, mm, pc, pt).
20390  	  * @param $points (boolean) If true returns points, otherwise returns value in user's units.
20391  	  * @return float value in user's unit or point if $points=true
20392  	  * @public
20393  	  * @since 4.4.004 (2008-12-10)
20394  	  */
20395  	public function getHTMLUnitToUnits($htmlval, $refsize=1, $defaultunit='px', $points=false) {
20396  	 	 $supportedunits = array('%', 'em', 'ex', 'px', 'in', 'cm', 'mm', 'pc', 'pt');
20397  	 	 $retval = 0;
20398  	 	 $value = 0;
20399  	 	 $unit = 'px';
20400  	 	 if ($points) {
20401  	 	 	 $k = 1;
20402  	 	 } else {
20403  	 	 	 $k = $this->k;
20404  	 	 }
20405  	 	 if (in_array($defaultunit, $supportedunits)) {
20406  	 	 	 $unit = $defaultunit;
20407  	 	 }
20408  	 	 if (is_numeric($htmlval)) {
20409  	 	 	 $value = floatval($htmlval);
20410  	 	 } elseif (preg_match('/([0-9\.\-\+]+)/', $htmlval, $mnum)) {
20411  	 	 	 $value = floatval($mnum[1]);
20412  	 	 	 if (preg_match('/([a-z%]+)/', $htmlval, $munit)) {
20413  	 	 	 	 if (in_array($munit[1], $supportedunits)) {
20414  	 	 	 	 	 $unit = $munit[1];
20415  	 	 	 	 }
20416  	 	 	 }
20417  	 	 }
20418  	 	 switch ($unit) {
20419  	 	 	 // percentage
20420  	 	 	 case '%': {
20421  	 	 	 	 $retval = (($value * $refsize) / 100);
20422  	 	 	 	 break;
20423  	 	 	 }
20424  	 	 	 // relative-size
20425  	 	 	 case 'em': {
20426  	 	 	 	 $retval = ($value * $refsize);
20427  	 	 	 	 break;
20428  	 	 	 }
20429  	 	 	 // height of lower case 'x' (about half the font-size)
20430  	 	 	 case 'ex': {
20431  	 	 	 	 $retval = ($value * ($refsize / 2));
20432  	 	 	 	 break;
20433  	 	 	 }
20434  	 	 	 // absolute-size
20435  	 	 	 case 'in': {
20436  	 	 	 	 $retval = (($value * $this->dpi) / $k);
20437  	 	 	 	 break;
20438  	 	 	 }
20439  	 	 	 // centimeters
20440  	 	 	 case 'cm': {
20441  	 	 	 	 $retval = (($value / 2.54 * $this->dpi) / $k);
20442  	 	 	 	 break;
20443  	 	 	 }
20444  	 	 	 // millimeters
20445  	 	 	 case 'mm': {
20446  	 	 	 	 $retval = (($value / 25.4 * $this->dpi) / $k);
20447  	 	 	 	 break;
20448  	 	 	 }
20449  	 	 	 // one pica is 12 points
20450  	 	 	 case 'pc': {
20451  	 	 	 	 $retval = (($value * 12) / $k);
20452  	 	 	 	 break;
20453  	 	 	 }
20454  	 	 	 // points
20455  	 	 	 case 'pt': {
20456  	 	 	 	 $retval = ($value / $k);
20457  	 	 	 	 break;
20458  	 	 	 }
20459  	 	 	 // pixels
20460  	 	 	 case 'px': {
20461  	 	 	 	 $retval = $this->pixelsToUnits($value);
20462  	 	 	 	 if ($points) {
20463  	 	 	 	 	 $retval *= $this->k;
20464  	 	 	 	 }
20465  	 	 	 	 break;
20466  	 	 	 }
20467  	 	 }
20468  	 	 return $retval;
20469  	 }
20470  
20471  	 /**
20472  	  * Output an HTML list bullet or ordered item symbol
20473  	  * @param $listdepth (int) list nesting level
20474  	  * @param $listtype (string) type of list
20475  	  * @param $size (float) current font size
20476  	  * @protected
20477  	  * @since 4.4.004 (2008-12-10)
20478  	  */
20479  	protected function putHtmlListBullet($listdepth, $listtype='', $size=10) {
20480  	 	 if ($this->state != 2) {
20481  	 	 	 return;
20482  	 	 }
20483  	 	 $size /= $this->k;
20484  	 	 $fill = '';
20485  	 	 $bgcolor = $this->bgcolor;
20486  	 	 $color = $this->fgcolor;
20487  	 	 $strokecolor = $this->strokecolor;
20488  	 	 $width = 0;
20489  	 	 $textitem = '';
20490  	 	 $tmpx = $this->x;
20491  	 	 $lspace = $this->GetStringWidth('  ');
20492  	 	 if ($listtype == '^') {
20493  	 	 	 // special symbol used for avoid justification of rect bullet
20494  	 	 	 $this->lispacer = '';
20495  	 	 	 return;
20496  	 	 } elseif ($listtype == '!') {
20497  	 	 	 // set default list type for unordered list
20498  	 	 	 $deftypes = array('disc', 'circle', 'square');
20499  	 	 	 $listtype = $deftypes[($listdepth - 1) % 3];
20500  	 	 } elseif ($listtype == '#') {
20501  	 	 	 // set default list type for ordered list
20502  	 	 	 $listtype = 'decimal';
20503  	 	 } elseif (substr($listtype, 0, 4) == 'img|') {
20504  	 	 	 // custom image type ('img|type|width|height|image.ext')
20505  	 	 	 $img = explode('|', $listtype);
20506  	 	 	 $listtype = 'img';
20507  	 	 }
20508  	 	 switch ($listtype) {
20509  	 	 	 // unordered types
20510  	 	 	 case 'none': {
20511  	 	 	 	 break;
20512  	 	 	 }
20513  	 	 	 case 'disc': {
20514  	 	 	 	 $r = $size / 6;
20515  	 	 	 	 $lspace += (2 * $r);
20516  	 	 	 	 if ($this->rtl) {
20517  	 	 	 	 	 $this->x += $lspace;
20518  	 	 	 	 } else {
20519  	 	 	 	 	 $this->x -= $lspace;
20520  	 	 	 	 }
20521  	 	 	 	 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), $r, 0, 360, 'F', array(), $color, 8);
20522  	 	 	 	 break;
20523  	 	 	 }
20524  	 	 	 case 'circle': {
20525  	 	 	 	 $r = $size / 6;
20526  	 	 	 	 $lspace += (2 * $r);
20527  	 	 	 	 if ($this->rtl) {
20528  	 	 	 	 	 $this->x += $lspace;
20529  	 	 	 	 } else {
20530  	 	 	 	 	 $this->x -= $lspace;
20531  	 	 	 	 }
20532  	 	 	 	 $prev_line_style = $this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor;
20533  	 	 	 	 $new_line_style = array('width' => ($r / 3), 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'phase' => 0, 'color'=>$color);
20534  	 	 	 	 $this->Circle(($this->x + $r), ($this->y + ($this->lasth / 2)), ($r * (1 - (1/6))), 0, 360, 'D', $new_line_style, array(), 8);
20535  	 	 	 	 $this->_out($prev_line_style); // restore line settings
20536  	 	 	 	 break;
20537  	 	 	 }
20538  	 	 	 case 'square': {
20539  	 	 	 	 $l = $size / 3;
20540  	 	 	 	 $lspace += $l;
20541  	 	 	 	 if ($this->rtl) {;
20542  	 	 	 	 	 $this->x += $lspace;
20543  	 	 	 	 } else {
20544  	 	 	 	 	 $this->x -= $lspace;
20545  	 	 	 	 }
20546  	 	 	 	 $this->Rect($this->x, ($this->y + (($this->lasth - $l) / 2)), $l, $l, 'F', array(), $color);
20547  	 	 	 	 break;
20548  	 	 	 }
20549  	 	 	 case 'img': {
20550  	 	 	 	 // 1=>type, 2=>width, 3=>height, 4=>image.ext
20551  	 	 	 	 $lspace += $img[2];
20552  	 	 	 	 if ($this->rtl) {;
20553  	 	 	 	 	 $this->x += $lspace;
20554  	 	 	 	 } else {
20555  	 	 	 	 	 $this->x -= $lspace;
20556  	 	 	 	 }
20557  	 	 	 	 $imgtype = strtolower($img[1]);
20558  	 	 	 	 $prev_y = $this->y;
20559  	 	 	 	 switch ($imgtype) {
20560  	 	 	 	 	 case 'svg': {
20561  	 	 	 	 	 	 $this->ImageSVG($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', 'T', '', 0, false);
20562  	 	 	 	 	 	 break;
20563  	 	 	 	 	 }
20564  	 	 	 	 	 case 'ai':
20565  	 	 	 	 	 case 'eps': {
20566  	 	 	 	 	 	 $this->ImageEps($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], '', true, 'T', '', 0, false);
20567  	 	 	 	 	 	 break;
20568  	 	 	 	 	 }
20569  	 	 	 	 	 default: {
20570  	 	 	 	 	 	 $this->Image($img[4], $this->x, ($this->y + (($this->lasth - $img[3]) / 2)), $img[2], $img[3], $img[1], '', 'T', false, 300, '', false, false, 0, false, false, false);
20571  	 	 	 	 	 	 break;
20572  	 	 	 	 	 }
20573  	 	 	 	 }
20574  	 	 	 	 $this->y = $prev_y;
20575  	 	 	 	 break;
20576  	 	 	 }
20577  	 	 	 // ordered types
20578  	 	 	 // $this->listcount[$this->listnum];
20579  	 	 	 // $textitem
20580  	 	 	 case '1':
20581  	 	 	 case 'decimal': {
20582  	 	 	 	 $textitem = $this->listcount[$this->listnum];
20583  	 	 	 	 break;
20584  	 	 	 }
20585  	 	 	 case 'decimal-leading-zero': {
20586  	 	 	 	 $textitem = sprintf('%02d', $this->listcount[$this->listnum]);
20587  	 	 	 	 break;
20588  	 	 	 }
20589  	 	 	 case 'i':
20590  	 	 	 case 'lower-roman': {
20591  	 	 	 	 $textitem = strtolower(TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]));
20592  	 	 	 	 break;
20593  	 	 	 }
20594  	 	 	 case 'I':
20595  	 	 	 case 'upper-roman': {
20596  	 	 	 	 $textitem = TCPDF_STATIC::intToRoman($this->listcount[$this->listnum]);
20597  	 	 	 	 break;
20598  	 	 	 }
20599  	 	 	 case 'a':
20600  	 	 	 case 'lower-alpha':
20601  	 	 	 case 'lower-latin': {
20602  	 	 	 	 $textitem = chr(97 + $this->listcount[$this->listnum] - 1);
20603  	 	 	 	 break;
20604  	 	 	 }
20605  	 	 	 case 'A':
20606  	 	 	 case 'upper-alpha':
20607  	 	 	 case 'upper-latin': {
20608  	 	 	 	 $textitem = chr(65 + $this->listcount[$this->listnum] - 1);
20609  	 	 	 	 break;
20610  	 	 	 }
20611  	 	 	 case 'lower-greek': {
20612  	 	 	 	 $textitem = TCPDF_FONTS::unichr((945 + $this->listcount[$this->listnum] - 1), $this->isunicode);
20613  	 	 	 	 break;
20614  	 	 	 }
20615  	 	 	 /*
20616  	 	 	 // Types to be implemented (special handling)
20617  	 	 	 case 'hebrew': {
20618  	 	 	 	 break;
20619  	 	 	 }
20620  	 	 	 case 'armenian': {
20621  	 	 	 	 break;
20622  	 	 	 }
20623  	 	 	 case 'georgian': {
20624  	 	 	 	 break;
20625  	 	 	 }
20626  	 	 	 case 'cjk-ideographic': {
20627  	 	 	 	 break;
20628  	 	 	 }
20629  	 	 	 case 'hiragana': {
20630  	 	 	 	 break;
20631  	 	 	 }
20632  	 	 	 case 'katakana': {
20633  	 	 	 	 break;
20634  	 	 	 }
20635  	 	 	 case 'hiragana-iroha': {
20636  	 	 	 	 break;
20637  	 	 	 }
20638  	 	 	 case 'katakana-iroha': {
20639  	 	 	 	 break;
20640  	 	 	 }
20641  	 	 	 */
20642  	 	 	 default: {
20643  	 	 	 	 $textitem = $this->listcount[$this->listnum];
20644  	 	 	 }
20645  	 	 }
20646  	 	 if (!TCPDF_STATIC::empty_string($textitem)) {
20647  	 	 	 // Check whether we need a new page or new column
20648  	 	 	 $prev_y = $this->y;
20649  	 	 	 $h = $this->getCellHeight($this->FontSize);
20650  	 	 	 if ($this->checkPageBreak($h) OR ($this->y < $prev_y)) {
20651  	 	 	 	 $tmpx = $this->x;
20652  	 	 	 }
20653  	 	 	 // print ordered item
20654  	 	 	 if ($this->rtl) {
20655  	 	 	 	 $textitem = '.'.$textitem;
20656  	 	 	 } else {
20657  	 	 	 	 $textitem = $textitem.'.';
20658  	 	 	 }
20659  	 	 	 $lspace += $this->GetStringWidth($textitem);
20660  	 	 	 if ($this->rtl) {
20661  	 	 	 	 $this->x += $lspace;
20662  	 	 	 } else {
20663  	 	 	 	 $this->x -= $lspace;
20664  	 	 	 }
20665  	 	 	 $this->Write($this->lasth, $textitem, '', false, '', false, 0, false);
20666  	 	 }
20667  	 	 $this->x = $tmpx;
20668  	 	 $this->lispacer = '^';
20669  	 	 // restore colors
20670  	 	 $this->SetFillColorArray($bgcolor);
20671  	 	 $this->SetDrawColorArray($strokecolor);
20672  	 	 $this->SettextColorArray($color);
20673  	 }
20674  
20675  	 /**
20676  	  * Returns current graphic variables as array.
20677  	  * @return array of graphic variables
20678  	  * @protected
20679  	  * @since 4.2.010 (2008-11-14)
20680  	  */
20681  	protected function getGraphicVars() {
20682  	 	 $grapvars = array(
20683  	 	 	 'FontFamily' => $this->FontFamily,
20684  	 	 	 'FontStyle' => $this->FontStyle,
20685  	 	 	 'FontSizePt' => $this->FontSizePt,
20686  	 	 	 'rMargin' => $this->rMargin,
20687  	 	 	 'lMargin' => $this->lMargin,
20688  	 	 	 'cell_padding' => $this->cell_padding,
20689  	 	 	 'cell_margin' => $this->cell_margin,
20690  	 	 	 'LineWidth' => $this->LineWidth,
20691  	 	 	 'linestyleWidth' => $this->linestyleWidth,
20692  	 	 	 'linestyleCap' => $this->linestyleCap,
20693  	 	 	 'linestyleJoin' => $this->linestyleJoin,
20694  	 	 	 'linestyleDash' => $this->linestyleDash,
20695  	 	 	 'textrendermode' => $this->textrendermode,
20696  	 	 	 'textstrokewidth' => $this->textstrokewidth,
20697  	 	 	 'DrawColor' => $this->DrawColor,
20698  	 	 	 'FillColor' => $this->FillColor,
20699  	 	 	 'TextColor' => $this->TextColor,
20700  	 	 	 'ColorFlag' => $this->ColorFlag,
20701  	 	 	 'bgcolor' => $this->bgcolor,
20702  	 	 	 'fgcolor' => $this->fgcolor,
20703  	 	 	 'htmlvspace' => $this->htmlvspace,
20704  	 	 	 'listindent' => $this->listindent,
20705  	 	 	 'listindentlevel' => $this->listindentlevel,
20706  	 	 	 'listnum' => $this->listnum,
20707  	 	 	 'listordered' => $this->listordered,
20708  	 	 	 'listcount' => $this->listcount,
20709  	 	 	 'lispacer' => $this->lispacer,
20710  	 	 	 'cell_height_ratio' => $this->cell_height_ratio,
20711  	 	 	 'font_stretching' => $this->font_stretching,
20712  	 	 	 'font_spacing' => $this->font_spacing,
20713  	 	 	 'alpha' => $this->alpha,
20714  	 	 	 // extended
20715  	 	 	 'lasth' => $this->lasth,
20716  	 	 	 'tMargin' => $this->tMargin,
20717  	 	 	 'bMargin' => $this->bMargin,
20718  	 	 	 'AutoPageBreak' => $this->AutoPageBreak,
20719  	 	 	 'PageBreakTrigger' => $this->PageBreakTrigger,
20720  	 	 	 'x' => $this->x,
20721  	 	 	 'y' => $this->y,
20722  	 	 	 'w' => $this->w,
20723  	 	 	 'h' => $this->h,
20724  	 	 	 'wPt' => $this->wPt,
20725  	 	 	 'hPt' => $this->hPt,
20726  	 	 	 'fwPt' => $this->fwPt,
20727  	 	 	 'fhPt' => $this->fhPt,
20728  	 	 	 'page' => $this->page,
20729  	 	 	 'current_column' => $this->current_column,
20730  	 	 	 'num_columns' => $this->num_columns
20731  	 	 	 );
20732  	 	 return $grapvars;
20733  	 }
20734  
20735  	 /**
20736  	  * Set graphic variables.
20737  	  * @param $gvars (array) array of graphic variablesto restore
20738  	  * @param $extended (boolean) if true restore extended graphic variables
20739  	  * @protected
20740  	  * @since 4.2.010 (2008-11-14)
20741  	  */
20742  	protected function setGraphicVars($gvars, $extended=false) {
20743  	 	 if ($this->state != 2) {
20744  	 	 	  return;
20745  	 	 }
20746  	 	 $this->FontFamily = $gvars['FontFamily'];
20747  	 	 $this->FontStyle = $gvars['FontStyle'];
20748  	 	 $this->FontSizePt = $gvars['FontSizePt'];
20749  	 	 $this->rMargin = $gvars['rMargin'];
20750  	 	 $this->lMargin = $gvars['lMargin'];
20751  	 	 $this->cell_padding = $gvars['cell_padding'];
20752  	 	 $this->cell_margin = $gvars['cell_margin'];
20753  	 	 $this->LineWidth = $gvars['LineWidth'];
20754  	 	 $this->linestyleWidth = $gvars['linestyleWidth'];
20755  	 	 $this->linestyleCap = $gvars['linestyleCap'];
20756  	 	 $this->linestyleJoin = $gvars['linestyleJoin'];
20757  	 	 $this->linestyleDash = $gvars['linestyleDash'];
20758  	 	 $this->textrendermode = $gvars['textrendermode'];
20759  	 	 $this->textstrokewidth = $gvars['textstrokewidth'];
20760  	 	 $this->DrawColor = $gvars['DrawColor'];
20761  	 	 $this->FillColor = $gvars['FillColor'];
20762  	 	 $this->TextColor = $gvars['TextColor'];
20763  	 	 $this->ColorFlag = $gvars['ColorFlag'];
20764  	 	 $this->bgcolor = $gvars['bgcolor'];
20765  	 	 $this->fgcolor = $gvars['fgcolor'];
20766  	 	 $this->htmlvspace = $gvars['htmlvspace'];
20767  	 	 $this->listindent = $gvars['listindent'];
20768  	 	 $this->listindentlevel = $gvars['listindentlevel'];
20769  	 	 $this->listnum = $gvars['listnum'];
20770  	 	 $this->listordered = $gvars['listordered'];
20771  	 	 $this->listcount = $gvars['listcount'];
20772  	 	 $this->lispacer = $gvars['lispacer'];
20773  	 	 $this->cell_height_ratio = $gvars['cell_height_ratio'];
20774  	 	 $this->font_stretching = $gvars['font_stretching'];
20775  	 	 $this->font_spacing = $gvars['font_spacing'];
20776  	 	 $this->alpha = $gvars['alpha'];
20777  	 	 if ($extended) {
20778  	 	 	 // restore extended values
20779  	 	 	 $this->lasth = $gvars['lasth'];
20780  	 	 	 $this->tMargin = $gvars['tMargin'];
20781  	 	 	 $this->bMargin = $gvars['bMargin'];
20782  	 	 	 $this->AutoPageBreak = $gvars['AutoPageBreak'];
20783  	 	 	 $this->PageBreakTrigger = $gvars['PageBreakTrigger'];
20784  	 	 	 $this->x = $gvars['x'];
20785  	 	 	 $this->y = $gvars['y'];
20786  	 	 	 $this->w = $gvars['w'];
20787  	 	 	 $this->h = $gvars['h'];
20788  	 	 	 $this->wPt = $gvars['wPt'];
20789  	 	 	 $this->hPt = $gvars['hPt'];
20790  	 	 	 $this->fwPt = $gvars['fwPt'];
20791  	 	 	 $this->fhPt = $gvars['fhPt'];
20792  	 	 	 $this->page = $gvars['page'];
20793  	 	 	 $this->current_column = $gvars['current_column'];
20794  	 	 	 $this->num_columns = $gvars['num_columns'];
20795  	 	 }
20796  	 	 $this->_out(''.$this->linestyleWidth.' '.$this->linestyleCap.' '.$this->linestyleJoin.' '.$this->linestyleDash.' '.$this->DrawColor.' '.$this->FillColor.'');
20797  	 	 if (!TCPDF_STATIC::empty_string($this->FontFamily)) {
20798  	 	 	 $this->SetFont($this->FontFamily, $this->FontStyle, $this->FontSizePt);
20799  	 	 }
20800  	 }
20801  
20802  	 /**
20803  	  * Outputs the "save graphics state" operator 'q'
20804  	  * @protected
20805  	  */
20806  	protected function _outSaveGraphicsState() {
20807  	 	 $this->_out('q');
20808  	 }
20809  
20810  	 /**
20811  	  * Outputs the "restore graphics state" operator 'Q'
20812  	  * @protected
20813  	  */
20814  	protected function _outRestoreGraphicsState() {
20815  	 	 $this->_out('Q');
20816  	 }
20817  
20818  	 /**
20819  	  * Set buffer content (always append data).
20820  	  * @param $data (string) data
20821  	  * @protected
20822  	  * @since 4.5.000 (2009-01-02)
20823  	  */
20824  	protected function setBuffer($data) {
20825  	 	 $this->bufferlen += strlen($data);
20826  	 	 $this->buffer .= $data;
20827  	 }
20828  
20829  	 /**
20830  	  * Replace the buffer content
20831  	  * @param $data (string) data
20832  	  * @protected
20833  	  * @since 5.5.000 (2010-06-22)
20834  	  */
20835  	protected function replaceBuffer($data) {
20836  	 	 $this->bufferlen = strlen($data);
20837  	 	 $this->buffer = $data;
20838  	 }
20839  
20840  	 /**
20841  	  * Get buffer content.
20842  	  * @return string buffer content
20843  	  * @protected
20844  	  * @since 4.5.000 (2009-01-02)
20845  	  */
20846  	protected function getBuffer() {
20847  	 	 return $this->buffer;
20848  	 }
20849  
20850  	 /**
20851  	  * Set page buffer content.
20852  	  * @param $page (int) page number
20853  	  * @param $data (string) page data
20854  	  * @param $append (boolean) if true append data, false replace.
20855  	  * @protected
20856  	  * @since 4.5.000 (2008-12-31)
20857  	  */
20858  	protected function setPageBuffer($page, $data, $append=false) {
20859  	 	 if ($append) {
20860  	 	 	 $this->pages[$page] .= $data;
20861  	 	 } else {
20862  	 	 	 $this->pages[$page] = $data;
20863  	 	 }
20864  	 	 if ($append AND isset($this->pagelen[$page])) {
20865  	 	 	 $this->pagelen[$page] += strlen($data);
20866  	 	 } else {
20867  	 	 	 $this->pagelen[$page] = strlen($data);
20868  	 	 }
20869  	 }
20870  
20871  	 /**
20872  	  * Get page buffer content.
20873  	  * @param $page (int) page number
20874  	  * @return string page buffer content or false in case of error
20875  	  * @protected
20876  	  * @since 4.5.000 (2008-12-31)
20877  	  */
20878  	protected function getPageBuffer($page) {
20879  	 	 if (isset($this->pages[$page])) {
20880  	 	 	 return $this->pages[$page];
20881  	 	 }
20882  	 	 return false;
20883  	 }
20884  
20885  	 /**
20886  	  * Set image buffer content.
20887  	  * @param $image (string) image key
20888  	  * @param $data (array) image data
20889  	  * @return int image index number
20890  	  * @protected
20891  	  * @since 4.5.000 (2008-12-31)
20892  	  */
20893  	protected function setImageBuffer($image, $data) {
20894  	 	 if (($data['i'] = array_search($image, $this->imagekeys)) === FALSE) {
20895  	 	 	 $this->imagekeys[$this->numimages] = $image;
20896  	 	 	 $data['i'] = $this->numimages;
20897  	 	 	 ++$this->numimages;
20898  	 	 }
20899  	 	 $this->images[$image] = $data;
20900  	 	 return $data['i'];
20901  	 }
20902  
20903  	 /**
20904  	  * Set image buffer content for a specified sub-key.
20905  	  * @param $image (string) image key
20906  	  * @param $key (string) image sub-key
20907  	  * @param $data (array) image data
20908  	  * @protected
20909  	  * @since 4.5.000 (2008-12-31)
20910  	  */
20911  	protected function setImageSubBuffer($image, $key, $data) {
20912  	 	 if (!isset($this->images[$image])) {
20913  	 	 	 $this->setImageBuffer($image, array());
20914  	 	 }
20915  	 	 $this->images[$image][$key] = $data;
20916  	 }
20917  
20918  	 /**
20919  	  * Get image buffer content.
20920  	  * @param $image (string) image key
20921  	  * @return string image buffer content or false in case of error
20922  	  * @protected
20923  	  * @since 4.5.000 (2008-12-31)
20924  	  */
20925  	protected function getImageBuffer($image) {
20926  	 	 if (isset($this->images[$image])) {
20927  	 	 	 return $this->images[$image];
20928  	 	 }
20929  	 	 return false;
20930  	 }
20931  
20932  	 /**
20933  	  * Set font buffer content.
20934  	  * @param $font (string) font key
20935  	  * @param $data (array) font data
20936  	  * @protected
20937  	  * @since 4.5.000 (2009-01-02)
20938  	  */
20939  	protected function setFontBuffer($font, $data) {
20940  	 	 $this->fonts[$font] = $data;
20941  	 	 if (!in_array($font, $this->fontkeys)) {
20942  	 	 	 $this->fontkeys[] = $font;
20943  	 	 	 // store object ID for current font
20944  	 	 	 ++$this->n;
20945  	 	 	 $this->font_obj_ids[$font] = $this->n;
20946  	 	 	 $this->setFontSubBuffer($font, 'n', $this->n);
20947  	 	 }
20948  	 }
20949  
20950  	 /**
20951  	  * Set font buffer content.
20952  	  * @param $font (string) font key
20953  	  * @param $key (string) font sub-key
20954  	  * @param $data (array) font data
20955  	  * @protected
20956  	  * @since 4.5.000 (2009-01-02)
20957  	  */
20958  	protected function setFontSubBuffer($font, $key, $data) {
20959  	 	 if (!isset($this->fonts[$font])) {
20960  	 	 	 $this->setFontBuffer($font, array());
20961  	 	 }
20962  	 	 $this->fonts[$font][$key] = $data;
20963  	 }
20964  
20965  	 /**
20966  	  * Get font buffer content.
20967  	  * @param $font (string) font key
20968  	  * @return string font buffer content or false in case of error
20969  	  * @protected
20970  	  * @since 4.5.000 (2009-01-02)
20971  	  */
20972  	protected function getFontBuffer($font) {
20973  	 	 if (isset($this->fonts[$font])) {
20974  	 	 	 return $this->fonts[$font];
20975  	 	 }
20976  	 	 return false;
20977  	 }
20978  
20979  	 /**
20980  	  * Move a page to a previous position.
20981  	  * @param $frompage (int) number of the source page
20982  	  * @param $topage (int) number of the destination page (must be less than $frompage)
20983  	  * @return true in case of success, false in case of error.
20984  	  * @public
20985  	  * @since 4.5.000 (2009-01-02)
20986  	  */
20987  	public function movePage($frompage, $topage) {
20988  	 	 if (($frompage > $this->numpages) OR ($frompage <= $topage)) {
20989  	 	 	 return false;
20990  	 	 }
20991  	 	 if ($frompage == $this->page) {
20992  	 	 	 // close the page before moving it
20993  	 	 	 $this->endPage();
20994  	 	 }
20995  	 	 // move all page-related states
20996  	 	 $tmppage = $this->getPageBuffer($frompage);
20997  	 	 $tmppagedim = $this->pagedim[$frompage];
20998  	 	 $tmppagelen = $this->pagelen[$frompage];
20999  	 	 $tmpintmrk = $this->intmrk[$frompage];
21000  	 	 $tmpbordermrk = $this->bordermrk[$frompage];
21001  	 	 $tmpcntmrk = $this->cntmrk[$frompage];
21002  	 	 $tmppageobjects = $this->pageobjects[$frompage];
21003  	 	 if (isset($this->footerpos[$frompage])) {
21004  	 	 	 $tmpfooterpos = $this->footerpos[$frompage];
21005  	 	 }
21006  	 	 if (isset($this->footerlen[$frompage])) {
21007  	 	 	 $tmpfooterlen = $this->footerlen[$frompage];
21008  	 	 }
21009  	 	 if (isset($this->transfmrk[$frompage])) {
21010  	 	 	 $tmptransfmrk = $this->transfmrk[$frompage];
21011  	 	 }
21012  	 	 if (isset($this->PageAnnots[$frompage])) {
21013  	 	 	 $tmpannots = $this->PageAnnots[$frompage];
21014  	 	 }
21015  	 	 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21016  	 	 	 for ($i = $frompage; $i > $topage; --$i) {
21017  	 	 	 	 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $frompage)) {
21018  	 	 	 	 	 --$this->pagegroups[$this->newpagegroup[$i]];
21019  	 	 	 	 	 break;
21020  	 	 	 	 }
21021  	 	 	 }
21022  	 	 	 for ($i = $topage; $i > 0; --$i) {
21023  	 	 	 	 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $topage)) {
21024  	 	 	 	 	 ++$this->pagegroups[$this->newpagegroup[$i]];
21025  	 	 	 	 	 break;
21026  	 	 	 	 }
21027  	 	 	 }
21028  	 	 }
21029  	 	 for ($i = $frompage; $i > $topage; --$i) {
21030  	 	 	 $j = $i - 1;
21031  	 	 	 // shift pages down
21032  	 	 	 $this->setPageBuffer($i, $this->getPageBuffer($j));
21033  	 	 	 $this->pagedim[$i] = $this->pagedim[$j];
21034  	 	 	 $this->pagelen[$i] = $this->pagelen[$j];
21035  	 	 	 $this->intmrk[$i] = $this->intmrk[$j];
21036  	 	 	 $this->bordermrk[$i] = $this->bordermrk[$j];
21037  	 	 	 $this->cntmrk[$i] = $this->cntmrk[$j];
21038  	 	 	 $this->pageobjects[$i] = $this->pageobjects[$j];
21039  	 	 	 if (isset($this->footerpos[$j])) {
21040  	 	 	 	 $this->footerpos[$i] = $this->footerpos[$j];
21041  	 	 	 } elseif (isset($this->footerpos[$i])) {
21042  	 	 	 	 unset($this->footerpos[$i]);
21043  	 	 	 }
21044  	 	 	 if (isset($this->footerlen[$j])) {
21045  	 	 	 	 $this->footerlen[$i] = $this->footerlen[$j];
21046  	 	 	 } elseif (isset($this->footerlen[$i])) {
21047  	 	 	 	 unset($this->footerlen[$i]);
21048  	 	 	 }
21049  	 	 	 if (isset($this->transfmrk[$j])) {
21050  	 	 	 	 $this->transfmrk[$i] = $this->transfmrk[$j];
21051  	 	 	 } elseif (isset($this->transfmrk[$i])) {
21052  	 	 	 	 unset($this->transfmrk[$i]);
21053  	 	 	 }
21054  	 	 	 if (isset($this->PageAnnots[$j])) {
21055  	 	 	 	 $this->PageAnnots[$i] = $this->PageAnnots[$j];
21056  	 	 	 } elseif (isset($this->PageAnnots[$i])) {
21057  	 	 	 	 unset($this->PageAnnots[$i]);
21058  	 	 	 }
21059  	 	 	 if (isset($this->newpagegroup[$j])) {
21060  	 	 	 	 $this->newpagegroup[$i] = $this->newpagegroup[$j];
21061  	 	 	 	 unset($this->newpagegroup[$j]);
21062  	 	 	 }
21063  	 	 	 if ($this->currpagegroup == $j) {
21064  	 	 	 	 $this->currpagegroup = $i;
21065  	 	 	 }
21066  	 	 }
21067  	 	 $this->setPageBuffer($topage, $tmppage);
21068  	 	 $this->pagedim[$topage] = $tmppagedim;
21069  	 	 $this->pagelen[$topage] = $tmppagelen;
21070  	 	 $this->intmrk[$topage] = $tmpintmrk;
21071  	 	 $this->bordermrk[$topage] = $tmpbordermrk;
21072  	 	 $this->cntmrk[$topage] = $tmpcntmrk;
21073  	 	 $this->pageobjects[$topage] = $tmppageobjects;
21074  	 	 if (isset($tmpfooterpos)) {
21075  	 	 	 $this->footerpos[$topage] = $tmpfooterpos;
21076  	 	 } elseif (isset($this->footerpos[$topage])) {
21077  	 	 	 unset($this->footerpos[$topage]);
21078  	 	 }
21079  	 	 if (isset($tmpfooterlen)) {
21080  	 	 	 $this->footerlen[$topage] = $tmpfooterlen;
21081  	 	 } elseif (isset($this->footerlen[$topage])) {
21082  	 	 	 unset($this->footerlen[$topage]);
21083  	 	 }
21084  	 	 if (isset($tmptransfmrk)) {
21085  	 	 	 $this->transfmrk[$topage] = $tmptransfmrk;
21086  	 	 } elseif (isset($this->transfmrk[$topage])) {
21087  	 	 	 unset($this->transfmrk[$topage]);
21088  	 	 }
21089  	 	 if (isset($tmpannots)) {
21090  	 	 	 $this->PageAnnots[$topage] = $tmpannots;
21091  	 	 } elseif (isset($this->PageAnnots[$topage])) {
21092  	 	 	 unset($this->PageAnnots[$topage]);
21093  	 	 }
21094  	 	 // adjust outlines
21095  	 	 $tmpoutlines = $this->outlines;
21096  	 	 foreach ($tmpoutlines as $key => $outline) {
21097  	 	 	 if (!$outline['f']) {
21098  	 	 	 	 if (($outline['p'] >= $topage) AND ($outline['p'] < $frompage)) {
21099  	 	 	 	 	 $this->outlines[$key]['p'] = ($outline['p'] + 1);
21100  	 	 	 	 } elseif ($outline['p'] == $frompage) {
21101  	 	 	 	 	 $this->outlines[$key]['p'] = $topage;
21102  	 	 	 	 }
21103  	 	 	 }
21104  	 	 }
21105  	 	 // adjust dests
21106  	 	 $tmpdests = $this->dests;
21107  	 	 foreach ($tmpdests as $key => $dest) {
21108  	 	 	 if (!$dest['f']) {
21109  	 	 	 	 if (($dest['p'] >= $topage) AND ($dest['p'] < $frompage)) {
21110  	 	 	 	 	 $this->dests[$key]['p'] = ($dest['p'] + 1);
21111  	 	 	 	 } elseif ($dest['p'] == $frompage) {
21112  	 	 	 	 	 $this->dests[$key]['p'] = $topage;
21113  	 	 	 	 }
21114  	 	 	 }
21115  	 	 }
21116  	 	 // adjust links
21117  	 	 $tmplinks = $this->links;
21118  	 	 foreach ($tmplinks as $key => $link) {
21119  	 	 	 if (!$link['f']) {
21120  	 	 	 	 if (($link['p'] >= $topage) AND ($link['p'] < $frompage)) {
21121  	 	 	 	 	 $this->links[$key]['p'] = ($link['p'] + 1);
21122  	 	 	 	 } elseif ($link['p'] == $frompage) {
21123  	 	 	 	 	 $this->links[$key]['p'] = $topage;
21124  	 	 	 	 }
21125  	 	 	 }
21126  	 	 }
21127  	 	 // adjust javascript
21128  	 	 $jfrompage = $frompage;
21129  	 	 $jtopage = $topage;
21130  	 	 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21131  	 	 	 foreach($pamatch[0] as $pk => $pmatch) {
21132  	 	 	 	 $pagenum = intval($pamatch[3][$pk]) + 1;
21133  	 	 	 	 if (($pagenum >= $jtopage) AND ($pagenum < $jfrompage)) {
21134  	 	 	 	 	 $newpage = ($pagenum + 1);
21135  	 	 	 	 } elseif ($pagenum == $jfrompage) {
21136  	 	 	 	 	 $newpage = $jtopage;
21137  	 	 	 	 } else {
21138  	 	 	 	 	 $newpage = $pagenum;
21139  	 	 	 	 }
21140  	 	 	 	 --$newpage;
21141  	 	 	 	 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21142  	 	 	 	 $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21143  	 	 	 }
21144  	 	 	 unset($pamatch);
21145  	 	 }
21146  	 	 // return to last page
21147  	 	 $this->lastPage(true);
21148  	 	 return true;
21149  	 }
21150  
21151  	 /**
21152  	  * Remove the specified page.
21153  	  * @param $page (int) page to remove
21154  	  * @return true in case of success, false in case of error.
21155  	  * @public
21156  	  * @since 4.6.004 (2009-04-23)
21157  	  */
21158  	public function deletePage($page) {
21159  	 	 if (($page < 1) OR ($page > $this->numpages)) {
21160  	 	 	 return false;
21161  	 	 }
21162  	 	 // delete current page
21163  	 	 unset($this->pages[$page]);
21164  	 	 unset($this->pagedim[$page]);
21165  	 	 unset($this->pagelen[$page]);
21166  	 	 unset($this->intmrk[$page]);
21167  	 	 unset($this->bordermrk[$page]);
21168  	 	 unset($this->cntmrk[$page]);
21169  	 	 foreach ($this->pageobjects[$page] as $oid) {
21170  	 	 	 if (isset($this->offsets[$oid])){
21171  	 	 	 	 unset($this->offsets[$oid]);
21172  	 	 	 }
21173  	 	 }
21174  	 	 unset($this->pageobjects[$page]);
21175  	 	 if (isset($this->footerpos[$page])) {
21176  	 	 	 unset($this->footerpos[$page]);
21177  	 	 }
21178  	 	 if (isset($this->footerlen[$page])) {
21179  	 	 	 unset($this->footerlen[$page]);
21180  	 	 }
21181  	 	 if (isset($this->transfmrk[$page])) {
21182  	 	 	 unset($this->transfmrk[$page]);
21183  	 	 }
21184  	 	 if (isset($this->PageAnnots[$page])) {
21185  	 	 	 unset($this->PageAnnots[$page]);
21186  	 	 }
21187  	 	 if (isset($this->newpagegroup) AND !empty($this->newpagegroup)) {
21188  	 	 	 for ($i = $page; $i > 0; --$i) {
21189  	 	 	 	 if (isset($this->newpagegroup[$i]) AND (($i + $this->pagegroups[$this->newpagegroup[$i]]) > $page)) {
21190  	 	 	 	 	 --$this->pagegroups[$this->newpagegroup[$i]];
21191  	 	 	 	 	 break;
21192  	 	 	 	 }
21193  	 	 	 }
21194  	 	 }
21195  	 	 if (isset($this->pageopen[$page])) {
21196  	 	 	 unset($this->pageopen[$page]);
21197  	 	 }
21198  	 	 if ($page < $this->numpages) {
21199  	 	 	 // update remaining pages
21200  	 	 	 for ($i = $page; $i < $this->numpages; ++$i) {
21201  	 	 	 	 $j = $i + 1;
21202  	 	 	 	 // shift pages
21203  	 	 	 	 $this->setPageBuffer($i, $this->getPageBuffer($j));
21204  	 	 	 	 $this->pagedim[$i] = $this->pagedim[$j];
21205  	 	 	 	 $this->pagelen[$i] = $this->pagelen[$j];
21206  	 	 	 	 $this->intmrk[$i] = $this->intmrk[$j];
21207  	 	 	 	 $this->bordermrk[$i] = $this->bordermrk[$j];
21208  	 	 	 	 $this->cntmrk[$i] = $this->cntmrk[$j];
21209  	 	 	 	 $this->pageobjects[$i] = $this->pageobjects[$j];
21210  	 	 	 	 if (isset($this->footerpos[$j])) {
21211  	 	 	 	 	 $this->footerpos[$i] = $this->footerpos[$j];
21212  	 	 	 	 } elseif (isset($this->footerpos[$i])) {
21213  	 	 	 	 	 unset($this->footerpos[$i]);
21214  	 	 	 	 }
21215  	 	 	 	 if (isset($this->footerlen[$j])) {
21216  	 	 	 	 	 $this->footerlen[$i] = $this->footerlen[$j];
21217  	 	 	 	 } elseif (isset($this->footerlen[$i])) {
21218  	 	 	 	 	 unset($this->footerlen[$i]);
21219  	 	 	 	 }
21220  	 	 	 	 if (isset($this->transfmrk[$j])) {
21221  	 	 	 	 	 $this->transfmrk[$i] = $this->transfmrk[$j];
21222  	 	 	 	 } elseif (isset($this->transfmrk[$i])) {
21223  	 	 	 	 	 unset($this->transfmrk[$i]);
21224  	 	 	 	 }
21225  	 	 	 	 if (isset($this->PageAnnots[$j])) {
21226  	 	 	 	 	 $this->PageAnnots[$i] = $this->PageAnnots[$j];
21227  	 	 	 	 } elseif (isset($this->PageAnnots[$i])) {
21228  	 	 	 	 	 unset($this->PageAnnots[$i]);
21229  	 	 	 	 }
21230  	 	 	 	 if (isset($this->newpagegroup[$j])) {
21231  	 	 	 	 	 $this->newpagegroup[$i] = $this->newpagegroup[$j];
21232  	 	 	 	 	 unset($this->newpagegroup[$j]);
21233  	 	 	 	 }
21234  	 	 	 	 if ($this->currpagegroup == $j) {
21235  	 	 	 	 	 $this->currpagegroup = $i;
21236  	 	 	 	 }
21237  	 	 	 	 if (isset($this->pageopen[$j])) {
21238  	 	 	 	 	 $this->pageopen[$i] = $this->pageopen[$j];
21239  	 	 	 	 } elseif (isset($this->pageopen[$i])) {
21240  	 	 	 	 	 unset($this->pageopen[$i]);
21241  	 	 	 	 }
21242  	 	 	 }
21243  	 	 	 // remove last page
21244  	 	 	 unset($this->pages[$this->numpages]);
21245  	 	 	 unset($this->pagedim[$this->numpages]);
21246  	 	 	 unset($this->pagelen[$this->numpages]);
21247  	 	 	 unset($this->intmrk[$this->numpages]);
21248  	 	 	 unset($this->bordermrk[$this->numpages]);
21249  	 	 	 unset($this->cntmrk[$this->numpages]);
21250  	 	 	 foreach ($this->pageobjects[$this->numpages] as $oid) {
21251  	 	 	 	 if (isset($this->offsets[$oid])){
21252  	 	 	 	 	 unset($this->offsets[$oid]);
21253  	 	 	 	 }
21254  	 	 	 }
21255  	 	 	 unset($this->pageobjects[$this->numpages]);
21256  	 	 	 if (isset($this->footerpos[$this->numpages])) {
21257  	 	 	 	 unset($this->footerpos[$this->numpages]);
21258  	 	 	 }
21259  	 	 	 if (isset($this->footerlen[$this->numpages])) {
21260  	 	 	 	 unset($this->footerlen[$this->numpages]);
21261  	 	 	 }
21262  	 	 	 if (isset($this->transfmrk[$this->numpages])) {
21263  	 	 	 	 unset($this->transfmrk[$this->numpages]);
21264  	 	 	 }
21265  	 	 	 if (isset($this->PageAnnots[$this->numpages])) {
21266  	 	 	 	 unset($this->PageAnnots[$this->numpages]);
21267  	 	 	 }
21268  	 	 	 if (isset($this->newpagegroup[$this->numpages])) {
21269  	 	 	 	 unset($this->newpagegroup[$this->numpages]);
21270  	 	 	 }
21271  	 	 	 if ($this->currpagegroup == $this->numpages) {
21272  	 	 	 	 $this->currpagegroup = ($this->numpages - 1);
21273  	 	 	 }
21274  	 	 	 if (isset($this->pagegroups[$this->numpages])) {
21275  	 	 	 	 unset($this->pagegroups[$this->numpages]);
21276  	 	 	 }
21277  	 	 	 if (isset($this->pageopen[$this->numpages])) {
21278  	 	 	 	 unset($this->pageopen[$this->numpages]);
21279  	 	 	 }
21280  	 	 }
21281  	 	 --$this->numpages;
21282  	 	 $this->page = $this->numpages;
21283  	 	 // adjust outlines
21284  	 	 $tmpoutlines = $this->outlines;
21285  	 	 foreach ($tmpoutlines as $key => $outline) {
21286  	 	 	 if (!$outline['f']) {
21287  	 	 	 	 if ($outline['p'] > $page) {
21288  	 	 	 	 	 $this->outlines[$key]['p'] = $outline['p'] - 1;
21289  	 	 	 	 } elseif ($outline['p'] == $page) {
21290  	 	 	 	 	 unset($this->outlines[$key]);
21291  	 	 	 	 }
21292  	 	 	 }
21293  	 	 }
21294  	 	 // adjust dests
21295  	 	 $tmpdests = $this->dests;
21296  	 	 foreach ($tmpdests as $key => $dest) {
21297  	 	 	 if (!$dest['f']) {
21298  	 	 	 	 if ($dest['p'] > $page) {
21299  	 	 	 	 	 $this->dests[$key]['p'] = $dest['p'] - 1;
21300  	 	 	 	 } elseif ($dest['p'] == $page) {
21301  	 	 	 	 	 unset($this->dests[$key]);
21302  	 	 	 	 }
21303  	 	 	 }
21304  	 	 }
21305  	 	 // adjust links
21306  	 	 $tmplinks = $this->links;
21307  	 	 foreach ($tmplinks as $key => $link) {
21308  	 	 	 if (!$link['f']) {
21309  	 	 	 	 if ($link['p'] > $page) {
21310  	 	 	 	 	 $this->links[$key]['p'] = $link['p'] - 1;
21311  	 	 	 	 } elseif ($link['p'] == $page) {
21312  	 	 	 	 	 unset($this->links[$key]);
21313  	 	 	 	 }
21314  	 	 	 }
21315  	 	 }
21316  	 	 // adjust javascript
21317  	 	 $jpage = $page;
21318  	 	 if (preg_match_all('/this\.addField\(\'([^\']*)\',\'([^\']*)\',([0-9]+)/', $this->javascript, $pamatch) > 0) {
21319  	 	 	 foreach($pamatch[0] as $pk => $pmatch) {
21320  	 	 	 	 $pagenum = intval($pamatch[3][$pk]) + 1;
21321  	 	 	 	 if ($pagenum >= $jpage) {
21322  	 	 	 	 	 $newpage = ($pagenum - 1);
21323  	 	 	 	 } elseif ($pagenum == $jpage) {
21324  	 	 	 	 	 $newpage = 1;
21325  	 	 	 	 } else {
21326  	 	 	 	 	 $newpage = $pagenum;
21327  	 	 	 	 }
21328  	 	 	 	 --$newpage;
21329  	 	 	 	 $newjs = "this.addField(\'".$pamatch[1][$pk]."\',\'".$pamatch[2][$pk]."\',".$newpage;
21330  	 	 	 	 $this->javascript = str_replace($pmatch, $newjs, $this->javascript);
21331  	 	 	 }
21332  	 	 	 unset($pamatch);
21333  	 	 }
21334  	 	 // return to last page
21335  	 	 if ($this->numpages > 0) {
21336  	 	 	 $this->lastPage(true);
21337  	 	 }
21338  	 	 return true;
21339  	 }
21340  
21341  	 /**
21342  	  * Clone the specified page to a new page.
21343  	  * @param $page (int) number of page to copy (0 = current page)
21344  	  * @return true in case of success, false in case of error.
21345  	  * @public
21346  	  * @since 4.9.015 (2010-04-20)
21347  	  */
21348  	public function copyPage($page=0) {
21349  	 	 if ($page == 0) {
21350  	 	 	 // default value
21351  	 	 	 $page = $this->page;
21352  	 	 }
21353  	 	 if (($page < 1) OR ($page > $this->numpages)) {
21354  	 	 	 return false;
21355  	 	 }
21356  	 	 // close the last page
21357  	 	 $this->endPage();
21358  	 	 // copy all page-related states
21359  	 	 ++$this->numpages;
21360  	 	 $this->page = $this->numpages;
21361  	 	 $this->setPageBuffer($this->page, $this->getPageBuffer($page));
21362  	 	 $this->pagedim[$this->page] = $this->pagedim[$page];
21363  	 	 $this->pagelen[$this->page] = $this->pagelen[$page];
21364  	 	 $this->intmrk[$this->page] = $this->intmrk[$page];
21365  	 	 $this->bordermrk[$this->page] = $this->bordermrk[$page];
21366  	 	 $this->cntmrk[$this->page] = $this->cntmrk[$page];
21367  	 	 $this->pageobjects[$this->page] = $this->pageobjects[$page];
21368  	 	 $this->pageopen[$this->page] = false;
21369  	 	 if (isset($this->footerpos[$page])) {
21370  	 	 	 $this->footerpos[$this->page] = $this->footerpos[$page];
21371  	 	 }
21372  	 	 if (isset($this->footerlen[$page])) {
21373  	 	 	 $this->footerlen[$this->page] = $this->footerlen[$page];
21374  	 	 }
21375  	 	 if (isset($this->transfmrk[$page])) {
21376  	 	 	 $this->transfmrk[$this->page] = $this->transfmrk[$page];
21377  	 	 }
21378  	 	 if (isset($this->PageAnnots[$page])) {
21379  	 	 	 $this->PageAnnots[$this->page] = $this->PageAnnots[$page];
21380  	 	 }
21381  	 	 if (isset($this->newpagegroup[$page])) {
21382  	 	 	 // start a new group
21383  	 	 	 $this->newpagegroup[$this->page] = sizeof($this->newpagegroup) + 1;
21384  	 	 	 $this->currpagegroup = $this->newpagegroup[$this->page];
21385  	 	 	 $this->pagegroups[$this->currpagegroup] = 1;
21386  	 	 } elseif (isset($this->currpagegroup) AND ($this->currpagegroup > 0)) {
21387  	 	 	 ++$this->pagegroups[$this->currpagegroup];
21388  	 	 }
21389  	 	 // copy outlines
21390  	 	 $tmpoutlines = $this->outlines;
21391  	 	 foreach ($tmpoutlines as $key => $outline) {
21392  	 	 	 if ($outline['p'] == $page) {
21393  	 	 	 	 $this->outlines[] = array('t' => $outline['t'], 'l' => $outline['l'], 'x' => $outline['x'], 'y' => $outline['y'], 'p' => $this->page, 'f' => $outline['f'], 's' => $outline['s'], 'c' => $outline['c']);
21394  	 	 	 }
21395  	 	 }
21396  	 	 // copy links
21397  	 	 $tmplinks = $this->links;
21398  	 	 foreach ($tmplinks as $key => $link) {
21399  	 	 	 if ($link['p'] == $page) {
21400  	 	 	 	 $this->links[] = array('p' => $this->page, 'y' => $link['y'], 'f' => $link['f']);
21401  	 	 	 }
21402  	 	 }
21403  	 	 // return to last page
21404  	 	 $this->lastPage(true);
21405  	 	 return true;
21406  	 }
21407  
21408  	 /**
21409  	  * Output a Table of Content Index (TOC).
21410  	  * This method must be called after all Bookmarks were set.
21411  	  * Before calling this method you have to open the page using the addTOCPage() method.
21412  	  * After calling this method you have to call endTOCPage() to close the TOC page.
21413  	  * You can override this method to achieve different styles.
21414  	  * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21415  	  * @param $numbersfont (string) set the font for page numbers (please use monospaced font for better alignment).
21416  	  * @param $filler (string) string used to fill the space between text and page number.
21417  	  * @param $toc_name (string) name to use for TOC bookmark.
21418  	  * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21419  	  * @param $color (array) RGB color array for bookmark title (values from 0 to 255).
21420  	  * @public
21421  	  * @author Nicola Asuni
21422  	  * @since 4.5.000 (2009-01-02)
21423  	  * @see addTOCPage(), endTOCPage(), addHTMLTOC()
21424  	  */
21425  	public function addTOC($page='', $numbersfont='', $filler='.', $toc_name='TOC', $style='', $color=array(0,0,0)) {
21426  	 	 $fontsize = $this->FontSizePt;
21427  	 	 $fontfamily = $this->FontFamily;
21428  	 	 $fontstyle = $this->FontStyle;
21429  	 	 $w = $this->w - $this->lMargin - $this->rMargin;
21430  	 	 $spacer = $this->GetStringWidth(chr(32)) * 4;
21431  	 	 $lmargin = $this->lMargin;
21432  	 	 $rmargin = $this->rMargin;
21433  	 	 $x_start = $this->GetX();
21434  	 	 $page_first = $this->page;
21435  	 	 $current_page = $this->page;
21436  	 	 $page_fill_start = false;
21437  	 	 $page_fill_end = false;
21438  	 	 $current_column = $this->current_column;
21439  	 	 if (TCPDF_STATIC::empty_string($numbersfont)) {
21440  	 	 	 $numbersfont = $this->default_monospaced_font;
21441  	 	 }
21442  	 	 if (TCPDF_STATIC::empty_string($filler)) {
21443  	 	 	 $filler = ' ';
21444  	 	 }
21445  	 	 if (TCPDF_STATIC::empty_string($page)) {
21446  	 	 	 $gap = ' ';
21447  	 	 } else {
21448  	 	 	 $gap = '';
21449  	 	 	 if ($page < 1) {
21450  	 	 	 	 $page = 1;
21451  	 	 	 }
21452  	 	 }
21453  	 	 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21454  	 	 $numwidth = $this->GetStringWidth('00000');
21455  	 	 $maxpage = 0; //used for pages on attached documents
21456  	 	 foreach ($this->outlines as $key => $outline) {
21457  	 	 	 // check for extra pages (used for attachments)
21458  	 	 	 if (($this->page > $page_first) AND ($outline['p'] >= $this->numpages)) {
21459  	 	 	 	 $outline['p'] += ($this->page - $page_first);
21460  	 	 	 }
21461  	 	 	 if ($this->rtl) {
21462  	 	 	 	 $aligntext = 'R';
21463  	 	 	 	 $alignnum = 'L';
21464  	 	 	 } else {
21465  	 	 	 	 $aligntext = 'L';
21466  	 	 	 	 $alignnum = 'R';
21467  	 	 	 }
21468  	 	 	 if ($outline['l'] == 0) {
21469  	 	 	 	 $this->SetFont($fontfamily, $outline['s'].'B', $fontsize);
21470  	 	 	 } else {
21471  	 	 	 	 $this->SetFont($fontfamily, $outline['s'], $fontsize - $outline['l']);
21472  	 	 	 }
21473  	 	 	 $this->SetTextColorArray($outline['c']);
21474  	 	 	 // check for page break
21475  	 	 	 $this->checkPageBreak(2 * $this->getCellHeight($this->FontSize));
21476  	 	 	 // set margins and X position
21477  	 	 	 if (($this->page == $current_page) AND ($this->current_column == $current_column)) {
21478  	 	 	 	 $this->lMargin = $lmargin;
21479  	 	 	 	 $this->rMargin = $rmargin;
21480  	 	 	 } else {
21481  	 	 	 	 if ($this->current_column != $current_column) {
21482  	 	 	 	 	 if ($this->rtl) {
21483  	 	 	 	 	 	 $x_start = $this->w - $this->columns[$this->current_column]['x'];
21484  	 	 	 	 	 } else {
21485  	 	 	 	 	 	 $x_start = $this->columns[$this->current_column]['x'];
21486  	 	 	 	 	 }
21487  	 	 	 	 }
21488  	 	 	 	 $lmargin = $this->lMargin;
21489  	 	 	 	 $rmargin = $this->rMargin;
21490  	 	 	 	 $current_page = $this->page;
21491  	 	 	 	 $current_column = $this->current_column;
21492  	 	 	 }
21493  	 	 	 $this->SetX($x_start);
21494  	 	 	 $indent = ($spacer * $outline['l']);
21495  	 	 	 if ($this->rtl) {
21496  	 	 	 	 $this->x -= $indent;
21497  	 	 	 	 $this->rMargin = $this->w - $this->x;
21498  	 	 	 } else {
21499  	 	 	 	 $this->x += $indent;
21500  	 	 	 	 $this->lMargin = $this->x;
21501  	 	 	 }
21502  	 	 	 $link = $this->AddLink();
21503  	 	 	 $this->SetLink($link, $outline['y'], $outline['p']);
21504  	 	 	 // write the text
21505  	 	 	 if ($this->rtl) {
21506  	 	 	 	 $txt = ' '.$outline['t'];
21507  	 	 	 } else {
21508  	 	 	 	 $txt = $outline['t'].' ';
21509  	 	 	 }
21510  	 	 	 $this->Write(0, $txt, $link, false, $aligntext, false, 0, false, false, 0, $numwidth, '');
21511  	 	 	 if ($this->rtl) {
21512  	 	 	 	 $tw = $this->x - $this->lMargin;
21513  	 	 	 } else {
21514  	 	 	 	 $tw = $this->w - $this->rMargin - $this->x;
21515  	 	 	 }
21516  	 	 	 $this->SetFont($numbersfont, $fontstyle, $fontsize);
21517  	 	 	 if (TCPDF_STATIC::empty_string($page)) {
21518  	 	 	 	 $pagenum = $outline['p'];
21519  	 	 	 } else {
21520  	 	 	 	 // placemark to be replaced with the correct number
21521  	 	 	 	 $pagenum = '{#'.($outline['p']).'}';
21522  	 	 	 	 if ($this->isUnicodeFont()) {
21523  	 	 	 	 	 $pagenum = '{'.$pagenum.'}';
21524  	 	 	 	 }
21525  	 	 	 	 $maxpage = max($maxpage, $outline['p']);
21526  	 	 	 }
21527  	 	 	 $fw = ($tw - $this->GetStringWidth($pagenum.$filler));
21528  	 	 	 $wfiller = $this->GetStringWidth($filler);
21529  	 	 	 if ($wfiller > 0) {
21530  	 	 	 	 $numfills = floor($fw / $wfiller);
21531  	 	 	 } else {
21532  	 	 	 	 $numfills = 0;
21533  	 	 	 }
21534  	 	 	 if ($numfills > 0) {
21535  	 	 	 	 $rowfill = str_repeat($filler, $numfills);
21536  	 	 	 } else {
21537  	 	 	 	 $rowfill = '';
21538  	 	 	 }
21539  	 	 	 if ($this->rtl) {
21540  	 	 	 	 $pagenum = $pagenum.$gap.$rowfill;
21541  	 	 	 } else {
21542  	 	 	 	 $pagenum = $rowfill.$gap.$pagenum;
21543  	 	 	 }
21544  	 	 	 // write the number
21545  	 	 	 $this->Cell($tw, 0, $pagenum, 0, 1, $alignnum, 0, $link, 0);
21546  	 	 }
21547  	 	 $page_last = $this->getPage();
21548  	 	 $numpages = ($page_last - $page_first + 1);
21549  	 	 // account for booklet mode
21550  	 	 if ($this->booklet) {
21551  	 	 	 // check if a blank page is required before TOC
21552  	 	 	 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21553  	 	 	 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21554  	 	 	 if ($page_fill_start) {
21555  	 	 	 	 // add a page at the end (to be moved before TOC)
21556  	 	 	 	 $this->addPage();
21557  	 	 	 	 ++$page_last;
21558  	 	 	 	 ++$numpages;
21559  	 	 	 }
21560  	 	 	 if ($page_fill_end) {
21561  	 	 	 	 // add a page at the end
21562  	 	 	 	 $this->addPage();
21563  	 	 	 	 ++$page_last;
21564  	 	 	 	 ++$numpages;
21565  	 	 	 }
21566  	 	 }
21567  	 	 $maxpage = max($maxpage, $page_last);
21568  	 	 if (!TCPDF_STATIC::empty_string($page)) {
21569  	 	 	 for ($p = $page_first; $p <= $page_last; ++$p) {
21570  	 	 	 	 // get page data
21571  	 	 	 	 $temppage = $this->getPageBuffer($p);
21572  	 	 	 	 for ($n = 1; $n <= $maxpage; ++$n) {
21573  	 	 	 	 	 // update page numbers
21574  	 	 	 	 	 $a = '{#'.$n.'}';
21575  	 	 	 	 	 // get page number aliases
21576  	 	 	 	 	 $pnalias = $this->getInternalPageNumberAliases($a);
21577  	 	 	 	 	 // calculate replacement number
21578  	 	 	 	 	 if (($n >= $page) AND ($n <= $this->numpages)) {
21579  	 	 	 	 	 	 $np = $n + $numpages;
21580  	 	 	 	 	 } else {
21581  	 	 	 	 	 	 $np = $n;
21582  	 	 	 	 	 }
21583  	 	 	 	 	 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21584  	 	 	 	 	 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21585  	 	 	 	 	 // replace aliases with numbers
21586  	 	 	 	 	 foreach ($pnalias['u'] as $u) {
21587  	 	 	 	 	 	 $sfill = str_repeat($filler, max(0, (strlen($u) - strlen($nu.' '))));
21588  	 	 	 	 	 	 if ($this->rtl) {
21589  	 	 	 	 	 	 	 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21590  	 	 	 	 	 	 } else {
21591  	 	 	 	 	 	 	 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21592  	 	 	 	 	 	 }
21593  	 	 	 	 	 	 $temppage = str_replace($u, $nr, $temppage);
21594  	 	 	 	 	 }
21595  	 	 	 	 	 foreach ($pnalias['a'] as $a) {
21596  	 	 	 	 	 	 $sfill = str_repeat($filler, max(0, (strlen($a) - strlen($na.' '))));
21597  	 	 	 	 	 	 if ($this->rtl) {
21598  	 	 	 	 	 	 	 $nr = $na.' '.$sfill;
21599  	 	 	 	 	 	 } else {
21600  	 	 	 	 	 	 	 $nr = $sfill.' '.$na;
21601  	 	 	 	 	 	 }
21602  	 	 	 	 	 	 $temppage = str_replace($a, $nr, $temppage);
21603  	 	 	 	 	 }
21604  	 	 	 	 }
21605  	 	 	 	 // save changes
21606  	 	 	 	 $this->setPageBuffer($p, $temppage);
21607  	 	 	 }
21608  	 	 	 // move pages
21609  	 	 	 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21610  	 	 	 if ($page_fill_start) {
21611  	 	 	 	 $this->movePage($page_last, $page_first);
21612  	 	 	 }
21613  	 	 	 for ($i = 0; $i < $numpages; ++$i) {
21614  	 	 	 	 $this->movePage($page_last, $page);
21615  	 	 	 }
21616  	 	 }
21617  	 }
21618  
21619  	 /**
21620  	  * Output a Table Of Content Index (TOC) using HTML templates.
21621  	  * This method must be called after all Bookmarks were set.
21622  	  * Before calling this method you have to open the page using the addTOCPage() method.
21623  	  * After calling this method you have to call endTOCPage() to close the TOC page.
21624  	  * @param $page (int) page number where this TOC should be inserted (leave empty for current page).
21625  	  * @param $toc_name (string) name to use for TOC bookmark.
21626  	  * @param $templates (array) array of html templates. Use: "#TOC_DESCRIPTION#" for bookmark title, "#TOC_PAGE_NUMBER#" for page number.
21627  	  * @param $correct_align (boolean) if true correct the number alignment (numbers must be in monospaced font like courier and right aligned on LTR, or left aligned on RTL)
21628  	  * @param $style (string) Font style for title: B = Bold, I = Italic, BI = Bold + Italic.
21629  	  * @param $color (array) RGB color array for title (values from 0 to 255).
21630  	  * @public
21631  	  * @author Nicola Asuni
21632  	  * @since 5.0.001 (2010-05-06)
21633  	  * @see addTOCPage(), endTOCPage(), addTOC()
21634  	  */
21635  	public function addHTMLTOC($page='', $toc_name='TOC', $templates=array(), $correct_align=true, $style='', $color=array(0,0,0)) {
21636  	 	 $filler = ' ';
21637  	 	 $prev_htmlLinkColorArray = $this->htmlLinkColorArray;
21638  	 	 $prev_htmlLinkFontStyle = $this->htmlLinkFontStyle;
21639  	 	 // set new style for link
21640  	 	 $this->htmlLinkColorArray = array();
21641  	 	 $this->htmlLinkFontStyle = '';
21642  	 	 $page_first = $this->getPage();
21643  	 	 $page_fill_start = false;
21644  	 	 $page_fill_end = false;
21645  	 	 // get the font type used for numbers in each template
21646  	 	 $current_font = $this->FontFamily;
21647  	 	 foreach ($templates as $level => $html) {
21648  	 	 	 $dom = $this->getHtmlDomArray($html);
21649  	 	 	 foreach ($dom as $key => $value) {
21650  	 	 	 	 if ($value['value'] == '#TOC_PAGE_NUMBER#') {
21651  	 	 	 	 	 $this->SetFont($dom[($key - 1)]['fontname']);
21652  	 	 	 	 	 $templates['F'.$level] = $this->isUnicodeFont();
21653  	 	 	 	 }
21654  	 	 	 }
21655  	 	 }
21656  	 	 $this->SetFont($current_font);
21657  	 	 $maxpage = 0; //used for pages on attached documents
21658  	 	 foreach ($this->outlines as $key => $outline) {
21659  	 	 	 // get HTML template
21660  	 	 	 $row = $templates[$outline['l']];
21661  	 	 	 if (TCPDF_STATIC::empty_string($page)) {
21662  	 	 	 	 $pagenum = $outline['p'];
21663  	 	 	 } else {
21664  	 	 	 	 // placemark to be replaced with the correct number
21665  	 	 	 	 $pagenum = '{#'.($outline['p']).'}';
21666  	 	 	 	 if (isset($templates['F'.$outline['l']]) && $templates['F'.$outline['l']]) {
21667  	 	 	 	 	 $pagenum = '{'.$pagenum.'}';
21668  	 	 	 	 }
21669  	 	 	 	 $maxpage = max($maxpage, $outline['p']);
21670  	 	 	 }
21671  	 	 	 // replace templates with current values
21672  	 	 	 $row = str_replace('#TOC_DESCRIPTION#', $outline['t'], $row);
21673  	 	 	 $row = str_replace('#TOC_PAGE_NUMBER#', $pagenum, $row);
21674  	 	 	 // add link to page
21675  	 	 	 $row = '<a href="#'.$outline['p'].','.$outline['y'].'">'.$row.'</a>';
21676  	 	 	 // write bookmark entry
21677  	 	 	 $this->writeHTML($row, false, false, true, false, '');
21678  	 	 }
21679  	 	 // restore link styles
21680  	 	 $this->htmlLinkColorArray = $prev_htmlLinkColorArray;
21681  	 	 $this->htmlLinkFontStyle = $prev_htmlLinkFontStyle;
21682  	 	 // move TOC page and replace numbers
21683  	 	 $page_last = $this->getPage();
21684  	 	 $numpages = ($page_last - $page_first + 1);
21685  	 	 // account for booklet mode
21686  	 	 if ($this->booklet) {
21687  	 	 	 // check if a blank page is required before TOC
21688  	 	 	 $page_fill_start = ((($page_first % 2) == 0) XOR (($page % 2) == 0));
21689  	 	 	 $page_fill_end = (!((($numpages % 2) == 0) XOR ($page_fill_start)));
21690  	 	 	 if ($page_fill_start) {
21691  	 	 	 	 // add a page at the end (to be moved before TOC)
21692  	 	 	 	 $this->addPage();
21693  	 	 	 	 ++$page_last;
21694  	 	 	 	 ++$numpages;
21695  	 	 	 }
21696  	 	 	 if ($page_fill_end) {
21697  	 	 	 	 // add a page at the end
21698  	 	 	 	 $this->addPage();
21699  	 	 	 	 ++$page_last;
21700  	 	 	 	 ++$numpages;
21701  	 	 	 }
21702  	 	 }
21703  	 	 $maxpage = max($maxpage, $page_last);
21704  	 	 if (!TCPDF_STATIC::empty_string($page)) {
21705  	 	 	 for ($p = $page_first; $p <= $page_last; ++$p) {
21706  	 	 	 	 // get page data
21707  	 	 	 	 $temppage = $this->getPageBuffer($p);
21708  	 	 	 	 for ($n = 1; $n <= $maxpage; ++$n) {
21709  	 	 	 	 	 // update page numbers
21710  	 	 	 	 	 $a = '{#'.$n.'}';
21711  	 	 	 	 	 // get page number aliases
21712  	 	 	 	 	 $pnalias = $this->getInternalPageNumberAliases($a);
21713  	 	 	 	 	 // calculate replacement number
21714  	 	 	 	 	 if ($n >= $page) {
21715  	 	 	 	 	 	 $np = $n + $numpages;
21716  	 	 	 	 	 } else {
21717  	 	 	 	 	 	 $np = $n;
21718  	 	 	 	 	 }
21719  	 	 	 	 	 $na = TCPDF_STATIC::formatTOCPageNumber(($this->starting_page_number + $np - 1));
21720  	 	 	 	 	 $nu = TCPDF_FONTS::UTF8ToUTF16BE($na, false, $this->isunicode, $this->CurrentFont);
21721  	 	 	 	 	 // replace aliases with numbers
21722  	 	 	 	 	 foreach ($pnalias['u'] as $u) {
21723  	 	 	 	 	 	 if ($correct_align) {
21724  	 	 	 	 	 	 	 $sfill = str_repeat($filler, (strlen($u) - strlen($nu.' ')));
21725  	 	 	 	 	 	 	 if ($this->rtl) {
21726  	 	 	 	 	 	 	 	 $nr = $nu.TCPDF_FONTS::UTF8ToUTF16BE(' '.$sfill, false, $this->isunicode, $this->CurrentFont);
21727  	 	 	 	 	 	 	 } else {
21728  	 	 	 	 	 	 	 	 $nr = TCPDF_FONTS::UTF8ToUTF16BE($sfill.' ', false, $this->isunicode, $this->CurrentFont).$nu;
21729  	 	 	 	 	 	 	 }
21730  	 	 	 	 	 	 } else {
21731  	 	 	 	 	 	 	 $nr = $nu;
21732  	 	 	 	 	 	 }
21733  	 	 	 	 	 	 $temppage = str_replace($u, $nr, $temppage);
21734  	 	 	 	 	 }
21735  	 	 	 	 	 foreach ($pnalias['a'] as $a) {
21736  	 	 	 	 	 	 if ($correct_align) {
21737  	 	 	 	 	 	 	 $sfill = str_repeat($filler, (strlen($a) - strlen($na.' ')));
21738  	 	 	 	 	 	 	 if ($this->rtl) {
21739  	 	 	 	 	 	 	 	 $nr = $na.' '.$sfill;
21740  	 	 	 	 	 	 	 } else {
21741  	 	 	 	 	 	 	 	 $nr = $sfill.' '.$na;
21742  	 	 	 	 	 	 	 }
21743  	 	 	 	 	 	 } else {
21744  	 	 	 	 	 	 	 $nr = $na;
21745  	 	 	 	 	 	 }
21746  	 	 	 	 	 	 $temppage = str_replace($a, $nr, $temppage);
21747  	 	 	 	 	 }
21748  	 	 	 	 }
21749  	 	 	 	 // save changes
21750  	 	 	 	 $this->setPageBuffer($p, $temppage);
21751  	 	 	 }
21752  	 	 	 // move pages
21753  	 	 	 $this->Bookmark($toc_name, 0, 0, $page_first, $style, $color);
21754  	 	 	 if ($page_fill_start) {
21755  	 	 	 	 $this->movePage($page_last, $page_first);
21756  	 	 	 }
21757  	 	 	 for ($i = 0; $i < $numpages; ++$i) {
21758  	 	 	 	 $this->movePage($page_last, $page);
21759  	 	 	 }
21760  	 	 }
21761  	 }
21762  
21763  	 /**
21764  	  * Stores a copy of the current TCPDF object used for undo operation.
21765  	  * @public
21766  	  * @since 4.5.029 (2009-03-19)
21767  	  */
21768  	public function startTransaction() {
21769  	 	 if (isset($this->objcopy)) {
21770  	 	 	 // remove previous copy
21771  	 	 	 $this->commitTransaction();
21772  	 	 }
21773  	 	 // record current page number and Y position
21774  	 	 $this->start_transaction_page = $this->page;
21775  	 	 $this->start_transaction_y = $this->y;
21776  	 	 // clone current object
21777  	 	 $this->objcopy = TCPDF_STATIC::objclone($this);
21778  	 }
21779  
21780  	 /**
21781  	  * Delete the copy of the current TCPDF object used for undo operation.
21782  	  * @public
21783  	  * @since 4.5.029 (2009-03-19)
21784  	  */
21785  	public function commitTransaction() {
21786  	 	 if (isset($this->objcopy)) {
21787  	 	 	 $this->objcopy->_destroy(true, true);
21788  	 	 	 /* The unique file_id should not be used during cleanup again */
21789  	 	 	 $this->objcopy->file_id = NULL;
21790  	 	 	 unset($this->objcopy);
21791  	 	 }
21792  	 }
21793  
21794  	 /**
21795  	  * This method allows to undo the latest transaction by returning the latest saved TCPDF object with startTransaction().
21796  	  * @param $self (boolean) if true restores current class object to previous state without the need of reassignment via the returned value.
21797  	  * @return TCPDF object.
21798  	  * @public
21799  	  * @since 4.5.029 (2009-03-19)
21800  	  */
21801  	public function rollbackTransaction($self=false) {
21802  	 	 if (isset($this->objcopy)) {
21803  	 	 	 $objcopy = $this->objcopy;
21804  	 	 	 $this->_destroy(true, true);
21805  	 	 	 if ($self) {
21806  	 	 	 	 $objvars = get_object_vars($objcopy);
21807  	 	 	 	 foreach ($objvars as $key => $value) {
21808  	 	 	 	 	 $this->$key = $value;
21809  	 	 	 	 }
21810  	 	 	 	 $objcopy->_destroy(true, true);
21811  	 	 	 	 /* The unique file_id should not be used during cleanup again */
21812  	 	 	 	 $objcopy->file_id = NULL;
21813  	 	 	 	 unset($objcopy);
21814  	 	 	 	 return $this;
21815  	 	 	 }
21816  	 	 	 /* The unique file_id should not be used during cleanup again */
21817  	 	 	 $this->file_id = NULL;
21818  	 	 	 return $objcopy;
21819  	 	 }
21820  	 	 return $this;
21821  	 }
21822  
21823  	 // --- MULTI COLUMNS METHODS -----------------------
21824  
21825  	 /**
21826  	  * Set multiple columns of the same size
21827  	  * @param $numcols (int) number of columns (set to zero to disable columns mode)
21828  	  * @param $width (int) column width
21829  	  * @param $y (int) column starting Y position (leave empty for current Y position)
21830  	  * @public
21831  	  * @since 4.9.001 (2010-03-28)
21832  	  */
21833  	public function setEqualColumns($numcols=0, $width=0, $y='') {
21834  	 	 $this->columns = array();
21835  	 	 if ($numcols < 2) {
21836  	 	 	 $numcols = 0;
21837  	 	 	 $this->columns = array();
21838  	 	 } else {
21839  	 	 	 // maximum column width
21840  	 	 	 $maxwidth = ($this->w - $this->original_lMargin - $this->original_rMargin) / $numcols;
21841  	 	 	 if (($width == 0) OR ($width > $maxwidth)) {
21842  	 	 	 	 $width = $maxwidth;
21843  	 	 	 }
21844  	 	 	 if (TCPDF_STATIC::empty_string($y)) {
21845  	 	 	 	 $y = $this->y;
21846  	 	 	 }
21847  	 	 	 // space between columns
21848  	 	 	 $space = (($this->w - $this->original_lMargin - $this->original_rMargin - ($numcols * $width)) / ($numcols - 1));
21849  	 	 	 // fill the columns array (with, space, starting Y position)
21850  	 	 	 for ($i = 0; $i < $numcols; ++$i) {
21851  	 	 	 	 $this->columns[$i] = array('w' => $width, 's' => $space, 'y' => $y);
21852  	 	 	 }
21853  	 	 }
21854  	 	 $this->num_columns = $numcols;
21855  	 	 $this->current_column = 0;
21856  	 	 $this->column_start_page = $this->page;
21857  	 	 $this->selectColumn(0);
21858  	 }
21859  
21860  	 /**
21861  	  * Remove columns and reset page margins.
21862  	  * @public
21863  	  * @since 5.9.072 (2011-04-26)
21864  	  */
21865  	public function resetColumns() {
21866  	 	 $this->lMargin = $this->original_lMargin;
21867  	 	 $this->rMargin = $this->original_rMargin;
21868  	 	 $this->setEqualColumns();
21869  	 }
21870  
21871  	 /**
21872  	  * Set columns array.
21873  	  * Each column is represented by an array of arrays with the following keys: (w = width, s = space between columns, y = column top position).
21874  	  * @param $columns (array)
21875  	  * @public
21876  	  * @since 4.9.001 (2010-03-28)
21877  	  */
21878  	public function setColumnsArray($columns) {
21879  	 	 $this->columns = $columns;
21880  	 	 $this->num_columns = count($columns);
21881  	 	 $this->current_column = 0;
21882  	 	 $this->column_start_page = $this->page;
21883  	 	 $this->selectColumn(0);
21884  	 }
21885  
21886  	 /**
21887  	  * Set position at a given column
21888  	  * @param $col (int) column number (from 0 to getNumberOfColumns()-1); empty string = current column.
21889  	  * @public
21890  	  * @since 4.9.001 (2010-03-28)
21891  	  */
21892  	public function selectColumn($col='') {
21893  	 	 if (is_string($col)) {
21894  	 	 	 $col = $this->current_column;
21895  	 	 } elseif ($col >= $this->num_columns) {
21896  	 	 	 $col = 0;
21897  	 	 }
21898  	 	 $xshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
21899  	 	 $enable_thead = false;
21900  	 	 if ($this->num_columns > 1) {
21901  	 	 	 if ($col != $this->current_column) {
21902  	 	 	 	 // move Y pointer at the top of the column
21903  	 	 	 	 if ($this->column_start_page == $this->page) {
21904  	 	 	 	 	 $this->y = $this->columns[$col]['y'];
21905  	 	 	 	 } else {
21906  	 	 	 	 	 $this->y = $this->tMargin;
21907  	 	 	 	 }
21908  	 	 	 	 // Avoid to write table headers more than once
21909  	 	 	 	 if (($this->page > $this->maxselcol['page']) OR (($this->page == $this->maxselcol['page']) AND ($col > $this->maxselcol['column']))) {
21910  	 	 	 	 	 $enable_thead = true;
21911  	 	 	 	 	 $this->maxselcol['page'] = $this->page;
21912  	 	 	 	 	 $this->maxselcol['column'] = $col;
21913  	 	 	 	 }
21914  	 	 	 }
21915  	 	 	 $xshift = $this->colxshift;
21916  	 	 	 // set X position of the current column by case
21917  	 	 	 $listindent = ($this->listindentlevel * $this->listindent);
21918  	 	 	 // calculate column X position
21919  	 	 	 $colpos = 0;
21920  	 	 	 for ($i = 0; $i < $col; ++$i) {
21921  	 	 	 	 $colpos += ($this->columns[$i]['w'] + $this->columns[$i]['s']);
21922  	 	 	 }
21923  	 	 	 if ($this->rtl) {
21924  	 	 	 	 $x = $this->w - $this->original_rMargin - $colpos;
21925  	 	 	 	 $this->rMargin = ($this->w - $x + $listindent);
21926  	 	 	 	 $this->lMargin = ($x - $this->columns[$col]['w']);
21927  	 	 	 	 $this->x = $x - $listindent;
21928  	 	 	 } else {
21929  	 	 	 	 $x = $this->original_lMargin + $colpos;
21930  	 	 	 	 $this->lMargin = ($x + $listindent);
21931  	 	 	 	 $this->rMargin = ($this->w - $x - $this->columns[$col]['w']);
21932  	 	 	 	 $this->x = $x + $listindent;
21933  	 	 	 }
21934  	 	 	 $this->columns[$col]['x'] = $x;
21935  	 	 }
21936  	 	 $this->current_column = $col;
21937  	 	 // fix for HTML mode
21938  	 	 $this->newline = true;
21939  	 	 // print HTML table header (if any)
21940  	 	 if ((!TCPDF_STATIC::empty_string($this->thead)) AND (!$this->inthead)) {
21941  	 	 	 if ($enable_thead) {
21942  	 	 	 	 // print table header
21943  	 	 	 	 $this->writeHTML($this->thead, false, false, false, false, '');
21944  	 	 	 	 $this->y += $xshift['s']['V'];
21945  	 	 	 	 // store end of header position
21946  	 	 	 	 if (!isset($this->columns[$col]['th'])) {
21947  	 	 	 	 	 $this->columns[$col]['th'] = array();
21948  	 	 	 	 }
21949  	 	 	 	 $this->columns[$col]['th']['\''.$this->page.'\''] = $this->y;
21950  	 	 	 	 $this->lasth = 0;
21951  	 	 	 } elseif (isset($this->columns[$col]['th']['\''.$this->page.'\''])) {
21952  	 	 	 	 $this->y = $this->columns[$col]['th']['\''.$this->page.'\''];
21953  	 	 	 }
21954  	 	 }
21955  	 	 // account for an html table cell over multiple columns
21956  	 	 if ($this->rtl) {
21957  	 	 	 $this->rMargin += $xshift['x'];
21958  	 	 	 $this->x -= ($xshift['x'] + $xshift['p']['R']);
21959  	 	 } else {
21960  	 	 	 $this->lMargin += $xshift['x'];
21961  	 	 	 $this->x += $xshift['x'] + $xshift['p']['L'];
21962  	 	 }
21963  	 }
21964  
21965  	 /**
21966  	  * Return the current column number
21967  	  * @return int current column number
21968  	  * @public
21969  	  * @since 5.5.011 (2010-07-08)
21970  	  */
21971  	public function getColumn() {
21972  	 	 return $this->current_column;
21973  	 }
21974  
21975  	 /**
21976  	  * Return the current number of columns.
21977  	  * @return int number of columns
21978  	  * @public
21979  	  * @since 5.8.018 (2010-08-25)
21980  	  */
21981  	public function getNumberOfColumns() {
21982  	 	 return $this->num_columns;
21983  	 }
21984  
21985  	 /**
21986  	  * Set Text rendering mode.
21987  	  * @param $stroke (int) outline size in user units (0 = disable).
21988  	  * @param $fill (boolean) if true fills the text (default).
21989  	  * @param $clip (boolean) if true activate clipping mode
21990  	  * @public
21991  	  * @since 4.9.008 (2009-04-02)
21992  	  */
21993  	public function setTextRenderingMode($stroke=0, $fill=true, $clip=false) {
21994  	 	 // Ref.: PDF 32000-1:2008 - 9.3.6 Text Rendering Mode
21995  	 	 // convert text rendering parameters
21996  	 	 if ($stroke < 0) {
21997  	 	 	 $stroke = 0;
21998  	 	 }
21999  	 	 if ($fill === true) {
22000  	 	 	 if ($stroke > 0) {
22001  	 	 	 	 if ($clip === true) {
22002  	 	 	 	 	 // Fill, then stroke text and add to path for clipping
22003  	 	 	 	 	 $textrendermode = 6;
22004  	 	 	 	 } else {
22005  	 	 	 	 	 // Fill, then stroke text
22006  	 	 	 	 	 $textrendermode = 2;
22007  	 	 	 	 }
22008  	 	 	 	 $textstrokewidth = $stroke;
22009  	 	 	 } else {
22010  	 	 	 	 if ($clip === true) {
22011  	 	 	 	 	 // Fill text and add to path for clipping
22012  	 	 	 	 	 $textrendermode = 4;
22013  	 	 	 	 } else {
22014  	 	 	 	 	 // Fill text
22015  	 	 	 	 	 $textrendermode = 0;
22016  	 	 	 	 }
22017  	 	 	 }
22018  	 	 } else {
22019  	 	 	 if ($stroke > 0) {
22020  	 	 	 	 if ($clip === true) {
22021  	 	 	 	 	 // Stroke text and add to path for clipping
22022  	 	 	 	 	 $textrendermode = 5;
22023  	 	 	 	 } else {
22024  	 	 	 	 	 // Stroke text
22025  	 	 	 	 	 $textrendermode = 1;
22026  	 	 	 	 }
22027  	 	 	 	 $textstrokewidth = $stroke;
22028  	 	 	 } else {
22029  	 	 	 	 if ($clip === true) {
22030  	 	 	 	 	 // Add text to path for clipping
22031  	 	 	 	 	 $textrendermode = 7;
22032  	 	 	 	 } else {
22033  	 	 	 	 	 // Neither fill nor stroke text (invisible)
22034  	 	 	 	 	 $textrendermode = 3;
22035  	 	 	 	 }
22036  	 	 	 }
22037  	 	 }
22038  	 	 $this->textrendermode = $textrendermode;
22039  	 	 $this->textstrokewidth = $stroke;
22040  	 }
22041  
22042  	 /**
22043  	  * Set parameters for drop shadow effect for text.
22044  	  * @param $params (array) Array of parameters: enabled (boolean) set to true to enable shadow; depth_w (float) shadow width in user units; depth_h (float) shadow height in user units; color (array) shadow color or false to use the stroke color; opacity (float) Alpha value: real value from 0 (transparent) to 1 (opaque); blend_mode (string) blend mode, one of the following: Normal, Multiply, Screen, Overlay, Darken, Lighten, ColorDodge, ColorBurn, HardLight, SoftLight, Difference, Exclusion, Hue, Saturation, Color, Luminosity.
22045  	  * @since 5.9.174 (2012-07-25)
22046  	  * @public
22047  	 */
22048  	public function setTextShadow($params=array('enabled'=>false, 'depth_w'=>0, 'depth_h'=>0, 'color'=>false, 'opacity'=>1, 'blend_mode'=>'Normal')) {
22049  	 	 if (isset($params['enabled'])) {
22050  	 	 	 $this->txtshadow['enabled'] = $params['enabled']?true:false;
22051  	 	 } else {
22052  	 	 	 $this->txtshadow['enabled'] = false;
22053  	 	 }
22054  	 	 if (isset($params['depth_w'])) {
22055  	 	 	 $this->txtshadow['depth_w'] = floatval($params['depth_w']);
22056  	 	 } else {
22057  	 	 	 $this->txtshadow['depth_w'] = 0;
22058  	 	 }
22059  	 	 if (isset($params['depth_h'])) {
22060  	 	 	 $this->txtshadow['depth_h'] = floatval($params['depth_h']);
22061  	 	 } else {
22062  	 	 	 $this->txtshadow['depth_h'] = 0;
22063  	 	 }
22064  	 	 if (isset($params['color']) AND ($params['color'] !== false) AND is_array($params['color'])) {
22065  	 	 	 $this->txtshadow['color'] = $params['color'];
22066  	 	 } else {
22067  	 	 	 $this->txtshadow['color'] = $this->strokecolor;
22068  	 	 }
22069  	 	 if (isset($params['opacity'])) {
22070  	 	 	 $this->txtshadow['opacity'] = min(1, max(0, floatval($params['opacity'])));
22071  	 	 } else {
22072  	 	 	 $this->txtshadow['opacity'] = 1;
22073  	 	 }
22074  	 	 if (isset($params['blend_mode']) AND in_array($params['blend_mode'], array('Normal', 'Multiply', 'Screen', 'Overlay', 'Darken', 'Lighten', 'ColorDodge', 'ColorBurn', 'HardLight', 'SoftLight', 'Difference', 'Exclusion', 'Hue', 'Saturation', 'Color', 'Luminosity'))) {
22075  	 	 	 $this->txtshadow['blend_mode'] = $params['blend_mode'];
22076  	 	 } else {
22077  	 	 	 $this->txtshadow['blend_mode'] = 'Normal';
22078  	 	 }
22079  	 	 if ((($this->txtshadow['depth_w'] == 0) AND ($this->txtshadow['depth_h'] == 0)) OR ($this->txtshadow['opacity'] == 0)) {
22080  	 	 	 $this->txtshadow['enabled'] = false;
22081  	 	 }
22082  	 }
22083  
22084  	 /**
22085  	  * Return the text shadow parameters array.
22086  	  * @return Array of parameters.
22087  	  * @since 5.9.174 (2012-07-25)
22088  	  * @public
22089  	  */
22090  	public function getTextShadow() {
22091  	 	 return $this->txtshadow;
22092  	 }
22093  
22094  	 /**
22095  	  * Returns an array of chars containing soft hyphens.
22096  	  * @param $word (array) array of chars
22097  	  * @param $patterns (array) Array of hypenation patterns.
22098  	  * @param $dictionary (array) Array of words to be returned without applying the hyphenation algorithm.
22099  	  * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22100  	  * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22101  	  * @param $charmin (int) Minimum word length to apply the hyphenation algorithm.
22102  	  * @param $charmax (int) Maximum length of broken piece of word.
22103  	  * @return array text with soft hyphens
22104  	  * @author Nicola Asuni
22105  	  * @since 4.9.012 (2010-04-12)
22106  	  * @protected
22107  	  */
22108  	protected function hyphenateWord($word, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22109  	 	 $hyphenword = array(); // hyphens positions
22110  	 	 $numchars = count($word);
22111  	 	 if ($numchars <= $charmin) {
22112  	 	 	 return $word;
22113  	 	 }
22114  	 	 $word_string = TCPDF_FONTS::UTF8ArrSubString($word, '', '', $this->isunicode);
22115  	 	 // some words will be returned as-is
22116  	 	 $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})(\]?)$/';
22117  	 	 if (preg_match($pattern, $word_string) > 0) {
22118  	 	 	 // email
22119  	 	 	 return $word;
22120  	 	 }
22121  	 	 $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})(\]?)$/';
22122  	 	 if (preg_match($pattern, $word_string) > 0) {
22123  	 	 	 // URL
22124  	 	 	 return $word;
22125  	 	 }
22126  	 	 if (isset($dictionary[$word_string])) {
22127  	 	 	 return TCPDF_FONTS::UTF8StringToArray($dictionary[$word_string], $this->isunicode, $this->CurrentFont);
22128  	 	 }
22129  	 	 // surround word with '_' characters
22130  	 	 $tmpword = array_merge(array(46), $word, array(46));
22131  	 	 $tmpnumchars = $numchars + 2;
22132  	 	 $maxpos = $tmpnumchars - 1;
22133  	 	 for ($pos = 0; $pos < $maxpos; ++$pos) {
22134  	 	 	 $imax = min(($tmpnumchars - $pos), $charmax);
22135  	 	 	 for ($i = 1; $i <= $imax; ++$i) {
22136  	 	 	 	 $subword = strtolower(TCPDF_FONTS::UTF8ArrSubString($tmpword, $pos, ($pos + $i), $this->isunicode));
22137  	 	 	 	 if (isset($patterns[$subword])) {
22138  	 	 	 	 	 $pattern = TCPDF_FONTS::UTF8StringToArray($patterns[$subword], $this->isunicode, $this->CurrentFont);
22139  	 	 	 	 	 $pattern_length = count($pattern);
22140  	 	 	 	 	 $digits = 1;
22141  	 	 	 	 	 for ($j = 0; $j < $pattern_length; ++$j) {
22142  	 	 	 	 	 	 // check if $pattern[$j] is a number = hyphenation level (only numbers from 1 to 5 are valid)
22143  	 	 	 	 	 	 if (($pattern[$j] >= 48) AND ($pattern[$j] <= 57)) {
22144  	 	 	 	 	 	 	 if ($j == 0) {
22145  	 	 	 	 	 	 	 	 $zero = $pos - 1;
22146  	 	 	 	 	 	 	 } else {
22147  	 	 	 	 	 	 	 	 $zero = $pos + $j - $digits;
22148  	 	 	 	 	 	 	 }
22149  	 	 	 	 	 	 	 // get hyphenation level
22150  	 	 	 	 	 	 	 $level = ($pattern[$j] - 48);
22151  	 	 	 	 	 	 	 // if two levels from two different patterns match at the same point, the higher one is selected.
22152  	 	 	 	 	 	 	 if (!isset($hyphenword[$zero]) OR ($hyphenword[$zero] < $level)) {
22153  	 	 	 	 	 	 	 	 $hyphenword[$zero] = $level;
22154  	 	 	 	 	 	 	 }
22155  	 	 	 	 	 	 	 ++$digits;
22156  	 	 	 	 	 	 }
22157  	 	 	 	 	 }
22158  	 	 	 	 }
22159  	 	 	 }
22160  	 	 }
22161  	 	 $inserted = 0;
22162  	 	 $maxpos = $numchars - $rightmin;
22163  	 	 for ($i = $leftmin; $i <= $maxpos; ++$i) {
22164  	 	 	 // only odd levels indicate allowed hyphenation points
22165  	 	 	 if (isset($hyphenword[$i]) AND (($hyphenword[$i] % 2) != 0)) {
22166  	 	 	 	 // 173 = soft hyphen character
22167  	 	 	 	 array_splice($word, $i + $inserted, 0, 173);
22168  	 	 	 	 ++$inserted;
22169  	 	 	 }
22170  	 	 }
22171  	 	 return $word;
22172  	 }
22173  
22174  	 /**
22175  	  * Returns text with soft hyphens.
22176  	  * @param $text (string) text to process
22177  	  * @param $patterns (mixed) 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/
22178  	  * @param $dictionary (array) Array of words to be returned without applying the hyphenation algorithm.
22179  	  * @param $leftmin (int) Minimum number of character to leave on the left of the word without applying the hyphens.
22180  	  * @param $rightmin (int) Minimum number of character to leave on the right of the word without applying the hyphens.
22181  	  * @param $charmin (int) Minimum word length to apply the hyphenation algorithm.
22182  	  * @param $charmax (int) Maximum length of broken piece of word.
22183  	  * @return array text with soft hyphens
22184  	  * @author Nicola Asuni
22185  	  * @since 4.9.012 (2010-04-12)
22186  	  * @public
22187  	  */
22188  	public function hyphenateText($text, $patterns, $dictionary=array(), $leftmin=1, $rightmin=2, $charmin=1, $charmax=8) {
22189  	 	 $text = $this->unhtmlentities($text);
22190  	 	 $word = array(); // last word
22191  	 	 $txtarr = array(); // text to be returned
22192  	 	 $intag = false; // true if we are inside an HTML tag
22193  	 	 $skip = false; // true to skip hyphenation
22194  	 	 if (!is_array($patterns)) {
22195  	 	 	 $patterns = TCPDF_STATIC::getHyphenPatternsFromTEX($patterns);
22196  	 	 }
22197  	 	 // get array of characters
22198  	 	 $unichars = TCPDF_FONTS::UTF8StringToArray($text, $this->isunicode, $this->CurrentFont);
22199  	 	 // for each char
22200  	 	 foreach ($unichars as $char) {
22201  	 	 	 if ((!$intag) AND (!$skip) AND TCPDF_FONT_DATA::$uni_type[$char] == 'L') {
22202  	 	 	 	 // letter character
22203  	 	 	 	 $word[] = $char;
22204  	 	 	 } else {
22205  	 	 	 	 // other type of character
22206  	 	 	 	 if (!TCPDF_STATIC::empty_string($word)) {
22207  	 	 	 	 	 // hypenate the word
22208  	 	 	 	 	 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22209  	 	 	 	 	 $word = array();
22210  	 	 	 	 }
22211  	 	 	 	 $txtarr[] = $char;
22212  	 	 	 	 if (chr($char) == '<') {
22213  	 	 	 	 	 // we are inside an HTML tag
22214  	 	 	 	 	 $intag = true;
22215  	 	 	 	 } elseif ($intag AND (chr($char) == '>')) {
22216  	 	 	 	 	 // end of HTML tag
22217  	 	 	 	 	 $intag = false;
22218  	 	 	 	 	 // check for style tag
22219  	 	 	 	 	 $expected = array(115, 116, 121, 108, 101); // = 'style'
22220  	 	 	 	 	 $current = array_slice($txtarr, -6, 5); // last 5 chars
22221  	 	 	 	 	 $compare = array_diff($expected, $current);
22222  	 	 	 	 	 if (empty($compare)) {
22223  	 	 	 	 	 	 // check if it is a closing tag
22224  	 	 	 	 	 	 $expected = array(47); // = '/'
22225  	 	 	 	 	 	 $current = array_slice($txtarr, -7, 1);
22226  	 	 	 	 	 	 $compare = array_diff($expected, $current);
22227  	 	 	 	 	 	 if (empty($compare)) {
22228  	 	 	 	 	 	 	 // closing style tag
22229  	 	 	 	 	 	 	 $skip = false;
22230  	 	 	 	 	 	 } else {
22231  	 	 	 	 	 	 	 // opening style tag
22232  	 	 	 	 	 	 	 $skip = true;
22233  	 	 	 	 	 	 }
22234  	 	 	 	 	 }
22235  	 	 	 	 }
22236  	 	 	 }
22237  	 	 }
22238  	 	 if (!TCPDF_STATIC::empty_string($word)) {
22239  	 	 	 // hypenate the word
22240  	 	 	 $txtarr = array_merge($txtarr, $this->hyphenateWord($word, $patterns, $dictionary, $leftmin, $rightmin, $charmin, $charmax));
22241  	 	 }
22242  	 	 // convert char array to string and return
22243  	 	 return TCPDF_FONTS::UTF8ArrSubString($txtarr, '', '', $this->isunicode);
22244  	 }
22245  
22246  	 /**
22247  	  * Enable/disable rasterization of vector images using ImageMagick library.
22248  	  * @param $mode (boolean) if true enable rasterization, false otherwise.
22249  	  * @public
22250  	  * @since 5.0.000 (2010-04-27)
22251  	  */
22252  	public function setRasterizeVectorImages($mode) {
22253  	 	 $this->rasterize_vector_images = $mode;
22254  	 }
22255  
22256  	 /**
22257  	  * Enable or disable default option for font subsetting.
22258  	  * @param $enable (boolean) if true enable font subsetting by default.
22259  	  * @author Nicola Asuni
22260  	  * @public
22261  	  * @since 5.3.002 (2010-06-07)
22262  	  */
22263  	public function setFontSubsetting($enable=true) {
22264  	 	 if ($this->pdfa_mode) {
22265  	 	 	 $this->font_subsetting = false;
22266  	 	 } else {
22267  	 	 	 $this->font_subsetting = $enable ? true : false;
22268  	 	 }
22269  	 }
22270  
22271  	 /**
22272  	  * Return the default option for font subsetting.
22273  	  * @return boolean default font subsetting state.
22274  	  * @author Nicola Asuni
22275  	  * @public
22276  	  * @since 5.3.002 (2010-06-07)
22277  	  */
22278  	public function getFontSubsetting() {
22279  	 	 return $this->font_subsetting;
22280  	 }
22281  
22282  	 /**
22283  	  * Left trim the input string
22284  	  * @param $str (string) string to trim
22285  	  * @param $replace (string) string that replace spaces.
22286  	  * @return left trimmed string
22287  	  * @author Nicola Asuni
22288  	  * @public
22289  	  * @since 5.8.000 (2010-08-11)
22290  	  */
22291  	public function stringLeftTrim($str, $replace='') {
22292  	 	 return preg_replace('/^'.$this->re_space['p'].'+/'.$this->re_space['m'], $replace, $str);
22293  	 }
22294  
22295  	 /**
22296  	  * Right trim the input string
22297  	  * @param $str (string) string to trim
22298  	  * @param $replace (string) string that replace spaces.
22299  	  * @return right trimmed string
22300  	  * @author Nicola Asuni
22301  	  * @public
22302  	  * @since 5.8.000 (2010-08-11)
22303  	  */
22304  	public function stringRightTrim($str, $replace='') {
22305  	 	 return preg_replace('/'.$this->re_space['p'].'+$/'.$this->re_space['m'], $replace, $str);
22306  	 }
22307  
22308  	 /**
22309  	  * Trim the input string
22310  	  * @param $str (string) string to trim
22311  	  * @param $replace (string) string that replace spaces.
22312  	  * @return trimmed string
22313  	  * @author Nicola Asuni
22314  	  * @public
22315  	  * @since 5.8.000 (2010-08-11)
22316  	  */
22317  	public function stringTrim($str, $replace='') {
22318  	 	 $str = $this->stringLeftTrim($str, $replace);
22319  	 	 $str = $this->stringRightTrim($str, $replace);
22320  	 	 return $str;
22321  	 }
22322  
22323  	 /**
22324  	  * Return true if the current font is unicode type.
22325  	  * @return true for unicode font, false otherwise.
22326  	  * @author Nicola Asuni
22327  	  * @public
22328  	  * @since 5.8.002 (2010-08-14)
22329  	  */
22330  	public function isUnicodeFont() {
22331  	 	 return (($this->CurrentFont['type'] == 'TrueTypeUnicode') OR ($this->CurrentFont['type'] == 'cidfont0'));
22332  	 }
22333  
22334  	 /**
22335  	  * Return normalized font name
22336  	  * @param $fontfamily (string) property string containing font family names
22337  	  * @return string normalized font name
22338  	  * @author Nicola Asuni
22339  	  * @public
22340  	  * @since 5.8.004 (2010-08-17)
22341  	  */
22342  	public function getFontFamilyName($fontfamily) {
22343  	 	 // remove spaces and symbols
22344  	 	 $fontfamily = preg_replace('/[^a-z0-9_\,]/', '', strtolower($fontfamily));
22345  	 	 // extract all font names
22346  	 	 $fontslist = preg_split('/[,]/', $fontfamily);
22347  	 	 // find first valid font name
22348  	 	 foreach ($fontslist as $font) {
22349  	 	 	 // replace font variations
22350  	 	 	 $font = preg_replace('/regular$/', '', $font);
22351  	 	 	 $font = preg_replace('/italic$/', 'I', $font);
22352  	 	 	 $font = preg_replace('/oblique$/', 'I', $font);
22353  	 	 	 $font = preg_replace('/bold([I]?)$/', 'B\\1', $font);
22354  	 	 	 // replace common family names and core fonts
22355  	 	 	 $pattern = array();
22356  	 	 	 $replacement = array();
22357  	 	 	 $pattern[] = '/^serif|^cursive|^fantasy|^timesnewroman/';
22358  	 	 	 $replacement[] = 'times';
22359  	 	 	 $pattern[] = '/^sansserif/';
22360  	 	 	 $replacement[] = 'helvetica';
22361  	 	 	 $pattern[] = '/^monospace/';
22362  	 	 	 $replacement[] = 'courier';
22363  	 	 	 $font = preg_replace($pattern, $replacement, $font);
22364  	 	 	 if (in_array(strtolower($font), $this->fontlist) OR in_array($font, $this->fontkeys)) {
22365  	 	 	 	 return $font;
22366  	 	 	 }
22367  	 	 }
22368  	 	 // return current font as default
22369  	 	 return $this->CurrentFont['fontkey'];
22370  	 }
22371  
22372  	 /**
22373  	  * Start a new XObject Template.
22374  	  * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22375  	  * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22376  	  * Note: X,Y coordinates will be reset to 0,0.
22377  	  * @param $w (int) Template width in user units (empty string or zero = page width less margins).
22378  	  * @param $h (int) Template height in user units (empty string or zero = page height less margins).
22379  	  * @param $group (mixed) Set transparency group. Can be a boolean value or an array specifying optional parameters: 'CS' (solour space name), 'I' (boolean flag to indicate isolated group) and 'K' (boolean flag to indicate knockout group).
22380  	  * @return int the XObject Template ID in case of success or false in case of error.
22381  	  * @author Nicola Asuni
22382  	  * @public
22383  	  * @since 5.8.017 (2010-08-24)
22384  	  * @see endTemplate(), printTemplate()
22385  	  */
22386  	public function startTemplate($w=0, $h=0, $group=false) {
22387  	 	 if ($this->inxobj) {
22388  	 	 	 // we are already inside an XObject template
22389  	 	 	 return false;
22390  	 	 }
22391  	 	 $this->inxobj = true;
22392  	 	 ++$this->n;
22393  	 	 // XObject ID
22394  	 	 $this->xobjid = 'XT'.$this->n;
22395  	 	 // object ID
22396  	 	 $this->xobjects[$this->xobjid] = array('n' => $this->n);
22397  	 	 // store current graphic state
22398  	 	 $this->xobjects[$this->xobjid]['gvars'] = $this->getGraphicVars();
22399  	 	 // initialize data
22400  	 	 $this->xobjects[$this->xobjid]['intmrk'] = 0;
22401  	 	 $this->xobjects[$this->xobjid]['transfmrk'] = array();
22402  	 	 $this->xobjects[$this->xobjid]['outdata'] = '';
22403  	 	 $this->xobjects[$this->xobjid]['xobjects'] = array();
22404  	 	 $this->xobjects[$this->xobjid]['images'] = array();
22405  	 	 $this->xobjects[$this->xobjid]['fonts'] = array();
22406  	 	 $this->xobjects[$this->xobjid]['annotations'] = array();
22407  	 	 $this->xobjects[$this->xobjid]['extgstates'] = array();
22408  	 	 $this->xobjects[$this->xobjid]['gradients'] = array();
22409  	 	 $this->xobjects[$this->xobjid]['spot_colors'] = array();
22410  	 	 // set new environment
22411  	 	 $this->num_columns = 1;
22412  	 	 $this->current_column = 0;
22413  	 	 $this->SetAutoPageBreak(false);
22414  	 	 if (($w === '') OR ($w <= 0)) {
22415  	 	 	 $w = $this->w - $this->lMargin - $this->rMargin;
22416  	 	 }
22417  	 	 if (($h === '') OR ($h <= 0)) {
22418  	 	 	 $h = $this->h - $this->tMargin - $this->bMargin;
22419  	 	 }
22420  	 	 $this->xobjects[$this->xobjid]['x'] = 0;
22421  	 	 $this->xobjects[$this->xobjid]['y'] = 0;
22422  	 	 $this->xobjects[$this->xobjid]['w'] = $w;
22423  	 	 $this->xobjects[$this->xobjid]['h'] = $h;
22424  	 	 $this->w = $w;
22425  	 	 $this->h = $h;
22426  	 	 $this->wPt = $this->w * $this->k;
22427  	 	 $this->hPt = $this->h * $this->k;
22428  	 	 $this->fwPt = $this->wPt;
22429  	 	 $this->fhPt = $this->hPt;
22430  	 	 $this->x = 0;
22431  	 	 $this->y = 0;
22432  	 	 $this->lMargin = 0;
22433  	 	 $this->rMargin = 0;
22434  	 	 $this->tMargin = 0;
22435  	 	 $this->bMargin = 0;
22436  	 	 // set group mode
22437  	 	 $this->xobjects[$this->xobjid]['group'] = $group;
22438  	 	 return $this->xobjid;
22439  	 }
22440  
22441  	 /**
22442  	  * End the current XObject Template started with startTemplate() and restore the previous graphic state.
22443  	  * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22444  	  * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22445  	  * @return int the XObject Template ID in case of success or false in case of error.
22446  	  * @author Nicola Asuni
22447  	  * @public
22448  	  * @since 5.8.017 (2010-08-24)
22449  	  * @see startTemplate(), printTemplate()
22450  	  */
22451  	public function endTemplate() {
22452  	 	 if (!$this->inxobj) {
22453  	 	 	 // we are not inside a template
22454  	 	 	 return false;
22455  	 	 }
22456  	 	 $this->inxobj = false;
22457  	 	 // restore previous graphic state
22458  	 	 $this->setGraphicVars($this->xobjects[$this->xobjid]['gvars'], true);
22459  	 	 return $this->xobjid;
22460  	 }
22461  
22462  	 /**
22463  	  * Print an XObject Template.
22464  	  * You can print an XObject Template inside the currently opened Template.
22465  	  * An XObject Template is a PDF block that is a self-contained description of any sequence of graphics objects (including path objects, text objects, and sampled images).
22466  	  * An XObject Template may be painted multiple times, either on several pages or at several locations on the same page and produces the same results each time, subject only to the graphics state at the time it is invoked.
22467  	  * @param $id (string) The ID of XObject Template to print.
22468  	  * @param $x (int) X position in user units (empty string = current x position)
22469  	  * @param $y (int) Y position in user units (empty string = current y position)
22470  	  * @param $w (int) Width in user units (zero = remaining page width)
22471  	  * @param $h (int) Height in user units (zero = remaining page height)
22472  	  * @param $align (string) Indicates the alignment of the pointer next to template insertion relative to template 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>
22473  	  * @param $palign (string) Allows to center or align the template 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>
22474  	  * @param $fitonpage (boolean) If true the template is resized to not exceed page dimensions.
22475  	  * @author Nicola Asuni
22476  	  * @public
22477  	  * @since 5.8.017 (2010-08-24)
22478  	  * @see startTemplate(), endTemplate()
22479  	  */
22480  	public function printTemplate($id, $x='', $y='', $w=0, $h=0, $align='', $palign='', $fitonpage=false) {
22481  	 	 if ($this->state != 2) {
22482  	 	 	  return;
22483  	 	 }
22484  	 	 if (!isset($this->xobjects[$id])) {
22485  	 	 	 $this->Error('The XObject Template \''.$id.'\' doesn\'t exist!');
22486  	 	 }
22487  	 	 if ($this->inxobj) {
22488  	 	 	 if ($id == $this->xobjid) {
22489  	 	 	 	 // close current template
22490  	 	 	 	 $this->endTemplate();
22491  	 	 	 } else {
22492  	 	 	 	 // use the template as resource for the template currently opened
22493  	 	 	 	 $this->xobjects[$this->xobjid]['xobjects'][$id] = $this->xobjects[$id];
22494  	 	 	 }
22495  	 	 }
22496  	 	 // set default values
22497  	 	 if ($x === '') {
22498  	 	 	 $x = $this->x;
22499  	 	 }
22500  	 	 if ($y === '') {
22501  	 	 	 $y = $this->y;
22502  	 	 }
22503  	 	 // check page for no-write regions and adapt page margins if necessary
22504  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22505  	 	 $ow = $this->xobjects[$id]['w'];
22506  	 	 if ($ow <= 0) {
22507  	 	 	 $ow = 1;
22508  	 	 }
22509  	 	 $oh = $this->xobjects[$id]['h'];
22510  	 	 if ($oh <= 0) {
22511  	 	 	 $oh = 1;
22512  	 	 }
22513  	 	 // calculate template width and height on document
22514  	 	 if (($w <= 0) AND ($h <= 0)) {
22515  	 	 	 $w = $ow;
22516  	 	 	 $h = $oh;
22517  	 	 } elseif ($w <= 0) {
22518  	 	 	 $w = $h * $ow / $oh;
22519  	 	 } elseif ($h <= 0) {
22520  	 	 	 $h = $w * $oh / $ow;
22521  	 	 }
22522  	 	 // fit the template on available space
22523  	 	 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22524  	 	 // set page alignment
22525  	 	 $rb_y = $y + $h;
22526  	 	 // set alignment
22527  	 	 if ($this->rtl) {
22528  	 	 	 if ($palign == 'L') {
22529  	 	 	 	 $xt = $this->lMargin;
22530  	 	 	 } elseif ($palign == 'C') {
22531  	 	 	 	 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22532  	 	 	 } elseif ($palign == 'R') {
22533  	 	 	 	 $xt = $this->w - $this->rMargin - $w;
22534  	 	 	 } else {
22535  	 	 	 	 $xt = $x - $w;
22536  	 	 	 }
22537  	 	 	 $rb_x = $xt;
22538  	 	 } else {
22539  	 	 	 if ($palign == 'L') {
22540  	 	 	 	 $xt = $this->lMargin;
22541  	 	 	 } elseif ($palign == 'C') {
22542  	 	 	 	 $xt = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22543  	 	 	 } elseif ($palign == 'R') {
22544  	 	 	 	 $xt = $this->w - $this->rMargin - $w;
22545  	 	 	 } else {
22546  	 	 	 	 $xt = $x;
22547  	 	 	 }
22548  	 	 	 $rb_x = $xt + $w;
22549  	 	 }
22550  	 	 // print XObject Template + Transformation matrix
22551  	 	 $this->StartTransform();
22552  	 	 // translate and scale
22553  	 	 $sx = ($w / $ow);
22554  	 	 $sy = ($h / $oh);
22555  	 	 $tm = array();
22556  	 	 $tm[0] = $sx;
22557  	 	 $tm[1] = 0;
22558  	 	 $tm[2] = 0;
22559  	 	 $tm[3] = $sy;
22560  	 	 $tm[4] = $xt * $this->k;
22561  	 	 $tm[5] = ($this->h - $h - $y) * $this->k;
22562  	 	 $this->Transform($tm);
22563  	 	 // set object
22564  	 	 $this->_out('/'.$id.' Do');
22565  	 	 $this->StopTransform();
22566  	 	 // add annotations
22567  	 	 if (!empty($this->xobjects[$id]['annotations'])) {
22568  	 	 	 foreach ($this->xobjects[$id]['annotations'] as $annot) {
22569  	 	 	 	 // transform original coordinates
22570  	 	 	 	 $coordlt = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, ($annot['x'] * $this->k), (-$annot['y'] * $this->k)));
22571  	 	 	 	 $ax = ($coordlt[4] / $this->k);
22572  	 	 	 	 $ay = ($this->h - $h - ($coordlt[5] / $this->k));
22573  	 	 	 	 $coordrb = TCPDF_STATIC::getTransformationMatrixProduct($tm, array(1, 0, 0, 1, (($annot['x'] + $annot['w']) * $this->k), ((-$annot['y'] - $annot['h']) * $this->k)));
22574  	 	 	 	 $aw = ($coordrb[4] / $this->k) - $ax;
22575  	 	 	 	 $ah = ($this->h - $h - ($coordrb[5] / $this->k)) - $ay;
22576  	 	 	 	 $this->Annotation($ax, $ay, $aw, $ah, $annot['text'], $annot['opt'], $annot['spaces']);
22577  	 	 	 }
22578  	 	 }
22579  	 	 // set pointer to align the next text/objects
22580  	 	 switch($align) {
22581  	 	 	 case 'T': {
22582  	 	 	 	 $this->y = $y;
22583  	 	 	 	 $this->x = $rb_x;
22584  	 	 	 	 break;
22585  	 	 	 }
22586  	 	 	 case 'M': {
22587  	 	 	 	 $this->y = $y + round($h/2);
22588  	 	 	 	 $this->x = $rb_x;
22589  	 	 	 	 break;
22590  	 	 	 }
22591  	 	 	 case 'B': {
22592  	 	 	 	 $this->y = $rb_y;
22593  	 	 	 	 $this->x = $rb_x;
22594  	 	 	 	 break;
22595  	 	 	 }
22596  	 	 	 case 'N': {
22597  	 	 	 	 $this->SetY($rb_y);
22598  	 	 	 	 break;
22599  	 	 	 }
22600  	 	 	 default:{
22601  	 	 	 	 break;
22602  	 	 	 }
22603  	 	 }
22604  	 }
22605  
22606  	 /**
22607  	  * Set the percentage of character stretching.
22608  	  * @param $perc (int) percentage of stretching (100 = no stretching)
22609  	  * @author Nicola Asuni
22610  	  * @public
22611  	  * @since 5.9.000 (2010-09-29)
22612  	  */
22613  	public function setFontStretching($perc=100) {
22614  	 	 $this->font_stretching = $perc;
22615  	 }
22616  
22617  	 /**
22618  	  * Get the percentage of character stretching.
22619  	  * @return float stretching value
22620  	  * @author Nicola Asuni
22621  	  * @public
22622  	  * @since 5.9.000 (2010-09-29)
22623  	  */
22624  	public function getFontStretching() {
22625  	 	 return $this->font_stretching;
22626  	 }
22627  
22628  	 /**
22629  	  * Set the amount to increase or decrease the space between characters in a text.
22630  	  * @param $spacing (float) amount to increase or decrease the space between characters in a text (0 = default spacing)
22631  	  * @author Nicola Asuni
22632  	  * @public
22633  	  * @since 5.9.000 (2010-09-29)
22634  	  */
22635  	public function setFontSpacing($spacing=0) {
22636  	 	 $this->font_spacing = $spacing;
22637  	 }
22638  
22639  	 /**
22640  	  * Get the amount to increase or decrease the space between characters in a text.
22641  	  * @return int font spacing (tracking) value
22642  	  * @author Nicola Asuni
22643  	  * @public
22644  	  * @since 5.9.000 (2010-09-29)
22645  	  */
22646  	public function getFontSpacing() {
22647  	 	 return $this->font_spacing;
22648  	 }
22649  
22650  	 /**
22651  	  * Return an array of no-write page regions
22652  	  * @return array of no-write page regions
22653  	  * @author Nicola Asuni
22654  	  * @public
22655  	  * @since 5.9.003 (2010-10-13)
22656  	  * @see setPageRegions(), addPageRegion()
22657  	  */
22658  	public function getPageRegions() {
22659  	 	 return $this->page_regions;
22660  	 }
22661  
22662  	 /**
22663  	  * Set no-write regions on page.
22664  	  * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22665  	  * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22666  	  * You can set multiple regions for the same page.
22667  	  * @param $regions (array) array of no-write regions. For each region you can define an array as follow: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right). Omit this parameter to remove all regions.
22668  	  * @author Nicola Asuni
22669  	  * @public
22670  	  * @since 5.9.003 (2010-10-13)
22671  	  * @see addPageRegion(), getPageRegions()
22672  	  */
22673  	public function setPageRegions($regions=array()) {
22674  	 	 // empty current regions array
22675  	 	 $this->page_regions = array();
22676  	 	 // add regions
22677  	 	 foreach ($regions as $data) {
22678  	 	 	 $this->addPageRegion($data);
22679  	 	 }
22680  	 }
22681  
22682  	 /**
22683  	  * Add a single no-write region on selected page.
22684  	  * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22685  	  * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22686  	  * You can set multiple regions for the same page.
22687  	  * @param $region (array) array of a single no-write region array: ('page' => page number or empy for current page, 'xt' => X top, 'yt' => Y top, 'xb' => X bottom, 'yb' => Y bottom, 'side' => page side 'L' = left or 'R' = right).
22688  	  * @author Nicola Asuni
22689  	  * @public
22690  	  * @since 5.9.003 (2010-10-13)
22691  	  * @see setPageRegions(), getPageRegions()
22692  	  */
22693  	public function addPageRegion($region) {
22694  	 	 if (!isset($region['page']) OR empty($region['page'])) {
22695  	 	 	 $region['page'] = $this->page;
22696  	 	 }
22697  	 	 if (isset($region['xt']) AND isset($region['xb']) AND ($region['xt'] > 0) AND ($region['xb'] > 0)
22698  	 	 	 AND isset($region['yt'])  AND isset($region['yb']) AND ($region['yt'] >= 0) AND ($region['yt'] < $region['yb'])
22699  	 	 	 AND isset($region['side']) AND (($region['side'] == 'L') OR ($region['side'] == 'R'))) {
22700  	 	 	 $this->page_regions[] = $region;
22701  	 	 }
22702  	 }
22703  
22704  	 /**
22705  	  * Remove a single no-write region.
22706  	  * @param $key (int) region key
22707  	  * @author Nicola Asuni
22708  	  * @public
22709  	  * @since 5.9.003 (2010-10-13)
22710  	  * @see setPageRegions(), getPageRegions()
22711  	  */
22712  	public function removePageRegion($key) {
22713  	 	 if (isset($this->page_regions[$key])) {
22714  	 	 	 unset($this->page_regions[$key]);
22715  	 	 }
22716  	 }
22717  
22718  	 /**
22719  	  * Check page for no-write regions and adapt current coordinates and page margins if necessary.
22720  	  * A no-write region is a portion of the page with a rectangular or trapezium shape that will not be covered when writing text or html code.
22721  	  * A region is always aligned on the left or right side of the page ad is defined using a vertical segment.
22722  	  * @param $h (float) height of the text/image/object to print in user units
22723  	  * @param $x (float) current X coordinate in user units
22724  	  * @param $y (float) current Y coordinate in user units
22725  	  * @return array($x, $y)
22726  	  * @author Nicola Asuni
22727  	  * @protected
22728  	  * @since 5.9.003 (2010-10-13)
22729  	  */
22730  	protected function checkPageRegions($h, $x, $y) {
22731  	 	 // set default values
22732  	 	 if ($x === '') {
22733  	 	 	 $x = $this->x;
22734  	 	 }
22735  	 	 if ($y === '') {
22736  	 	 	 $y = $this->y;
22737  	 	 }
22738  	 	 if (!$this->check_page_regions OR empty($this->page_regions)) {
22739  	 	 	 // no page regions defined
22740  	 	 	 return array($x, $y);
22741  	 	 }
22742  	 	 if (empty($h)) {
22743  	 	 	 $h = $this->getCellHeight($this->FontSize);
22744  	 	 }
22745  	 	 // check for page break
22746  	 	 if ($this->checkPageBreak($h, $y)) {
22747  	 	 	 // the content will be printed on a new page
22748  	 	 	 $x = $this->x;
22749  	 	 	 $y = $this->y;
22750  	 	 }
22751  	 	 if ($this->num_columns > 1) {
22752  	 	 	 if ($this->rtl) {
22753  	 	 	 	 $this->lMargin = ($this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22754  	 	 	 } else {
22755  	 	 	 	 $this->rMargin = ($this->w - $this->columns[$this->current_column]['x'] - $this->columns[$this->current_column]['w']);
22756  	 	 	 }
22757  	 	 } else {
22758  	 	 	 if ($this->rtl) {
22759  	 	 	 	 $this->lMargin = max($this->clMargin, $this->original_lMargin);
22760  	 	 	 } else {
22761  	 	 	 	 $this->rMargin = max($this->crMargin, $this->original_rMargin);
22762  	 	 	 }
22763  	 	 }
22764  	 	 // adjust coordinates and page margins
22765  	 	 foreach ($this->page_regions as $regid => $regdata) {
22766  	 	 	 if ($regdata['page'] == $this->page) {
22767  	 	 	 	 // check region boundaries
22768  	 	 	 	 if (($y > ($regdata['yt'] - $h)) AND ($y <= $regdata['yb'])) {
22769  	 	 	 	 	 // Y is inside the region
22770  	 	 	 	 	 $minv = ($regdata['xb'] - $regdata['xt']) / ($regdata['yb'] - $regdata['yt']); // inverse of angular coefficient
22771  	 	 	 	 	 $yt = max($y, $regdata['yt']);
22772  	 	 	 	 	 $yb = min(($yt + $h), $regdata['yb']);
22773  	 	 	 	 	 $xt = (($yt - $regdata['yt']) * $minv) + $regdata['xt'];
22774  	 	 	 	 	 $xb = (($yb - $regdata['yt']) * $minv) + $regdata['xt'];
22775  	 	 	 	 	 if ($regdata['side'] == 'L') { // left side
22776  	 	 	 	 	 	 $new_margin = max($xt, $xb);
22777  	 	 	 	 	 	 if ($this->lMargin < $new_margin) {
22778  	 	 	 	 	 	 	 if ($this->rtl) {
22779  	 	 	 	 	 	 	 	 // adjust left page margin
22780  	 	 	 	 	 	 	 	 $this->lMargin = max(0, $new_margin);
22781  	 	 	 	 	 	 	 }
22782  	 	 	 	 	 	 	 if ($x < $new_margin) {
22783  	 	 	 	 	 	 	 	 // adjust x position
22784  	 	 	 	 	 	 	 	 $x = $new_margin;
22785  	 	 	 	 	 	 	 	 if ($new_margin > ($this->w - $this->rMargin)) {
22786  	 	 	 	 	 	 	 	 	 // adjust y position
22787  	 	 	 	 	 	 	 	 	 $y = $regdata['yb'] - $h;
22788  	 	 	 	 	 	 	 	 }
22789  	 	 	 	 	 	 	 }
22790  	 	 	 	 	 	 }
22791  	 	 	 	 	 } elseif ($regdata['side'] == 'R') { // right side
22792  	 	 	 	 	 	 $new_margin = min($xt, $xb);
22793  	 	 	 	 	 	 if (($this->w - $this->rMargin) > $new_margin) {
22794  	 	 	 	 	 	 	 if (!$this->rtl) {
22795  	 	 	 	 	 	 	 	 // adjust right page margin
22796  	 	 	 	 	 	 	 	 $this->rMargin = max(0, ($this->w - $new_margin));
22797  	 	 	 	 	 	 	 }
22798  	 	 	 	 	 	 	 if ($x > $new_margin) {
22799  	 	 	 	 	 	 	 	 // adjust x position
22800  	 	 	 	 	 	 	 	 $x = $new_margin;
22801  	 	 	 	 	 	 	 	 if ($new_margin > $this->lMargin) {
22802  	 	 	 	 	 	 	 	 	 // adjust y position
22803  	 	 	 	 	 	 	 	 	 $y = $regdata['yb'] - $h;
22804  	 	 	 	 	 	 	 	 }
22805  	 	 	 	 	 	 	 }
22806  	 	 	 	 	 	 }
22807  	 	 	 	 	 }
22808  	 	 	 	 }
22809  	 	 	 }
22810  	 	 }
22811  	 	 return array($x, $y);
22812  	 }
22813  
22814  	 // --- SVG METHODS ---------------------------------------------------------
22815  
22816  	 /**
22817  	  * Embedd a Scalable Vector Graphics (SVG) image.
22818  	  * NOTE: SVG standard is not yet fully implemented, use the setRasterizeVectorImages() method to enable/disable rasterization of vector images using ImageMagick library.
22819  	  * @param $file (string) Name of the SVG file or a '@' character followed by the SVG data string.
22820  	  * @param $x (float) Abscissa of the upper-left corner.
22821  	  * @param $y (float) Ordinate of the upper-left corner.
22822  	  * @param $w (float) Width of the image in the page. If not specified or equal to zero, it is automatically calculated.
22823  	  * @param $h (float) Height of the image in the page. If not specified or equal to zero, it is automatically calculated.
22824  	  * @param $link (mixed) URL or identifier returned by AddLink().
22825  	  * @param $align (string) 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> If the alignment is an empty string, then the pointer will be restored on the starting SVG position.
22826  	  * @param $palign (string) 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>
22827  	  * @param $border (mixed) Indicates if borders must be drawn around the cell. The value can be 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> or an array of line styles for each border group - for example: array('LTRB' => array('width' => 2, 'cap' => 'butt', 'join' => 'miter', 'dash' => 0, 'color' => array(0, 0, 0)))
22828  	  * @param $fitonpage (boolean) if true the image is resized to not exceed page dimensions.
22829  	  * @author Nicola Asuni
22830  	  * @since 5.0.000 (2010-05-02)
22831  	  * @public
22832  	  */
22833  	public function ImageSVG($file, $x='', $y='', $w=0, $h=0, $link='', $align='', $palign='', $border=0, $fitonpage=false) {
22834  	 	 if ($this->state != 2) {
22835  	 	 	  return;
22836  	 	 }
22837  	 	 // reset SVG vars
22838  	 	 $this->svggradients = array();
22839  	 	 $this->svggradientid = 0;
22840  	 	 $this->svgdefsmode = false;
22841  	 	 $this->svgdefs = array();
22842  	 	 $this->svgclipmode = false;
22843  	 	 $this->svgclippaths = array();
22844  	 	 $this->svgcliptm = array();
22845  	 	 $this->svgclipid = 0;
22846  	 	 $this->svgtext = '';
22847  	 	 $this->svgtextmode = array();
22848  	 	 if ($this->rasterize_vector_images AND ($w > 0) AND ($h > 0)) {
22849  	 	 	 // convert SVG to raster image using GD or ImageMagick libraries
22850  	 	 	 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22851  	 	 }
22852  	 	 if ($file[0] === '@') { // image from string
22853  	 	 	 $this->svgdir = '';
22854  	 	 	 $svgdata = substr($file, 1);
22855  	 	 } else { // SVG file
22856  	 	 	 $this->svgdir = dirname($file);
22857              $svgdata = $this->getCachedFileContents($file);
22858  	 	 }
22859  	 	 if ($svgdata === FALSE) {
22860  	 	 	 $this->Error('SVG file not found: '.$file);
22861  	 	 }
22862  	 	 if ($x === '') {
22863  	 	 	 $x = $this->x;
22864  	 	 }
22865  	 	 if ($y === '') {
22866  	 	 	 $y = $this->y;
22867  	 	 }
22868  	 	 // check page for no-write regions and adapt page margins if necessary
22869  	 	 list($x, $y) = $this->checkPageRegions($h, $x, $y);
22870  	 	 $k = $this->k;
22871  	 	 $ox = 0;
22872  	 	 $oy = 0;
22873  	 	 $ow = $w;
22874  	 	 $oh = $h;
22875  	 	 $aspect_ratio_align = 'xMidYMid';
22876  	 	 $aspect_ratio_ms = 'meet';
22877  	 	 $regs = array();
22878  	 	 // get original image width and height
22879  	 	 preg_match('/<svg([^\>]*)>/si', $svgdata, $regs);
22880  	 	 if (isset($regs[1]) AND !empty($regs[1])) {
22881  	 	 	 $tmp = array();
22882  	 	 	 if (preg_match('/[\s]+x[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22883  	 	 	 	 $ox = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22884  	 	 	 }
22885  	 	 	 $tmp = array();
22886  	 	 	 if (preg_match('/[\s]+y[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22887  	 	 	 	 $oy = $this->getHTMLUnitToUnits($tmp[1], 0, $this->svgunit, false);
22888  	 	 	 }
22889  	 	 	 $tmp = array();
22890  	 	 	 if (preg_match('/[\s]+width[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22891  	 	 	 	 $ow = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22892  	 	 	 }
22893  	 	 	 $tmp = array();
22894  	 	 	 if (preg_match('/[\s]+height[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22895  	 	 	 	 $oh = $this->getHTMLUnitToUnits($tmp[1], 1, $this->svgunit, false);
22896  	 	 	 }
22897  	 	 	 $tmp = array();
22898  	 	 	 $view_box = array();
22899  	 	 	 if (preg_match('/[\s]+viewBox[\s]*=[\s]*"[\s]*([0-9\.\-]+)[\s]+([0-9\.\-]+)[\s]+([0-9\.]+)[\s]+([0-9\.]+)[\s]*"/si', $regs[1], $tmp)) {
22900  	 	 	 	 if (count($tmp) == 5) {
22901  	 	 	 	 	 array_shift($tmp);
22902  	 	 	 	 	 foreach ($tmp as $key => $val) {
22903  	 	 	 	 	 	 $view_box[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
22904  	 	 	 	 	 }
22905  	 	 	 	 	 $ox = $view_box[0];
22906  	 	 	 	 	 $oy = $view_box[1];
22907  	 	 	 	 }
22908  	 	 	 	 // get aspect ratio
22909  	 	 	 	 $tmp = array();
22910  	 	 	 	 if (preg_match('/[\s]+preserveAspectRatio[\s]*=[\s]*"([^"]*)"/si', $regs[1], $tmp)) {
22911  	 	 	 	 	 $aspect_ratio = preg_split('/[\s]+/si', $tmp[1]);
22912  	 	 	 	 	 switch (count($aspect_ratio)) {
22913  	 	 	 	 	 	 case 3: {
22914  	 	 	 	 	 	 	 $aspect_ratio_align = $aspect_ratio[1];
22915  	 	 	 	 	 	 	 $aspect_ratio_ms = $aspect_ratio[2];
22916  	 	 	 	 	 	 	 break;
22917  	 	 	 	 	 	 }
22918  	 	 	 	 	 	 case 2: {
22919  	 	 	 	 	 	 	 $aspect_ratio_align = $aspect_ratio[0];
22920  	 	 	 	 	 	 	 $aspect_ratio_ms = $aspect_ratio[1];
22921  	 	 	 	 	 	 	 break;
22922  	 	 	 	 	 	 }
22923  	 	 	 	 	 	 case 1: {
22924  	 	 	 	 	 	 	 $aspect_ratio_align = $aspect_ratio[0];
22925  	 	 	 	 	 	 	 $aspect_ratio_ms = 'meet';
22926  	 	 	 	 	 	 	 break;
22927  	 	 	 	 	 	 }
22928  	 	 	 	 	 }
22929  	 	 	 	 }
22930  	 	 	 }
22931  	 	 }
22932  	 	 if ($ow <= 0) {
22933  	 	 	 $ow = 1;
22934  	 	 }
22935  	 	 if ($oh <= 0) {
22936  	 	 	 $oh = 1;
22937  	 	 }
22938  	 	 // calculate image width and height on document
22939  	 	 if (($w <= 0) AND ($h <= 0)) {
22940  	 	 	 // convert image size to document unit
22941  	 	 	 $w = $ow;
22942  	 	 	 $h = $oh;
22943  	 	 } elseif ($w <= 0) {
22944  	 	 	 $w = $h * $ow / $oh;
22945  	 	 } elseif ($h <= 0) {
22946  	 	 	 $h = $w * $oh / $ow;
22947  	 	 }
22948  	 	 // fit the image on available space
22949  	 	 list($w, $h, $x, $y) = $this->fitBlock($w, $h, $x, $y, $fitonpage);
22950  	 	 if ($this->rasterize_vector_images) {
22951  	 	 	 // convert SVG to raster image using GD or ImageMagick libraries
22952  	 	 	 return $this->Image($file, $x, $y, $w, $h, 'SVG', $link, $align, true, 300, $palign, false, false, $border, false, false, false);
22953  	 	 }
22954  	 	 // set alignment
22955  	 	 $this->img_rb_y = $y + $h;
22956  	 	 // set alignment
22957  	 	 if ($this->rtl) {
22958  	 	 	 if ($palign == 'L') {
22959  	 	 	 	 $ximg = $this->lMargin;
22960  	 	 	 } elseif ($palign == 'C') {
22961  	 	 	 	 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22962  	 	 	 } elseif ($palign == 'R') {
22963  	 	 	 	 $ximg = $this->w - $this->rMargin - $w;
22964  	 	 	 } else {
22965  	 	 	 	 $ximg = $x - $w;
22966  	 	 	 }
22967  	 	 	 $this->img_rb_x = $ximg;
22968  	 	 } else {
22969  	 	 	 if ($palign == 'L') {
22970  	 	 	 	 $ximg = $this->lMargin;
22971  	 	 	 } elseif ($palign == 'C') {
22972  	 	 	 	 $ximg = ($this->w + $this->lMargin - $this->rMargin - $w) / 2;
22973  	 	 	 } elseif ($palign == 'R') {
22974  	 	 	 	 $ximg = $this->w - $this->rMargin - $w;
22975  	 	 	 } else {
22976  	 	 	 	 $ximg = $x;
22977  	 	 	 }
22978  	 	 	 $this->img_rb_x = $ximg + $w;
22979  	 	 }
22980  	 	 // store current graphic vars
22981  	 	 $gvars = $this->getGraphicVars();
22982  	 	 // store SVG position and scale factors
22983  	 	 $svgoffset_x = ($ximg - $ox) * $this->k;
22984  	 	 $svgoffset_y = -($y - $oy) * $this->k;
22985  	 	 if (isset($view_box[2]) AND ($view_box[2] > 0) AND ($view_box[3] > 0)) {
22986  	 	 	 $ow = $view_box[2];
22987  	 	 	 $oh = $view_box[3];
22988  	 	 } else {
22989  	 	 	 if ($ow <= 0) {
22990  	 	 	 	 $ow = $w;
22991  	 	 	 }
22992  	 	 	 if ($oh <= 0) {
22993  	 	 	 	 $oh = $h;
22994  	 	 	 }
22995  	 	 }
22996  	 	 $svgscale_x = $w / $ow;
22997  	 	 $svgscale_y = $h / $oh;
22998  	 	 // scaling and alignment
22999  	 	 if ($aspect_ratio_align != 'none') {
23000  	 	 	 // store current scaling values
23001  	 	 	 $svgscale_old_x = $svgscale_x;
23002  	 	 	 $svgscale_old_y = $svgscale_y;
23003  	 	 	 // force uniform scaling
23004  	 	 	 if ($aspect_ratio_ms == 'slice') {
23005  	 	 	 	 // the entire viewport is covered by the viewBox
23006  	 	 	 	 if ($svgscale_x > $svgscale_y) {
23007  	 	 	 	 	 $svgscale_y = $svgscale_x;
23008  	 	 	 	 } elseif ($svgscale_x < $svgscale_y) {
23009  	 	 	 	 	 $svgscale_x = $svgscale_y;
23010  	 	 	 	 }
23011  	 	 	 } else { // meet
23012  	 	 	 	 // the entire viewBox is visible within the viewport
23013  	 	 	 	 if ($svgscale_x < $svgscale_y) {
23014  	 	 	 	 	 $svgscale_y = $svgscale_x;
23015  	 	 	 	 } elseif ($svgscale_x > $svgscale_y) {
23016  	 	 	 	 	 $svgscale_x = $svgscale_y;
23017  	 	 	 	 }
23018  	 	 	 }
23019  	 	 	 // correct X alignment
23020  	 	 	 switch (substr($aspect_ratio_align, 1, 3)) {
23021  	 	 	 	 case 'Min': {
23022  	 	 	 	 	 // do nothing
23023  	 	 	 	 	 break;
23024  	 	 	 	 }
23025  	 	 	 	 case 'Max': {
23026  	 	 	 	 	 $svgoffset_x += (($w * $this->k) - ($ow * $this->k * $svgscale_x));
23027  	 	 	 	 	 break;
23028  	 	 	 	 }
23029  	 	 	 	 default:
23030  	 	 	 	 case 'Mid': {
23031  	 	 	 	 	 $svgoffset_x += ((($w * $this->k) - ($ow * $this->k * $svgscale_x)) / 2);
23032  	 	 	 	 	 break;
23033  	 	 	 	 }
23034  	 	 	 }
23035  	 	 	 // correct Y alignment
23036  	 	 	 switch (substr($aspect_ratio_align, 5)) {
23037  	 	 	 	 case 'Min': {
23038  	 	 	 	 	 // do nothing
23039  	 	 	 	 	 break;
23040  	 	 	 	 }
23041  	 	 	 	 case 'Max': {
23042  	 	 	 	 	 $svgoffset_y -= (($h * $this->k) - ($oh * $this->k * $svgscale_y));
23043  	 	 	 	 	 break;
23044  	 	 	 	 }
23045  	 	 	 	 default:
23046  	 	 	 	 case 'Mid': {
23047  	 	 	 	 	 $svgoffset_y -= ((($h * $this->k) - ($oh * $this->k * $svgscale_y)) / 2);
23048  	 	 	 	 	 break;
23049  	 	 	 	 }
23050  	 	 	 }
23051  	 	 }
23052  	 	 // store current page break mode
23053  	 	 $page_break_mode = $this->AutoPageBreak;
23054  	 	 $page_break_margin = $this->getBreakMargin();
23055  	 	 $cell_padding = $this->cell_padding;
23056  	 	 $this->SetCellPadding(0);
23057  	 	 $this->SetAutoPageBreak(false);
23058  	 	 // save the current graphic state
23059  	 	 $this->_out('q'.$this->epsmarker);
23060  	 	 // set initial clipping mask
23061  	 	 $this->Rect($ximg, $y, $w, $h, 'CNZ', array(), array());
23062  	 	 // scale and translate
23063  	 	 $e = $ox * $this->k * (1 - $svgscale_x);
23064  	 	 $f = ($this->h - $oy) * $this->k * (1 - $svgscale_y);
23065  	 	 $this->_out(sprintf('%F %F %F %F %F %F cm', $svgscale_x, 0, 0, $svgscale_y, ($e + $svgoffset_x), ($f + $svgoffset_y)));
23066  	 	 // creates a new XML parser to be used by the other XML functions
23067  	 	 $parser = xml_parser_create('UTF-8');
23068  	 	 // the following function allows to use parser inside object
23069  	 	 xml_set_object($parser, $this);
23070  	 	 // disable case-folding for this XML parser
23071  	 	 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
23072  	 	 // sets the element handler functions for the XML parser
23073  	 	 xml_set_element_handler($parser, 'startSVGElementHandler', 'endSVGElementHandler');
23074  	 	 // sets the character data handler function for the XML parser
23075  	 	 xml_set_character_data_handler($parser, 'segSVGContentHandler');
23076  	 	 // start parsing an XML document
23077  	 	 if (!xml_parse($parser, $svgdata)) {
23078  	 	 	 $error_message = sprintf('SVG Error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser));
23079  	 	 	 $this->Error($error_message);
23080  	 	 }
23081  	 	 // free this XML parser
23082  	 	 xml_parser_free($parser);
23083  
23084  	 	 // >= PHP 7.0.0 "explicitly unset the reference to parser to avoid memory leaks"
23085  	 	 unset($parser);
23086  
23087  	 	 // restore previous graphic state
23088  	 	 $this->_out($this->epsmarker.'Q');
23089  	 	 // restore graphic vars
23090  	 	 $this->setGraphicVars($gvars);
23091  	 	 $this->lasth = $gvars['lasth'];
23092  	 	 if (!empty($border)) {
23093  	 	 	 $bx = $this->x;
23094  	 	 	 $by = $this->y;
23095  	 	 	 $this->x = $ximg;
23096  	 	 	 if ($this->rtl) {
23097  	 	 	 	 $this->x += $w;
23098  	 	 	 }
23099  	 	 	 $this->y = $y;
23100  	 	 	 $this->Cell($w, $h, '', $border, 0, '', 0, '', 0, true);
23101  	 	 	 $this->x = $bx;
23102  	 	 	 $this->y = $by;
23103  	 	 }
23104  	 	 if ($link) {
23105  	 	 	 $this->Link($ximg, $y, $w, $h, $link, 0);
23106  	 	 }
23107  	 	 // set pointer to align the next text/objects
23108  	 	 switch($align) {
23109  	 	 	 case 'T':{
23110  	 	 	 	 $this->y = $y;
23111  	 	 	 	 $this->x = $this->img_rb_x;
23112  	 	 	 	 break;
23113  	 	 	 }
23114  	 	 	 case 'M':{
23115  	 	 	 	 $this->y = $y + round($h/2);
23116  	 	 	 	 $this->x = $this->img_rb_x;
23117  	 	 	 	 break;
23118  	 	 	 }
23119  	 	 	 case 'B':{
23120  	 	 	 	 $this->y = $this->img_rb_y;
23121  	 	 	 	 $this->x = $this->img_rb_x;
23122  	 	 	 	 break;
23123  	 	 	 }
23124  	 	 	 case 'N':{
23125  	 	 	 	 $this->SetY($this->img_rb_y);
23126  	 	 	 	 break;
23127  	 	 	 }
23128  	 	 	 default:{
23129  	 	 	 	 // restore pointer to starting position
23130  	 	 	 	 $this->x = $gvars['x'];
23131  	 	 	 	 $this->y = $gvars['y'];
23132  	 	 	 	 $this->page = $gvars['page'];
23133  	 	 	 	 $this->current_column = $gvars['current_column'];
23134  	 	 	 	 $this->tMargin = $gvars['tMargin'];
23135  	 	 	 	 $this->bMargin = $gvars['bMargin'];
23136  	 	 	 	 $this->w = $gvars['w'];
23137  	 	 	 	 $this->h = $gvars['h'];
23138  	 	 	 	 $this->wPt = $gvars['wPt'];
23139  	 	 	 	 $this->hPt = $gvars['hPt'];
23140  	 	 	 	 $this->fwPt = $gvars['fwPt'];
23141  	 	 	 	 $this->fhPt = $gvars['fhPt'];
23142  	 	 	 	 break;
23143  	 	 	 }
23144  	 	 }
23145  	 	 $this->endlinex = $this->img_rb_x;
23146  	 	 // restore page break
23147  	 	 $this->SetAutoPageBreak($page_break_mode, $page_break_margin);
23148  	 	 $this->cell_padding = $cell_padding;
23149  	 }
23150  
23151  	 /**
23152  	  * Convert SVG transformation matrix to PDF.
23153  	  * @param $tm (array) original SVG transformation matrix
23154  	  * @return array transformation matrix
23155  	  * @protected
23156  	  * @since 5.0.000 (2010-05-02)
23157  	  */
23158  	protected function convertSVGtMatrix($tm) {
23159  	 	 $a = $tm[0];
23160  	 	 $b = -$tm[1];
23161  	 	 $c = -$tm[2];
23162  	 	 $d = $tm[3];
23163  	 	 $e = $this->getHTMLUnitToUnits($tm[4], 1, $this->svgunit, false) * $this->k;
23164  	 	 $f = -$this->getHTMLUnitToUnits($tm[5], 1, $this->svgunit, false) * $this->k;
23165  	 	 $x = 0;
23166  	 	 $y = $this->h * $this->k;
23167  	 	 $e = ($x * (1 - $a)) - ($y * $c) + $e;
23168  	 	 $f = ($y * (1 - $d)) - ($x * $b) + $f;
23169  	 	 return array($a, $b, $c, $d, $e, $f);
23170  	 }
23171  
23172  	 /**
23173  	  * Apply SVG graphic transformation matrix.
23174  	  * @param $tm (array) original SVG transformation matrix
23175  	  * @protected
23176  	  * @since 5.0.000 (2010-05-02)
23177  	  */
23178  	protected function SVGTransform($tm) {
23179  	 	 $this->Transform($this->convertSVGtMatrix($tm));
23180  	 }
23181  
23182  	 /**
23183  	  * Apply the requested SVG styles (*** TO BE COMPLETED ***)
23184  	  * @param $svgstyle (array) array of SVG styles to apply
23185  	  * @param $prevsvgstyle (array) array of previous SVG style
23186  	  * @param $x (int) X origin of the bounding box
23187  	  * @param $y (int) Y origin of the bounding box
23188  	  * @param $w (int) width of the bounding box
23189  	  * @param $h (int) height of the bounding box
23190  	  * @param $clip_function (string) clip function
23191  	  * @param $clip_params (array) array of parameters for clipping function
23192  	  * @return object style
23193  	  * @author Nicola Asuni
23194  	  * @since 5.0.000 (2010-05-02)
23195  	  * @protected
23196  	  */
23197  	protected function setSVGStyles($svgstyle, $prevsvgstyle, $x=0, $y=0, $w=1, $h=1, $clip_function='', $clip_params=array()) {
23198  	 	 if ($this->state != 2) {
23199  	 	 	  return;
23200  	 	 }
23201  	 	 $objstyle = '';
23202  	 	 $minlen = (0.01 / $this->k); // minimum acceptable length
23203  	 	 if (!isset($svgstyle['opacity'])) {
23204  	 	 	 return $objstyle;
23205  	 	 }
23206  	 	 // clip-path
23207  	 	 $regs = array();
23208  	 	 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['clip-path'], $regs)) {
23209  	 	 	 $clip_path = $this->svgclippaths[$regs[1]];
23210  	 	 	 foreach ($clip_path as $cp) {
23211  	 	 	 	 $this->startSVGElementHandler('clip-path', $cp['name'], $cp['attribs'], $cp['tm']);
23212  	 	 	 }
23213  	 	 }
23214  	 	 // opacity
23215  	 	 if ($svgstyle['opacity'] != 1) {
23216  	 	 	 $this->setAlpha($svgstyle['opacity'], 'Normal', $svgstyle['opacity'], false);
23217  	 	 }
23218  	 	 // color
23219  	 	 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['color'], $this->spot_colors);
23220  	 	 $this->SetFillColorArray($fill_color);
23221  	 	 // text color
23222  	 	 $text_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['text-color'], $this->spot_colors);
23223  	 	 $this->SetTextColorArray($text_color);
23224  	 	 // clip
23225  	 	 if (preg_match('/rect\(([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)[\s]*([a-z0-9\-\.]*)\)/si', $svgstyle['clip'], $regs)) {
23226  	 	 	 $top = (isset($regs[1])?$this->getHTMLUnitToUnits($regs[1], 0, $this->svgunit, false):0);
23227  	 	 	 $right = (isset($regs[2])?$this->getHTMLUnitToUnits($regs[2], 0, $this->svgunit, false):0);
23228  	 	 	 $bottom = (isset($regs[3])?$this->getHTMLUnitToUnits($regs[3], 0, $this->svgunit, false):0);
23229  	 	 	 $left = (isset($regs[4])?$this->getHTMLUnitToUnits($regs[4], 0, $this->svgunit, false):0);
23230  	 	 	 $cx = $x + $left;
23231  	 	 	 $cy = $y + $top;
23232  	 	 	 $cw = $w - $left - $right;
23233  	 	 	 $ch = $h - $top - $bottom;
23234  	 	 	 if ($svgstyle['clip-rule'] == 'evenodd') {
23235  	 	 	 	 $clip_rule = 'CNZ';
23236  	 	 	 } else {
23237  	 	 	 	 $clip_rule = 'CEO';
23238  	 	 	 }
23239  	 	 	 $this->Rect($cx, $cy, $cw, $ch, $clip_rule, array(), array());
23240  	 	 }
23241  	 	 // fill
23242  	 	 $regs = array();
23243  	 	 if (preg_match('/url\([\s]*\#([^\)]*)\)/si', $svgstyle['fill'], $regs)) {
23244  	 	 	 // gradient
23245  	 	 	 $gradient = $this->svggradients[$regs[1]];
23246  	 	 	 if (isset($gradient['xref'])) {
23247  	 	 	 	 // reference to another gradient definition
23248  	 	 	 	 $newgradient = $this->svggradients[$gradient['xref']];
23249  	 	 	 	 $newgradient['coords'] = $gradient['coords'];
23250  	 	 	 	 $newgradient['mode'] = $gradient['mode'];
23251  	 	 	 	 $newgradient['type'] = $gradient['type'];
23252  	 	 	 	 $newgradient['gradientUnits'] = $gradient['gradientUnits'];
23253  	 	 	 	 if (isset($gradient['gradientTransform'])) {
23254  	 	 	 	 	 $newgradient['gradientTransform'] = $gradient['gradientTransform'];
23255  	 	 	 	 }
23256  	 	 	 	 $gradient = $newgradient;
23257  	 	 	 }
23258  	 	 	 //save current Graphic State
23259  	 	 	 $this->_outSaveGraphicsState();
23260  	 	 	 //set clipping area
23261  	 	 	 if (!empty($clip_function) AND method_exists($this, $clip_function)) {
23262  	 	 	 	 $bbox = call_user_func_array(array($this, $clip_function), $clip_params);
23263  	 	 	 	 if ((!isset($gradient['type']) OR ($gradient['type'] != 3)) AND is_array($bbox) AND (count($bbox) == 4)) {
23264  	 	 	 	 	 list($x, $y, $w, $h) = $bbox;
23265  	 	 	 	 }
23266  	 	 	 }
23267  	 	 	 if ($gradient['mode'] == 'measure') {
23268  	 	 	 	 if (!isset($gradient['coords'][4])) {
23269  	 	 	 	 	 $gradient['coords'][4] = 0.5;
23270  	 	 	 	 }
23271  	 	 	 	 if (isset($gradient['gradientTransform']) AND !empty($gradient['gradientTransform'])) {
23272  	 	 	 	 	 $gtm = $gradient['gradientTransform'];
23273  	 	 	 	 	 // apply transformation matrix
23274  	 	 	 	 	 $xa = ($gtm[0] * $gradient['coords'][0]) + ($gtm[2] * $gradient['coords'][1]) + $gtm[4];
23275  	 	 	 	 	 $ya = ($gtm[1] * $gradient['coords'][0]) + ($gtm[3] * $gradient['coords'][1]) + $gtm[5];
23276  	 	 	 	 	 $xb = ($gtm[0] * $gradient['coords'][2]) + ($gtm[2] * $gradient['coords'][3]) + $gtm[4];
23277  	 	 	 	 	 $yb = ($gtm[1] * $gradient['coords'][2]) + ($gtm[3] * $gradient['coords'][3]) + $gtm[5];
23278  	 	 	 	 	 $r = sqrt(pow(($gtm[0] * $gradient['coords'][4]), 2) + pow(($gtm[1] * $gradient['coords'][4]), 2));
23279  	 	 	 	 	 $gradient['coords'][0] = $xa;
23280  	 	 	 	 	 $gradient['coords'][1] = $ya;
23281  	 	 	 	 	 $gradient['coords'][2] = $xb;
23282  	 	 	 	 	 $gradient['coords'][3] = $yb;
23283  	 	 	 	 	 $gradient['coords'][4] = $r;
23284  	 	 	 	 }
23285  	 	 	 	 // convert SVG coordinates to user units
23286  	 	 	 	 $gradient['coords'][0] = $this->getHTMLUnitToUnits($gradient['coords'][0], 0, $this->svgunit, false);
23287  	 	 	 	 $gradient['coords'][1] = $this->getHTMLUnitToUnits($gradient['coords'][1], 0, $this->svgunit, false);
23288  	 	 	 	 $gradient['coords'][2] = $this->getHTMLUnitToUnits($gradient['coords'][2], 0, $this->svgunit, false);
23289  	 	 	 	 $gradient['coords'][3] = $this->getHTMLUnitToUnits($gradient['coords'][3], 0, $this->svgunit, false);
23290  	 	 	 	 $gradient['coords'][4] = $this->getHTMLUnitToUnits($gradient['coords'][4], 0, $this->svgunit, false);
23291  	 	 	 	 if ($w <= $minlen) {
23292  	 	 	 	 	 $w = $minlen;
23293  	 	 	 	 }
23294  	 	 	 	 if ($h <= $minlen) {
23295  	 	 	 	 	 $h = $minlen;
23296  	 	 	 	 }
23297  	 	 	 	 // shift units
23298  	 	 	 	 if ($gradient['gradientUnits'] == 'objectBoundingBox') {
23299  	 	 	 	 	 // convert to SVG coordinate system
23300  	 	 	 	 	 $gradient['coords'][0] += $x;
23301  	 	 	 	 	 $gradient['coords'][1] += $y;
23302  	 	 	 	 	 $gradient['coords'][2] += $x;
23303  	 	 	 	 	 $gradient['coords'][3] += $y;
23304  	 	 	 	 }
23305  	 	 	 	 // calculate percentages
23306  	 	 	 	 $gradient['coords'][0] = (($gradient['coords'][0] - $x) / $w);
23307  	 	 	 	 $gradient['coords'][1] = (($gradient['coords'][1] - $y) / $h);
23308  	 	 	 	 $gradient['coords'][2] = (($gradient['coords'][2] - $x) / $w);
23309  	 	 	 	 $gradient['coords'][3] = (($gradient['coords'][3] - $y) / $h);
23310  	 	 	 	 $gradient['coords'][4] /= $w;
23311  	 	 	 } elseif ($gradient['mode'] == 'percentage') {
23312  	 	 	 	 foreach($gradient['coords'] as $key => $val) {
23313  	 	 	 	 	 $gradient['coords'][$key] = (intval($val) / 100);
23314  	 	 	 	 	 if ($val < 0) {
23315  	 	 	 	 	 	 $gradient['coords'][$key] = 0;
23316  	 	 	 	 	 } elseif ($val > 1) {
23317  	 	 	 	 	 	 $gradient['coords'][$key] = 1;
23318  	 	 	 	 	 }
23319  	 	 	 	 }
23320  	 	 	 }
23321  	 	 	 if (($gradient['type'] == 2) AND ($gradient['coords'][0] == $gradient['coords'][2]) AND ($gradient['coords'][1] == $gradient['coords'][3])) {
23322  	 	 	 	 // single color (no shading)
23323  	 	 	 	 $gradient['coords'][0] = 1;
23324  	 	 	 	 $gradient['coords'][1] = 0;
23325  	 	 	 	 $gradient['coords'][2] = 0.999;
23326  	 	 	 	 $gradient['coords'][3] = 0;
23327  	 	 	 }
23328  	 	 	 // swap Y coordinates
23329  	 	 	 $tmp = $gradient['coords'][1];
23330  	 	 	 $gradient['coords'][1] = $gradient['coords'][3];
23331  	 	 	 $gradient['coords'][3] = $tmp;
23332  	 	 	 // set transformation map for gradient
23333  	 	 	 $cy = ($this->h - $y);
23334  	 	 	 if ($gradient['type'] == 3) {
23335  	 	 	 	 // circular gradient
23336  	 	 	 	 $cy -= ($gradient['coords'][1] * ($w + $h));
23337  	 	 	 	 $h = $w = max($w, $h);
23338  	 	 	 } else {
23339  	 	 	 	 $cy -= $h;
23340  	 	 	 }
23341  	 	 	 $this->_out(sprintf('%F 0 0 %F %F %F cm', ($w * $this->k), ($h * $this->k), ($x * $this->k), ($cy * $this->k)));
23342  	 	 	 if (count($gradient['stops']) > 1) {
23343  	 	 	 	 $this->Gradient($gradient['type'], $gradient['coords'], $gradient['stops'], array(), false);
23344  	 	 	 }
23345  	 	 } elseif ($svgstyle['fill'] != 'none') {
23346  	 	 	 $fill_color = TCPDF_COLORS::convertHTMLColorToDec($svgstyle['fill'], $this->spot_colors);
23347  	 	 	 if ($svgstyle['fill-opacity'] != 1) {
23348  	 	 	 	 $this->setAlpha($this->alpha['CA'], 'Normal', $svgstyle['fill-opacity'], false);
23349  	 	 	 }
23350  	 	 	 $this->SetFillColorArray($fill_color);
23351  	 	 	 if ($svgstyle['fill-rule'] == 'evenodd') {
23352  	 	 	 	 $objstyle .= 'F*';
23353  	 	 	 } else {
23354  	 	 	 	 $objstyle .= 'F';
23355  	 	 	 }
23356  	 	 }
23357  	 	 // stroke
23358  	 	 if ($svgstyle['stroke'] != 'none') {
23359  	 	 	 if ($svgstyle['stroke-opacity'] != 1) {
23360  	 	 	 	 $this->setAlpha($svgstyle['stroke-opacity'], 'Normal', $this->alpha['ca'], false);
23361  	 	 	 } elseif (preg_match('/rgba\(\d+%?,\s*\d+%?,\s*\d+%?,\s*(\d+(?:\.\d+)?)\)/i', $svgstyle['stroke'], $rgba_matches)) {
23362  	 	 	 	 $this->setAlpha($rgba_matches[1], 'Normal', $this->alpha['ca'], false);
23363  	 	 	 }
23364  	 	 	 $stroke_style = array(
23365  	 	 	 	 'color' => TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stroke'], $this->spot_colors),
23366  	 	 	 	 'width' => $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false),
23367  	 	 	 	 'cap' => $svgstyle['stroke-linecap'],
23368  	 	 	 	 'join' => $svgstyle['stroke-linejoin']
23369  	 	 	 	 );
23370  	 	 	 if (isset($svgstyle['stroke-dasharray']) AND !empty($svgstyle['stroke-dasharray']) AND ($svgstyle['stroke-dasharray'] != 'none')) {
23371  	 	 	 	 $stroke_style['dash'] = $svgstyle['stroke-dasharray'];
23372  	 	 	 }
23373  	 	 	 $this->SetLineStyle($stroke_style);
23374  	 	 	 $objstyle .= 'D';
23375  	 	 }
23376  	 	 // font
23377  	 	 $regs = array();
23378  	 	 if (!empty($svgstyle['font'])) {
23379  	 	 	 if (preg_match('/font-family[\s]*:[\s]*([^\;\"]*)/si', $svgstyle['font'], $regs)) {
23380  	 	 	 	 $font_family = $this->getFontFamilyName($regs[1]);
23381  	 	 	 } else {
23382  	 	 	 	 $font_family = $svgstyle['font-family'];
23383  	 	 	 }
23384  	 	 	 if (preg_match('/font-size[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23385  	 	 	 	 $font_size = trim($regs[1]);
23386  	 	 	 } else {
23387  	 	 	 	 $font_size = $svgstyle['font-size'];
23388  	 	 	 }
23389  	 	 	 if (preg_match('/font-style[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23390  	 	 	 	 $font_style = trim($regs[1]);
23391  	 	 	 } else {
23392  	 	 	 	 $font_style = $svgstyle['font-style'];
23393  	 	 	 }
23394  	 	 	 if (preg_match('/font-weight[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23395  	 	 	 	 $font_weight = trim($regs[1]);
23396  	 	 	 } else {
23397  	 	 	 	 $font_weight = $svgstyle['font-weight'];
23398  	 	 	 }
23399  	 	 	 if (preg_match('/font-stretch[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23400  	 	 	 	 $font_stretch = trim($regs[1]);
23401  	 	 	 } else {
23402  	 	 	 	 $font_stretch = $svgstyle['font-stretch'];
23403  	 	 	 }
23404  	 	 	 if (preg_match('/letter-spacing[\s]*:[\s]*([^\s\;\"]*)/si', $svgstyle['font'], $regs)) {
23405  	 	 	 	 $font_spacing = trim($regs[1]);
23406  	 	 	 } else {
23407  	 	 	 	 $font_spacing = $svgstyle['letter-spacing'];
23408  	 	 	 }
23409  	 	 } else {
23410  	 	 	 $font_family = $this->getFontFamilyName($svgstyle['font-family']);
23411  	 	 	 $font_size = $svgstyle['font-size'];
23412  	 	 	 $font_style = $svgstyle['font-style'];
23413  	 	 	 $font_weight = $svgstyle['font-weight'];
23414  	 	 	 $font_stretch = $svgstyle['font-stretch'];
23415  	 	 	 $font_spacing = $svgstyle['letter-spacing'];
23416  	 	 }
23417  	 	 $font_size = $this->getHTMLFontUnits($font_size, $this->svgstyles[0]['font-size'], $prevsvgstyle['font-size'], $this->svgunit);
23418  	 	 $font_stretch = $this->getCSSFontStretching($font_stretch, $svgstyle['font-stretch']);
23419  	 	 $font_spacing = $this->getCSSFontSpacing($font_spacing, $svgstyle['letter-spacing']);
23420  	 	 switch ($font_style) {
23421  	 	 	 case 'italic': {
23422  	 	 	 	 $font_style = 'I';
23423  	 	 	 	 break;
23424  	 	 	 }
23425  	 	 	 case 'oblique': {
23426  	 	 	 	 $font_style = 'I';
23427  	 	 	 	 break;
23428  	 	 	 }
23429  	 	 	 default:
23430  	 	 	 case 'normal': {
23431  	 	 	 	 $font_style = '';
23432  	 	 	 	 break;
23433  	 	 	 }
23434  	 	 }
23435  	 	 switch ($font_weight) {
23436  	 	 	 case 'bold':
23437  	 	 	 case 'bolder': {
23438  	 	 	 	 $font_style .= 'B';
23439  	 	 	 	 break;
23440  	 	 	 }
23441  	 	 	 case 'normal': {
23442  	 	 	 	 if ((substr($font_family, -1) == 'I') AND (substr($font_family, -2, 1) == 'B')) {
23443  	 	 	 	 	 $font_family = substr($font_family, 0, -2).'I';
23444  	 	 	 	 } elseif (substr($font_family, -1) == 'B') {
23445  	 	 	 	 	 $font_family = substr($font_family, 0, -1);
23446  	 	 	 	 }
23447  	 	 	 	 break;
23448  	 	 	 }
23449  	 	 }
23450  	 	 switch ($svgstyle['text-decoration']) {
23451  	 	 	 case 'underline': {
23452  	 	 	 	 $font_style .= 'U';
23453  	 	 	 	 break;
23454  	 	 	 }
23455  	 	 	 case 'overline': {
23456  	 	 	 	 $font_style .= 'O';
23457  	 	 	 	 break;
23458  	 	 	 }
23459  	 	 	 case 'line-through': {
23460  	 	 	 	 $font_style .= 'D';
23461  	 	 	 	 break;
23462  	 	 	 }
23463  	 	 	 default:
23464  	 	 	 case 'none': {
23465  	 	 	 	 break;
23466  	 	 	 }
23467  	 	 }
23468  	 	 $this->SetFont($font_family, $font_style, $font_size);
23469  	 	 $this->setFontStretching($font_stretch);
23470  	 	 $this->setFontSpacing($font_spacing);
23471  	 	 return $objstyle;
23472  	 }
23473  
23474  	 /**
23475  	  * Draws an SVG path
23476  	  * @param $d (string) attribute d of the path SVG element
23477  	  * @param $style (string) Style of rendering. Possible values are:
23478  	  * <ul>
23479  	  *	  <li>D or empty string: Draw (default).</li>
23480  	  *	  <li>F: Fill.</li>
23481  	  *	  <li>F*: Fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23482  	  *	  <li>DF or FD: Draw and fill.</li>
23483  	  *	  <li>DF* or FD*: Draw and fill using the even-odd rule to determine which regions lie inside the clipping path.</li>
23484  	  *	  <li>CNZ: Clipping mode (using the even-odd rule to determine which regions lie inside the clipping path).</li>
23485  	  *	  <li>CEO: Clipping mode (using the nonzero winding number rule to determine which regions lie inside the clipping path).</li>
23486  	  * </ul>
23487  	  * @return array of container box measures (x, y, w, h)
23488  	  * @author Nicola Asuni
23489  	  * @since 5.0.000 (2010-05-02)
23490  	  * @protected
23491  	  */
23492  	protected function SVGPath($d, $style='') {
23493  	 	 if ($this->state != 2) {
23494  	 	 	 return;
23495  	 	 }
23496  	 	 // set fill/stroke style
23497  	 	 $op = TCPDF_STATIC::getPathPaintOperator($style, '');
23498  	 	 if (empty($op)) {
23499  	 	 	 return;
23500  	 	 }
23501  	 	 $paths = array();
23502  	 	 $d = preg_replace('/([0-9ACHLMQSTVZ])([\-\+])/si', '\\1 \\2', $d);
23503  	 	 $d = preg_replace('/(\.[0-9]+)(\.)/s', '\\1 \\2', $d);
23504  	 	 preg_match_all('/([ACHLMQSTVZ])[\s]*([^ACHLMQSTVZ\"]*)/si', $d, $paths, PREG_SET_ORDER);
23505  	 	 $x = 0;
23506  	 	 $y = 0;
23507  	 	 $x1 = 0;
23508  	 	 $y1 = 0;
23509  	 	 $x2 = 0;
23510  	 	 $y2 = 0;
23511  	 	 $xmin = 2147483647;
23512  	 	 $xmax = 0;
23513  	 	 $ymin = 2147483647;
23514  	 	 $ymax = 0;
23515  	 	 $xinitial = 0;
23516  	 	 $yinitial = 0;
23517  	 	 $relcoord = false;
23518  	 	 $minlen = (0.01 / $this->k); // minimum acceptable length (3 point)
23519  	 	 $firstcmd = true; // used to print first point
23520  	 	 // draw curve pieces
23521  	 	 foreach ($paths as $key => $val) {
23522  	 	 	 // get curve type
23523  	 	 	 $cmd = trim($val[1]);
23524  	 	 	 if (strtolower($cmd) == $cmd) {
23525  	 	 	 	 // use relative coordinated instead of absolute
23526  	 	 	 	 $relcoord = true;
23527  	 	 	 	 $xoffset = $x;
23528  	 	 	 	 $yoffset = $y;
23529  	 	 	 } else {
23530  	 	 	 	 $relcoord = false;
23531  	 	 	 	 $xoffset = 0;
23532  	 	 	 	 $yoffset = 0;
23533  	 	 	 }
23534  	 	 	 $params = array();
23535  	 	 	 if (isset($val[2])) {
23536  	 	 	 	 // get curve parameters
23537  	 	 	 	 $rawparams = preg_split('/([\,\s]+)/si', trim($val[2]));
23538  	 	 	 	 $params = array();
23539  	 	 	 	 foreach ($rawparams as $ck => $cp) {
23540  	 	 	 	 	 $params[$ck] = $this->getHTMLUnitToUnits($cp, 0, $this->svgunit, false);
23541  	 	 	 	 	 if (abs($params[$ck]) < $minlen) {
23542  	 	 	 	 	 	 // approximate little values to zero
23543  	 	 	 	 	 	 $params[$ck] = 0;
23544  	 	 	 	 	 }
23545  	 	 	 	 }
23546  	 	 	 }
23547  	 	 	 // store current origin point
23548  	 	 	 $x0 = $x;
23549  	 	 	 $y0 = $y;
23550  	 	 	 switch (strtoupper($cmd)) {
23551  	 	 	 	 case 'M': { // moveto
23552  	 	 	 	 	 foreach ($params as $ck => $cp) {
23553  	 	 	 	 	 	 if (($ck % 2) == 0) {
23554  	 	 	 	 	 	 	 $x = $cp + $xoffset;
23555  	 	 	 	 	 	 } else {
23556  	 	 	 	 	 	 	 $y = $cp + $yoffset;
23557  	 	 	 	 	 	 	 if ($firstcmd OR (abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23558  	 	 	 	 	 	 	 	 if ($ck == 1) {
23559  	 	 	 	 	 	 	 	 	 $this->_outPoint($x, $y);
23560  	 	 	 	 	 	 	 	 	 $firstcmd = false;
23561  	 	 	 	 	 	 	 	 	 $xinitial = $x;
23562  	 	 	 	 	 	 	 	 	 $yinitial = $y;
23563  	 	 	 	 	 	 	 	 } else {
23564  	 	 	 	 	 	 	 	 	 $this->_outLine($x, $y);
23565  	 	 	 	 	 	 	 	 }
23566  	 	 	 	 	 	 	 	 $x0 = $x;
23567  	 	 	 	 	 	 	 	 $y0 = $y;
23568  	 	 	 	 	 	 	 }
23569  	 	 	 	 	 	 	 $xmin = min($xmin, $x);
23570  	 	 	 	 	 	 	 $ymin = min($ymin, $y);
23571  	 	 	 	 	 	 	 $xmax = max($xmax, $x);
23572  	 	 	 	 	 	 	 $ymax = max($ymax, $y);
23573  	 	 	 	 	 	 	 if ($relcoord) {
23574  	 	 	 	 	 	 	 	 $xoffset = $x;
23575  	 	 	 	 	 	 	 	 $yoffset = $y;
23576  	 	 	 	 	 	 	 }
23577  	 	 	 	 	 	 }
23578  	 	 	 	 	 }
23579  	 	 	 	 	 break;
23580  	 	 	 	 }
23581  	 	 	 	 case 'L': { // lineto
23582  	 	 	 	 	 foreach ($params as $ck => $cp) {
23583  	 	 	 	 	 	 if (($ck % 2) == 0) {
23584  	 	 	 	 	 	 	 $x = $cp + $xoffset;
23585  	 	 	 	 	 	 } else {
23586  	 	 	 	 	 	 	 $y = $cp + $yoffset;
23587  	 	 	 	 	 	 	 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23588  	 	 	 	 	 	 	 	 $this->_outLine($x, $y);
23589  	 	 	 	 	 	 	 	 $x0 = $x;
23590  	 	 	 	 	 	 	 	 $y0 = $y;
23591  	 	 	 	 	 	 	 }
23592  	 	 	 	 	 	 	 $xmin = min($xmin, $x);
23593  	 	 	 	 	 	 	 $ymin = min($ymin, $y);
23594  	 	 	 	 	 	 	 $xmax = max($xmax, $x);
23595  	 	 	 	 	 	 	 $ymax = max($ymax, $y);
23596  	 	 	 	 	 	 	 if ($relcoord) {
23597  	 	 	 	 	 	 	 	 $xoffset = $x;
23598  	 	 	 	 	 	 	 	 $yoffset = $y;
23599  	 	 	 	 	 	 	 }
23600  	 	 	 	 	 	 }
23601  	 	 	 	 	 }
23602  	 	 	 	 	 break;
23603  	 	 	 	 }
23604  	 	 	 	 case 'H': { // horizontal lineto
23605  	 	 	 	 	 foreach ($params as $ck => $cp) {
23606  	 	 	 	 	 	 $x = $cp + $xoffset;
23607  	 	 	 	 	 	 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23608  	 	 	 	 	 	 	 $this->_outLine($x, $y);
23609  	 	 	 	 	 	 	 $x0 = $x;
23610  	 	 	 	 	 	 	 $y0 = $y;
23611  	 	 	 	 	 	 }
23612  	 	 	 	 	 	 $xmin = min($xmin, $x);
23613  	 	 	 	 	 	 $xmax = max($xmax, $x);
23614  	 	 	 	 	 	 if ($relcoord) {
23615  	 	 	 	 	 	 	 $xoffset = $x;
23616  	 	 	 	 	 	 }
23617  	 	 	 	 	 }
23618  	 	 	 	 	 break;
23619  	 	 	 	 }
23620  	 	 	 	 case 'V': { // vertical lineto
23621  	 	 	 	 	 foreach ($params as $ck => $cp) {
23622  	 	 	 	 	 	 $y = $cp + $yoffset;
23623  	 	 	 	 	 	 if ((abs($x0 - $x) >= $minlen) OR (abs($y0 - $y) >= $minlen)) {
23624  	 	 	 	 	 	 	 $this->_outLine($x, $y);
23625  	 	 	 	 	 	 	 $x0 = $x;
23626  	 	 	 	 	 	 	 $y0 = $y;
23627  	 	 	 	 	 	 }
23628  	 	 	 	 	 	 $ymin = min($ymin, $y);
23629  	 	 	 	 	 	 $ymax = max($ymax, $y);
23630  	 	 	 	 	 	 if ($relcoord) {
23631  	 	 	 	 	 	 	 $yoffset = $y;
23632  	 	 	 	 	 	 }
23633  	 	 	 	 	 }
23634  	 	 	 	 	 break;
23635  	 	 	 	 }
23636  	 	 	 	 case 'C': { // curveto
23637  	 	 	 	 	 foreach ($params as $ck => $cp) {
23638  	 	 	 	 	 	 $params[$ck] = $cp;
23639  	 	 	 	 	 	 if ((($ck + 1) % 6) == 0) {
23640  	 	 	 	 	 	 	 $x1 = $params[($ck - 5)] + $xoffset;
23641  	 	 	 	 	 	 	 $y1 = $params[($ck - 4)] + $yoffset;
23642  	 	 	 	 	 	 	 $x2 = $params[($ck - 3)] + $xoffset;
23643  	 	 	 	 	 	 	 $y2 = $params[($ck - 2)] + $yoffset;
23644  	 	 	 	 	 	 	 $x = $params[($ck - 1)] + $xoffset;
23645  	 	 	 	 	 	 	 $y = $params[($ck)] + $yoffset;
23646  	 	 	 	 	 	 	 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23647  	 	 	 	 	 	 	 $xmin = min($xmin, $x, $x1, $x2);
23648  	 	 	 	 	 	 	 $ymin = min($ymin, $y, $y1, $y2);
23649  	 	 	 	 	 	 	 $xmax = max($xmax, $x, $x1, $x2);
23650  	 	 	 	 	 	 	 $ymax = max($ymax, $y, $y1, $y2);
23651  	 	 	 	 	 	 	 if ($relcoord) {
23652  	 	 	 	 	 	 	 	 $xoffset = $x;
23653  	 	 	 	 	 	 	 	 $yoffset = $y;
23654  	 	 	 	 	 	 	 }
23655  	 	 	 	 	 	 }
23656  	 	 	 	 	 }
23657  	 	 	 	 	 break;
23658  	 	 	 	 }
23659  	 	 	 	 case 'S': { // shorthand/smooth curveto
23660  	 	 	 	 	 foreach ($params as $ck => $cp) {
23661  	 	 	 	 	 	 $params[$ck] = $cp;
23662  	 	 	 	 	 	 if ((($ck + 1) % 4) == 0) {
23663  	 	 	 	 	 	 	 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'C') OR (strtoupper($paths[($key - 1)][1]) == 'S'))) {
23664  	 	 	 	 	 	 	 	 $x1 = (2 * $x) - $x2;
23665  	 	 	 	 	 	 	 	 $y1 = (2 * $y) - $y2;
23666  	 	 	 	 	 	 	 } else {
23667  	 	 	 	 	 	 	 	 $x1 = $x;
23668  	 	 	 	 	 	 	 	 $y1 = $y;
23669  	 	 	 	 	 	 	 }
23670  	 	 	 	 	 	 	 $x2 = $params[($ck - 3)] + $xoffset;
23671  	 	 	 	 	 	 	 $y2 = $params[($ck - 2)] + $yoffset;
23672  	 	 	 	 	 	 	 $x = $params[($ck - 1)] + $xoffset;
23673  	 	 	 	 	 	 	 $y = $params[($ck)] + $yoffset;
23674  	 	 	 	 	 	 	 $this->_outCurve($x1, $y1, $x2, $y2, $x, $y);
23675  	 	 	 	 	 	 	 $xmin = min($xmin, $x, $x1, $x2);
23676  	 	 	 	 	 	 	 $ymin = min($ymin, $y, $y1, $y2);
23677  	 	 	 	 	 	 	 $xmax = max($xmax, $x, $x1, $x2);
23678  	 	 	 	 	 	 	 $ymax = max($ymax, $y, $y1, $y2);
23679  	 	 	 	 	 	 	 if ($relcoord) {
23680  	 	 	 	 	 	 	 	 $xoffset = $x;
23681  	 	 	 	 	 	 	 	 $yoffset = $y;
23682  	 	 	 	 	 	 	 }
23683  	 	 	 	 	 	 }
23684  	 	 	 	 	 }
23685  	 	 	 	 	 break;
23686  	 	 	 	 }
23687  	 	 	 	 case 'Q': { // quadratic Bezier curveto
23688  	 	 	 	 	 foreach ($params as $ck => $cp) {
23689  	 	 	 	 	 	 $params[$ck] = $cp;
23690  	 	 	 	 	 	 if ((($ck + 1) % 4) == 0) {
23691  	 	 	 	 	 	 	 // convert quadratic points to cubic points
23692  	 	 	 	 	 	 	 $x1 = $params[($ck - 3)] + $xoffset;
23693  	 	 	 	 	 	 	 $y1 = $params[($ck - 2)] + $yoffset;
23694  	 	 	 	 	 	 	 $xa = ($x + (2 * $x1)) / 3;
23695  	 	 	 	 	 	 	 $ya = ($y + (2 * $y1)) / 3;
23696  	 	 	 	 	 	 	 $x = $params[($ck - 1)] + $xoffset;
23697  	 	 	 	 	 	 	 $y = $params[($ck)] + $yoffset;
23698  	 	 	 	 	 	 	 $xb = ($x + (2 * $x1)) / 3;
23699  	 	 	 	 	 	 	 $yb = ($y + (2 * $y1)) / 3;
23700  	 	 	 	 	 	 	 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23701  	 	 	 	 	 	 	 $xmin = min($xmin, $x, $xa, $xb);
23702  	 	 	 	 	 	 	 $ymin = min($ymin, $y, $ya, $yb);
23703  	 	 	 	 	 	 	 $xmax = max($xmax, $x, $xa, $xb);
23704  	 	 	 	 	 	 	 $ymax = max($ymax, $y, $ya, $yb);
23705  	 	 	 	 	 	 	 if ($relcoord) {
23706  	 	 	 	 	 	 	 	 $xoffset = $x;
23707  	 	 	 	 	 	 	 	 $yoffset = $y;
23708  	 	 	 	 	 	 	 }
23709  	 	 	 	 	 	 }
23710  	 	 	 	 	 }
23711  	 	 	 	 	 break;
23712  	 	 	 	 }
23713  	 	 	 	 case 'T': { // shorthand/smooth quadratic Bezier curveto
23714  	 	 	 	 	 foreach ($params as $ck => $cp) {
23715  	 	 	 	 	 	 $params[$ck] = $cp;
23716  	 	 	 	 	 	 if (($ck % 2) != 0) {
23717  	 	 	 	 	 	 	 if (($key > 0) AND ((strtoupper($paths[($key - 1)][1]) == 'Q') OR (strtoupper($paths[($key - 1)][1]) == 'T'))) {
23718  	 	 	 	 	 	 	 	 $x1 = (2 * $x) - $x1;
23719  	 	 	 	 	 	 	 	 $y1 = (2 * $y) - $y1;
23720  	 	 	 	 	 	 	 } else {
23721  	 	 	 	 	 	 	 	 $x1 = $x;
23722  	 	 	 	 	 	 	 	 $y1 = $y;
23723  	 	 	 	 	 	 	 }
23724  	 	 	 	 	 	 	 // convert quadratic points to cubic points
23725  	 	 	 	 	 	 	 $xa = ($x + (2 * $x1)) / 3;
23726  	 	 	 	 	 	 	 $ya = ($y + (2 * $y1)) / 3;
23727  	 	 	 	 	 	 	 $x = $params[($ck - 1)] + $xoffset;
23728  	 	 	 	 	 	 	 $y = $params[($ck)] + $yoffset;
23729  	 	 	 	 	 	 	 $xb = ($x + (2 * $x1)) / 3;
23730  	 	 	 	 	 	 	 $yb = ($y + (2 * $y1)) / 3;
23731  	 	 	 	 	 	 	 $this->_outCurve($xa, $ya, $xb, $yb, $x, $y);
23732  	 	 	 	 	 	 	 $xmin = min($xmin, $x, $xa, $xb);
23733  	 	 	 	 	 	 	 $ymin = min($ymin, $y, $ya, $yb);
23734  	 	 	 	 	 	 	 $xmax = max($xmax, $x, $xa, $xb);
23735  	 	 	 	 	 	 	 $ymax = max($ymax, $y, $ya, $yb);
23736  	 	 	 	 	 	 	 if ($relcoord) {
23737  	 	 	 	 	 	 	 	 $xoffset = $x;
23738  	 	 	 	 	 	 	 	 $yoffset = $y;
23739  	 	 	 	 	 	 	 }
23740  	 	 	 	 	 	 }
23741  	 	 	 	 	 }
23742  	 	 	 	 	 break;
23743  	 	 	 	 }
23744  	 	 	 	 case 'A': { // elliptical arc
23745  	 	 	 	 	 foreach ($params as $ck => $cp) {
23746  	 	 	 	 	 	 $params[$ck] = $cp;
23747  	 	 	 	 	 	 if ((($ck + 1) % 7) == 0) {
23748  	 	 	 	 	 	 	 $x0 = $x;
23749  	 	 	 	 	 	 	 $y0 = $y;
23750  	 	 	 	 	 	 	 $rx = max(abs($params[($ck - 6)]), .000000001);
23751  	 	 	 	 	 	 	 $ry = max(abs($params[($ck - 5)]), .000000001);
23752  	 	 	 	 	 	 	 $ang = -$rawparams[($ck - 4)];
23753  	 	 	 	 	 	 	 $angle = deg2rad($ang);
23754  	 	 	 	 	 	 	 $fa = $rawparams[($ck - 3)]; // large-arc-flag
23755  	 	 	 	 	 	 	 $fs = $rawparams[($ck - 2)]; // sweep-flag
23756  	 	 	 	 	 	 	 $x = $params[($ck - 1)] + $xoffset;
23757  	 	 	 	 	 	 	 $y = $params[$ck] + $yoffset;
23758  	 	 	 	 	 	 	 if ((abs($x0 - $x) < $minlen) AND (abs($y0 - $y) < $minlen)) {
23759  	 	 	 	 	 	 	 	 // endpoints are almost identical
23760  	 	 	 	 	 	 	 	 $xmin = min($xmin, $x);
23761  	 	 	 	 	 	 	 	 $ymin = min($ymin, $y);
23762  	 	 	 	 	 	 	 	 $xmax = max($xmax, $x);
23763  	 	 	 	 	 	 	 	 $ymax = max($ymax, $y);
23764  	 	 	 	 	 	 	 } else {
23765  	 	 	 	 	 	 	 	 $cos_ang = cos($angle);
23766  	 	 	 	 	 	 	 	 $sin_ang = sin($angle);
23767  	 	 	 	 	 	 	 	 $a = (($x0 - $x) / 2);
23768  	 	 	 	 	 	 	 	 $b = (($y0 - $y) / 2);
23769  	 	 	 	 	 	 	 	 $xa = ($a * $cos_ang) - ($b * $sin_ang);
23770  	 	 	 	 	 	 	 	 $ya = ($a * $sin_ang) + ($b * $cos_ang);
23771  	 	 	 	 	 	 	 	 $rx2 = $rx * $rx;
23772  	 	 	 	 	 	 	 	 $ry2 = $ry * $ry;
23773  	 	 	 	 	 	 	 	 $xa2 = $xa * $xa;
23774  	 	 	 	 	 	 	 	 $ya2 = $ya * $ya;
23775  	 	 	 	 	 	 	 	 $delta = ($xa2 / $rx2) + ($ya2 / $ry2);
23776  	 	 	 	 	 	 	 	 if ($delta > 1) {
23777  	 	 	 	 	 	 	 	 	 $rx *= sqrt($delta);
23778  	 	 	 	 	 	 	 	 	 $ry *= sqrt($delta);
23779  	 	 	 	 	 	 	 	 	 $rx2 = $rx * $rx;
23780  	 	 	 	 	 	 	 	 	 $ry2 = $ry * $ry;
23781  	 	 	 	 	 	 	 	 }
23782  	 	 	 	 	 	 	 	 $numerator = (($rx2 * $ry2) - ($rx2 * $ya2) - ($ry2 * $xa2));
23783  	 	 	 	 	 	 	 	 if ($numerator < 0) {
23784  	 	 	 	 	 	 	 	 	 $root = 0;
23785  	 	 	 	 	 	 	 	 } else {
23786  	 	 	 	 	 	 	 	 	 $root = sqrt($numerator / (($rx2 * $ya2) + ($ry2 * $xa2)));
23787  	 	 	 	 	 	 	 	 }
23788  	 	 	 	 	 	 	 	 if ($fa == $fs){
23789  	 	 	 	 	 	 	 	 	 $root *= -1;
23790  	 	 	 	 	 	 	 	 }
23791  	 	 	 	 	 	 	 	 $cax = $root * (($rx * $ya) / $ry);
23792  	 	 	 	 	 	 	 	 $cay = -$root * (($ry * $xa) / $rx);
23793  	 	 	 	 	 	 	 	 // coordinates of ellipse center
23794  	 	 	 	 	 	 	 	 $cx = ($cax * $cos_ang) - ($cay * $sin_ang) + (($x0 + $x) / 2);
23795  	 	 	 	 	 	 	 	 $cy = ($cax * $sin_ang) + ($cay * $cos_ang) + (($y0 + $y) / 2);
23796  	 	 	 	 	 	 	 	 // get angles
23797  	 	 	 	 	 	 	 	 $angs = TCPDF_STATIC::getVectorsAngle(1, 0, (($xa - $cax) / $rx), (($cay - $ya) / $ry));
23798  	 	 	 	 	 	 	 	 $dang = TCPDF_STATIC::getVectorsAngle((($xa - $cax) / $rx), (($ya - $cay) / $ry), ((-$xa - $cax) / $rx), ((-$ya - $cay) / $ry));
23799  	 	 	 	 	 	 	 	 if (($fs == 0) AND ($dang > 0)) {
23800  	 	 	 	 	 	 	 	 	 $dang -= (2 * M_PI);
23801  	 	 	 	 	 	 	 	 } elseif (($fs == 1) AND ($dang < 0)) {
23802  	 	 	 	 	 	 	 	 	 $dang += (2 * M_PI);
23803  	 	 	 	 	 	 	 	 }
23804  	 	 	 	 	 	 	 	 $angf = $angs - $dang;
23805  	 	 	 	 	 	 	 	 if ((($fs == 0) AND ($angs > $angf)) OR (($fs == 1) AND ($angs < $angf))) {
23806  	 	 	 	 	 	 	 	 	 // reverse angles
23807  	 	 	 	 	 	 	 	 	 $tmp = $angs;
23808  	 	 	 	 	 	 	 	 	 $angs = $angf;
23809  	 	 	 	 	 	 	 	 	 $angf = $tmp;
23810  	 	 	 	 	 	 	 	 }
23811  	 	 	 	 	 	 	 	 $angs = round(rad2deg($angs), 6);
23812  	 	 	 	 	 	 	 	 $angf = round(rad2deg($angf), 6);
23813  	 	 	 	 	 	 	 	 // covent angles to positive values
23814  	 	 	 	 	 	 	 	 if (($angs < 0) AND ($angf < 0)) {
23815  	 	 	 	 	 	 	 	 	 $angs += 360;
23816  	 	 	 	 	 	 	 	 	 $angf += 360;
23817  	 	 	 	 	 	 	 	 }
23818  	 	 	 	 	 	 	 	 $pie = false;
23819  	 	 	 	 	 	 	 	 if (($key == 0) AND (isset($paths[($key + 1)][1])) AND (trim($paths[($key + 1)][1]) == 'z')) {
23820  	 	 	 	 	 	 	 	 	 $pie = true;
23821  	 	 	 	 	 	 	 	 }
23822  	 	 	 	 	 	 	 	 list($axmin, $aymin, $axmax, $aymax) = $this->_outellipticalarc($cx, $cy, $rx, $ry, $ang, $angs, $angf, $pie, 2, false, ($fs == 0), true);
23823  	 	 	 	 	 	 	 	 $xmin = min($xmin, $x, $axmin);
23824  	 	 	 	 	 	 	 	 $ymin = min($ymin, $y, $aymin);
23825  	 	 	 	 	 	 	 	 $xmax = max($xmax, $x, $axmax);
23826  	 	 	 	 	 	 	 	 $ymax = max($ymax, $y, $aymax);
23827  	 	 	 	 	 	 	 }
23828  	 	 	 	 	 	 	 if ($relcoord) {
23829  	 	 	 	 	 	 	 	 $xoffset = $x;
23830  	 	 	 	 	 	 	 	 $yoffset = $y;
23831  	 	 	 	 	 	 	 }
23832  	 	 	 	 	 	 }
23833  	 	 	 	 	 }
23834  	 	 	 	 	 break;
23835  	 	 	 	 }
23836  	 	 	 	 case 'Z': {
23837  	 	 	 	 	 $this->_out('h');
23838  	 	 	 	 	 $x = $x0 = $xinitial;
23839  	 	 	 	 	 $y = $y0 = $yinitial;
23840  	 	 	 	 	 break;
23841  	 	 	 	 }
23842  	 	 	 }
23843  	 	 	 $firstcmd = false;
23844  	 	 } // end foreach
23845  	 	 if (!empty($op)) {
23846  	 	 	 $this->_out($op);
23847  	 	 }
23848  	 	 return array($xmin, $ymin, ($xmax - $xmin), ($ymax - $ymin));
23849  	 }
23850  
23851  	 /**
23852  	  * Return the tag name without the namespace
23853  	  * @param $name (string) Tag name
23854  	  * @protected
23855  	  */
23856  	protected function removeTagNamespace($name) {
23857  	 	 if(strpos($name, ':') !== false) {
23858  	 	 	 $parts = explode(':', $name);
23859  	 	 	 return $parts[(sizeof($parts) - 1)];
23860  	 	 }
23861  	 	 return $name;
23862  	 }
23863  
23864  	 /**
23865  	  * Sets the opening SVG element handler function for the XML parser. (*** TO BE COMPLETED ***)
23866  	  * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
23867  	  * @param $name (string) 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.
23868  	  * @param $attribs (array) 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.
23869  	  * @param $ctm (array) tranformation matrix for clipping mode (starting transformation matrix).
23870  	  * @author Nicola Asuni
23871  	  * @since 5.0.000 (2010-05-02)
23872  	  * @protected
23873  	  */
23874  	protected function startSVGElementHandler($parser, $name, $attribs, $ctm=array()) {
23875  	 	 $name = $this->removeTagNamespace($name);
23876  	 	 // check if we are in clip mode
23877  	 	 if ($this->svgclipmode) {
23878  	 	 	 $this->svgclippaths[$this->svgclipid][] = array('name' => $name, 'attribs' => $attribs, 'tm' => $this->svgcliptm[$this->svgclipid]);
23879  	 	 	 return;
23880  	 	 }
23881  	 	 if ($this->svgdefsmode AND !in_array($name, array('clipPath', 'linearGradient', 'radialGradient', 'stop'))) {
23882  	 	 	 if (isset($attribs['id'])) {
23883  	 	 	 	 $attribs['child_elements'] = array();
23884  	 	 	 	 $this->svgdefs[$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23885  	 	 	 	 return;
23886  	 	 	 }
23887  	 	 	 if (end($this->svgdefs) !== FALSE) {
23888  	 	 	 	 $last_svgdefs_id = key($this->svgdefs);
23889  	 	 	 	 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
23890  	 	 	 	 	 $attribs['id'] = 'DF_'.(count($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements']) + 1);
23891  	 	 	 	 	 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$attribs['id']] = array('name' => $name, 'attribs' => $attribs);
23892  	 	 	 	 	 return;
23893  	 	 	 	 }
23894  	 	 	 }
23895  	 	 	 return;
23896  	 	 }
23897  	 	 $clipping = false;
23898  	 	 if ($parser == 'clip-path') {
23899  	 	 	 // set clipping mode
23900  	 	 	 $clipping = true;
23901  	 	 }
23902  	 	 // get styling properties
23903  	 	 $prev_svgstyle = $this->svgstyles[max(0,(count($this->svgstyles) - 1))]; // previous style
23904  	 	 $svgstyle = $this->svgstyles[0]; // set default style
23905  	 	 if ($clipping AND !isset($attribs['fill']) AND (!isset($attribs['style']) OR (!preg_match('/[;\"\s]{1}fill[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval)))) {
23906  	 	 	 // default fill attribute for clipping
23907  	 	 	 $attribs['fill'] = 'none';
23908  	 	 }
23909  	 	 if (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style']) AND ($attribs['style'][0] != ';')) {
23910  	 	 	 // fix style for regular expression
23911  	 	 	 $attribs['style'] = ';'.$attribs['style'];
23912  	 	 }
23913  	 	 foreach ($prev_svgstyle as $key => $val) {
23914  	 	 	 if (in_array($key, TCPDF_IMAGES::$svginheritprop)) {
23915  	 	 	 	 // inherit previous value
23916  	 	 	 	 $svgstyle[$key] = $val;
23917  	 	 	 }
23918  	 	 	 if (isset($attribs[$key]) AND !TCPDF_STATIC::empty_string($attribs[$key])) {
23919  	 	 	 	 // specific attribute settings
23920  	 	 	 	 if ($attribs[$key] == 'inherit') {
23921  	 	 	 	 	 $svgstyle[$key] = $val;
23922  	 	 	 	 } else {
23923  	 	 	 	 	 $svgstyle[$key] = $attribs[$key];
23924  	 	 	 	 }
23925  	 	 	 } elseif (isset($attribs['style']) AND !TCPDF_STATIC::empty_string($attribs['style'])) {
23926  	 	 	 	 // CSS style syntax
23927  	 	 	 	 $attrval = array();
23928  	 	 	 	 if (preg_match('/[;\"\s]{1}'.$key.'[\s]*:[\s]*([^;\"]*)/si', $attribs['style'], $attrval) AND isset($attrval[1])) {
23929  	 	 	 	 	 if ($attrval[1] == 'inherit') {
23930  	 	 	 	 	 	 $svgstyle[$key] = $val;
23931  	 	 	 	 	 } else {
23932  	 	 	 	 	 	 $svgstyle[$key] = $attrval[1];
23933  	 	 	 	 	 }
23934  	 	 	 	 }
23935  	 	 	 }
23936  	 	 }
23937  	 	 // transformation matrix
23938  	 	 if (!empty($ctm)) {
23939  	 	 	 $tm = $ctm;
23940  	 	 } else {
23941  	 	 	 $tm = array(1,0,0,1,0,0);
23942  	 	 }
23943  	 	 if (isset($attribs['transform']) AND !empty($attribs['transform'])) {
23944  	 	 	 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, TCPDF_STATIC::getSVGTransformMatrix($attribs['transform']));
23945  	 	 }
23946  	 	 $svgstyle['transfmatrix'] = $tm;
23947  	 	 $invisible = false;
23948  	 	 if (($svgstyle['visibility'] == 'hidden') OR ($svgstyle['visibility'] == 'collapse') OR ($svgstyle['display'] == 'none')) {
23949  	 	 	 // the current graphics element is invisible (nothing is painted)
23950  	 	 	 $invisible = true;
23951  	 	 }
23952  	 	 // process tag
23953  	 	 switch($name) {
23954  	 	 	 case 'defs': {
23955  	 	 	 	 $this->svgdefsmode = true;
23956  	 	 	 	 break;
23957  	 	 	 }
23958  	 	 	 // clipPath
23959  	 	 	 case 'clipPath': {
23960  	 	 	 	 if ($invisible) {
23961  	 	 	 	 	 break;
23962  	 	 	 	 }
23963  	 	 	 	 $this->svgclipmode = true;
23964  	 	 	 	 if (!isset($attribs['id'])) {
23965  	 	 	 	 	 $attribs['id'] = 'CP_'.(count($this->svgcliptm) + 1);
23966  	 	 	 	 }
23967  	 	 	 	 $this->svgclipid = $attribs['id'];
23968  	 	 	 	 $this->svgclippaths[$this->svgclipid] = array();
23969  	 	 	 	 $this->svgcliptm[$this->svgclipid] = $tm;
23970  	 	 	 	 break;
23971  	 	 	 }
23972  	 	 	 case 'svg': {
23973  	 	 	 	 // start of SVG object
23974  	 	 	 	 if(++$this->svg_tag_depth <= 1) {
23975  	 	 	 	 	 break;
23976  	 	 	 	 }
23977  	 	 	 	 // inner SVG
23978  	 	 	 	 array_push($this->svgstyles, $svgstyle);
23979  	 	 	 	 $this->StartTransform();
23980  	 	 	 	 $svgX = (isset($attribs['x'])?$attribs['x']:0);
23981  	 	 	 	 $svgY = (isset($attribs['y'])?$attribs['y']:0);
23982  	 	 	 	 $svgW = (isset($attribs['width'])?$attribs['width']:0);
23983  	 	 	 	 $svgH = (isset($attribs['height'])?$attribs['height']:0);
23984  	 	 	 	 // set x, y position using transform matrix
23985  	 	 	 	 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array( 1, 0, 0, 1, $svgX, $svgY));
23986  	 	 	 	 $this->SVGTransform($tm);
23987  	 	 	 	 // set clipping for width and height
23988  	 	 	 	 $x = 0;
23989  	 	 	 	 $y = 0;
23990  	 	 	 	 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):$this->w);
23991  	 	 	 	 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):$this->h);
23992  	 	 	 	 // draw clipping rect
23993  	 	 	 	 $this->Rect($x, $y, $w, $h, 'CNZ', array(), array());
23994  	 	 	 	 // parse viewbox, calculate extra transformation matrix
23995  	 	 	 	 if (isset($attribs['viewBox'])) {
23996  	 	 	 	 	 $tmp = array();
23997  	 	 	 	 	 preg_match_all("/[0-9]+/", $attribs['viewBox'], $tmp);
23998  	 	 	 	 	 $tmp = $tmp[0];
23999  	 	 	 	 	 if (sizeof($tmp) == 4) {
24000  	 	 	 	 	 	 $vx = $tmp[0];
24001  	 	 	 	 	 	 $vy = $tmp[1];
24002  	 	 	 	 	 	 $vw = $tmp[2];
24003  	 	 	 	 	 	 $vh = $tmp[3];
24004  	 	 	 	 	 	 // get aspect ratio
24005  	 	 	 	 	 	 $tmp = array();
24006  	 	 	 	 	 	 $aspectX = 'xMid';
24007  	 	 	 	 	 	 $aspectY = 'YMid';
24008  	 	 	 	 	 	 $fit = 'meet';
24009  	 	 	 	 	 	 if (isset($attribs['preserveAspectRatio'])) {
24010  	 	 	 	 	 	 	 if($attribs['preserveAspectRatio'] == 'none') {
24011  	 	 	 	 	 	 	 	 $fit = 'none';
24012  	 	 	 	 	 	 	 } else {
24013  	 	 	 	 	 	 	 	 preg_match_all('/[a-zA-Z]+/', $attribs['preserveAspectRatio'], $tmp);
24014  	 	 	 	 	 	 	 	 $tmp = $tmp[0];
24015  	 	 	 	 	 	 	 	 if ((sizeof($tmp) == 2) AND (strlen($tmp[0]) == 8) AND (in_array($tmp[1], array('meet', 'slice', 'none')))) {
24016  	 	 	 	 	 	 	 	 	 $aspectX = substr($tmp[0], 0, 4);
24017  	 	 	 	 	 	 	 	 	 $aspectY = substr($tmp[0], 4, 4);
24018  	 	 	 	 	 	 	 	 	 $fit = $tmp[1];
24019  	 	 	 	 	 	 	 	 }
24020  	 	 	 	 	 	 	 }
24021  	 	 	 	 	 	 }
24022  	 	 	 	 	 	 $wr = ($svgW / $vw);
24023  	 	 	 	 	 	 $hr = ($svgH / $vh);
24024  	 	 	 	 	 	 $ax = $ay = 0;
24025  	 	 	 	 	 	 if ((($fit == 'meet') AND ($hr < $wr)) OR (($fit == 'slice') AND ($hr > $wr))) {
24026  	 	 	 	 	 	 	 if ($aspectX == 'xMax') {
24027  	 	 	 	 	 	 	 	 $ax = (($vw * ($wr / $hr)) - $vw);
24028  	 	 	 	 	 	 	 }
24029  	 	 	 	 	 	 	 if ($aspectX == 'xMid') {
24030  	 	 	 	 	 	 	 	 $ax = ((($vw * ($wr / $hr)) - $vw) / 2);
24031  	 	 	 	 	 	 	 }
24032  	 	 	 	 	 	 	 $wr = $hr;
24033  	 	 	 	 	 	 } elseif ((($fit == 'meet') AND ($hr > $wr)) OR (($fit == 'slice') AND ($hr < $wr))) {
24034  	 	 	 	 	 	 	 if ($aspectY == 'YMax') {
24035  	 	 	 	 	 	 	 	 $ay = (($vh * ($hr / $wr)) - $vh);
24036  	 	 	 	 	 	 	 }
24037  	 	 	 	 	 	 	 if ($aspectY == 'YMid') {
24038  	 	 	 	 	 	 	 	 $ay = ((($vh * ($hr / $wr)) - $vh) / 2);
24039  	 	 	 	 	 	 	 }
24040  	 	 	 	 	 	 	 $hr = $wr;
24041  	 	 	 	 	 	 }
24042  	 	 	 	 	 	 $newtm = array($wr, 0, 0, $hr, (($wr * ($ax - $vx)) - $svgX), (($hr * ($ay - $vy)) - $svgY));
24043  	 	 	 	 	 	 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, $newtm);
24044  	 	 	 	 	 	 $this->SVGTransform($tm);
24045  	 	 	 	 	 }
24046  	 	 	 	 }
24047  	 	 	 	 $this->setSVGStyles($svgstyle, $prev_svgstyle);
24048  	 	 	 	 break;
24049  	 	 	 }
24050  	 	 	 case 'g': {
24051  	 	 	 	 // group together related graphics elements
24052  	 	 	 	 array_push($this->svgstyles, $svgstyle);
24053  	 	 	 	 $this->StartTransform();
24054  	 	 	 	 $x = (isset($attribs['x'])?$attribs['x']:0);
24055  	 	 	 	 $y = (isset($attribs['y'])?$attribs['y']:0);
24056  	 	 	 	 $w = 1;//(isset($attribs['width'])?$attribs['width']:1);
24057  	 	 	 	 $h = 1;//(isset($attribs['height'])?$attribs['height']:1);
24058  	 	 	 	 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24059  	 	 	 	 $this->SVGTransform($tm);
24060  	 	 	 	 $this->setSVGStyles($svgstyle, $prev_svgstyle);
24061  	 	 	 	 break;
24062  	 	 	 }
24063  	 	 	 case 'linearGradient': {
24064  	 	 	 	 if ($this->pdfa_mode) {
24065  	 	 	 	 	 break;
24066  	 	 	 	 }
24067  	 	 	 	 if (!isset($attribs['id'])) {
24068  	 	 	 	 	 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
24069  	 	 	 	 }
24070  	 	 	 	 $this->svggradientid = $attribs['id'];
24071  	 	 	 	 $this->svggradients[$this->svggradientid] = array();
24072  	 	 	 	 $this->svggradients[$this->svggradientid]['type'] = 2;
24073  	 	 	 	 $this->svggradients[$this->svggradientid]['stops'] = array();
24074  	 	 	 	 if (isset($attribs['gradientUnits'])) {
24075  	 	 	 	 	 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
24076  	 	 	 	 } else {
24077  	 	 	 	 	 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
24078  	 	 	 	 }
24079  	 	 	 	 //$attribs['spreadMethod']
24080  	 	 	 	 if (((!isset($attribs['x1'])) AND (!isset($attribs['y1'])) AND (!isset($attribs['x2'])) AND (!isset($attribs['y2'])))
24081  	 	 	 	 	 OR ((isset($attribs['x1']) AND (substr($attribs['x1'], -1) == '%'))
24082  	 	 	 	 	 	 OR (isset($attribs['y1']) AND (substr($attribs['y1'], -1) == '%'))
24083  	 	 	 	 	 	 OR (isset($attribs['x2']) AND (substr($attribs['x2'], -1) == '%'))
24084  	 	 	 	 	 	 OR (isset($attribs['y2']) AND (substr($attribs['y2'], -1) == '%')))) {
24085  	 	 	 	 	 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
24086  	 	 	 	 } else {
24087  	 	 	 	 	 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
24088  	 	 	 	 }
24089  	 	 	 	 $x1 = (isset($attribs['x1'])?$attribs['x1']:'0');
24090  	 	 	 	 $y1 = (isset($attribs['y1'])?$attribs['y1']:'0');
24091  	 	 	 	 $x2 = (isset($attribs['x2'])?$attribs['x2']:'100');
24092  	 	 	 	 $y2 = (isset($attribs['y2'])?$attribs['y2']:'0');
24093  	 	 	 	 if (isset($attribs['gradientTransform'])) {
24094  	 	 	 	 	 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
24095  	 	 	 	 }
24096  	 	 	 	 $this->svggradients[$this->svggradientid]['coords'] = array($x1, $y1, $x2, $y2);
24097  	 	 	 	 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24098  	 	 	 	 	 // gradient is defined on another place
24099  	 	 	 	 	 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
24100  	 	 	 	 }
24101  	 	 	 	 break;
24102  	 	 	 }
24103  	 	 	 case 'radialGradient': {
24104  	 	 	 	 if ($this->pdfa_mode) {
24105  	 	 	 	 	 break;
24106  	 	 	 	 }
24107  	 	 	 	 if (!isset($attribs['id'])) {
24108  	 	 	 	 	 $attribs['id'] = 'GR_'.(count($this->svggradients) + 1);
24109  	 	 	 	 }
24110  	 	 	 	 $this->svggradientid = $attribs['id'];
24111  	 	 	 	 $this->svggradients[$this->svggradientid] = array();
24112  	 	 	 	 $this->svggradients[$this->svggradientid]['type'] = 3;
24113  	 	 	 	 $this->svggradients[$this->svggradientid]['stops'] = array();
24114  	 	 	 	 if (isset($attribs['gradientUnits'])) {
24115  	 	 	 	 	 $this->svggradients[$this->svggradientid]['gradientUnits'] = $attribs['gradientUnits'];
24116  	 	 	 	 } else {
24117  	 	 	 	 	 $this->svggradients[$this->svggradientid]['gradientUnits'] = 'objectBoundingBox';
24118  	 	 	 	 }
24119  	 	 	 	 //$attribs['spreadMethod']
24120  	 	 	 	 if (((!isset($attribs['cx'])) AND (!isset($attribs['cy'])))
24121  	 	 	 	 	 OR ((isset($attribs['cx']) AND (substr($attribs['cx'], -1) == '%'))
24122  	 	 	 	 	 OR (isset($attribs['cy']) AND (substr($attribs['cy'], -1) == '%')))) {
24123  	 	 	 	 	 $this->svggradients[$this->svggradientid]['mode'] = 'percentage';
24124  	 	 	 	 } elseif (isset($attribs['r']) AND is_numeric($attribs['r']) AND ($attribs['r']) <= 1) {
24125  	 	 	 	 	 $this->svggradients[$this->svggradientid]['mode'] = 'ratio';
24126  	 	 	 	 } else {
24127  	 	 	 	 	 $this->svggradients[$this->svggradientid]['mode'] = 'measure';
24128  	 	 	 	 }
24129  	 	 	 	 $cx = (isset($attribs['cx']) ? $attribs['cx'] : 0.5);
24130  	 	 	 	 $cy = (isset($attribs['cy']) ? $attribs['cy'] : 0.5);
24131  	 	 	 	 $fx = (isset($attribs['fx']) ? $attribs['fx'] : $cx);
24132  	 	 	 	 $fy = (isset($attribs['fy']) ? $attribs['fy'] : $cy);
24133  	 	 	 	 $r = (isset($attribs['r']) ? $attribs['r'] : 0.5);
24134  	 	 	 	 if (isset($attribs['gradientTransform'])) {
24135  	 	 	 	 	 $this->svggradients[$this->svggradientid]['gradientTransform'] = TCPDF_STATIC::getSVGTransformMatrix($attribs['gradientTransform']);
24136  	 	 	 	 }
24137  	 	 	 	 $this->svggradients[$this->svggradientid]['coords'] = array($cx, $cy, $fx, $fy, $r);
24138  	 	 	 	 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24139  	 	 	 	 	 // gradient is defined on another place
24140  	 	 	 	 	 $this->svggradients[$this->svggradientid]['xref'] = substr($attribs['xlink:href'], 1);
24141  	 	 	 	 }
24142  	 	 	 	 break;
24143  	 	 	 }
24144  	 	 	 case 'stop': {
24145  	 	 	 	 // gradient stops
24146  	 	 	 	 if (substr($attribs['offset'], -1) == '%') {
24147  	 	 	 	 	 $offset = floatval(substr($attribs['offset'], 0, -1)) / 100;
24148  	 	 	 	 } else {
24149  	 	 	 	 	 $offset = floatval($attribs['offset']);
24150  	 	 	 	 	 if ($offset > 1) {
24151  	 	 	 	 	 	 $offset /= 100;
24152  	 	 	 	 	 }
24153  	 	 	 	 }
24154  	 	 	 	 $stop_color = isset($svgstyle['stop-color'])?TCPDF_COLORS::convertHTMLColorToDec($svgstyle['stop-color'], $this->spot_colors):'black';
24155  	 	 	 	 $opacity = isset($svgstyle['stop-opacity'])?$svgstyle['stop-opacity']:1;
24156  	 	 	 	 $this->svggradients[$this->svggradientid]['stops'][] = array('offset' => $offset, 'color' => $stop_color, 'opacity' => $opacity);
24157  	 	 	 	 break;
24158  	 	 	 }
24159  	 	 	 // paths
24160  	 	 	 case 'path': {
24161  	 	 	 	 if ($invisible) {
24162  	 	 	 	 	 break;
24163  	 	 	 	 }
24164  	 	 	 	 if (isset($attribs['d'])) {
24165  	 	 	 	 	 $d = trim($attribs['d']);
24166  	 	 	 	 	 if (!empty($d)) {
24167  	 	 	 	 	 	 $x = (isset($attribs['x'])?$attribs['x']:0);
24168  	 	 	 	 	 	 $y = (isset($attribs['y'])?$attribs['y']:0);
24169  	 	 	 	 	 	 $w = (isset($attribs['width'])?$attribs['width']:1);
24170  	 	 	 	 	 	 $h = (isset($attribs['height'])?$attribs['height']:1);
24171  	 	 	 	 	 	 $tm = TCPDF_STATIC::getTransformationMatrixProduct($tm, array($w, 0, 0, $h, $x, $y));
24172  	 	 	 	 	 	 if ($clipping) {
24173  	 	 	 	 	 	 	 $this->SVGTransform($tm);
24174  	 	 	 	 	 	 	 $this->SVGPath($d, 'CNZ');
24175  	 	 	 	 	 	 } else {
24176  	 	 	 	 	 	 	 $this->StartTransform();
24177  	 	 	 	 	 	 	 $this->SVGTransform($tm);
24178  	 	 	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'SVGPath', array($d, 'CNZ'));
24179  	 	 	 	 	 	 	 if (!empty($obstyle)) {
24180  	 	 	 	 	 	 	 	 $this->SVGPath($d, $obstyle);
24181  	 	 	 	 	 	 	 }
24182  	 	 	 	 	 	 	 $this->StopTransform();
24183  	 	 	 	 	 	 }
24184  	 	 	 	 	 }
24185  	 	 	 	 }
24186  	 	 	 	 break;
24187  	 	 	 }
24188  	 	 	 // shapes
24189  	 	 	 case 'rect': {
24190  	 	 	 	 if ($invisible) {
24191  	 	 	 	 	 break;
24192  	 	 	 	 }
24193  	 	 	 	 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24194  	 	 	 	 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24195  	 	 	 	 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24196  	 	 	 	 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24197  	 	 	 	 $rx = (isset($attribs['rx'])?$this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false):0);
24198  	 	 	 	 $ry = (isset($attribs['ry'])?$this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false):$rx);
24199  	 	 	 	 if ($clipping) {
24200  	 	 	 	 	 $this->SVGTransform($tm);
24201  	 	 	 	 	 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ', array(), array());
24202  	 	 	 	 } else {
24203  	 	 	 	 	 $this->StartTransform();
24204  	 	 	 	 	 $this->SVGTransform($tm);
24205  	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'RoundedRectXY', array($x, $y, $w, $h, $rx, $ry, '1111', 'CNZ'));
24206  	 	 	 	 	 if (!empty($obstyle)) {
24207  	 	 	 	 	 	 $this->RoundedRectXY($x, $y, $w, $h, $rx, $ry, '1111', $obstyle, array(), array());
24208  	 	 	 	 	 }
24209  	 	 	 	 	 $this->StopTransform();
24210  	 	 	 	 }
24211  	 	 	 	 break;
24212  	 	 	 }
24213  	 	 	 case 'circle': {
24214  	 	 	 	 if ($invisible) {
24215  	 	 	 	 	 break;
24216  	 	 	 	 }
24217  	 	 	 	 $r = (isset($attribs['r']) ? $this->getHTMLUnitToUnits($attribs['r'], 0, $this->svgunit, false) : 0);
24218  	 	 	 	 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24219  	 	 	 	 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24220  	 	 	 	 $x = ($cx - $r);
24221  	 	 	 	 $y = ($cy - $r);
24222  	 	 	 	 $w = (2 * $r);
24223  	 	 	 	 $h = $w;
24224  	 	 	 	 if ($clipping) {
24225  	 	 	 	 	 $this->SVGTransform($tm);
24226  	 	 	 	 	 $this->Circle($cx, $cy, $r, 0, 360, 'CNZ', array(), array(), 8);
24227  	 	 	 	 } else {
24228  	 	 	 	 	 $this->StartTransform();
24229  	 	 	 	 	 $this->SVGTransform($tm);
24230  	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Circle', array($cx, $cy, $r, 0, 360, 'CNZ'));
24231  	 	 	 	 	 if (!empty($obstyle)) {
24232  	 	 	 	 	 	 $this->Circle($cx, $cy, $r, 0, 360, $obstyle, array(), array(), 8);
24233  	 	 	 	 	 }
24234  	 	 	 	 	 $this->StopTransform();
24235  	 	 	 	 }
24236  	 	 	 	 break;
24237  	 	 	 }
24238  	 	 	 case 'ellipse': {
24239  	 	 	 	 if ($invisible) {
24240  	 	 	 	 	 break;
24241  	 	 	 	 }
24242  	 	 	 	 $rx = (isset($attribs['rx']) ? $this->getHTMLUnitToUnits($attribs['rx'], 0, $this->svgunit, false) : 0);
24243  	 	 	 	 $ry = (isset($attribs['ry']) ? $this->getHTMLUnitToUnits($attribs['ry'], 0, $this->svgunit, false) : 0);
24244  	 	 	 	 $cx = (isset($attribs['cx']) ? $this->getHTMLUnitToUnits($attribs['cx'], 0, $this->svgunit, false) : (isset($attribs['x']) ? $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false) : 0));
24245  	 	 	 	 $cy = (isset($attribs['cy']) ? $this->getHTMLUnitToUnits($attribs['cy'], 0, $this->svgunit, false) : (isset($attribs['y']) ? $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false) : 0));
24246  	 	 	 	 $x = ($cx - $rx);
24247  	 	 	 	 $y = ($cy - $ry);
24248  	 	 	 	 $w = (2 * $rx);
24249  	 	 	 	 $h = (2 * $ry);
24250  	 	 	 	 if ($clipping) {
24251  	 	 	 	 	 $this->SVGTransform($tm);
24252  	 	 	 	 	 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ', array(), array(), 8);
24253  	 	 	 	 } else {
24254  	 	 	 	 	 $this->StartTransform();
24255  	 	 	 	 	 $this->SVGTransform($tm);
24256  	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Ellipse', array($cx, $cy, $rx, $ry, 0, 0, 360, 'CNZ'));
24257  	 	 	 	 	 if (!empty($obstyle)) {
24258  	 	 	 	 	 	 $this->Ellipse($cx, $cy, $rx, $ry, 0, 0, 360, $obstyle, array(), array(), 8);
24259  	 	 	 	 	 }
24260  	 	 	 	 	 $this->StopTransform();
24261  	 	 	 	 }
24262  	 	 	 	 break;
24263  	 	 	 }
24264  	 	 	 case 'line': {
24265  	 	 	 	 if ($invisible) {
24266  	 	 	 	 	 break;
24267  	 	 	 	 }
24268  	 	 	 	 $x1 = (isset($attribs['x1'])?$this->getHTMLUnitToUnits($attribs['x1'], 0, $this->svgunit, false):0);
24269  	 	 	 	 $y1 = (isset($attribs['y1'])?$this->getHTMLUnitToUnits($attribs['y1'], 0, $this->svgunit, false):0);
24270  	 	 	 	 $x2 = (isset($attribs['x2'])?$this->getHTMLUnitToUnits($attribs['x2'], 0, $this->svgunit, false):0);
24271  	 	 	 	 $y2 = (isset($attribs['y2'])?$this->getHTMLUnitToUnits($attribs['y2'], 0, $this->svgunit, false):0);
24272  	 	 	 	 $x = $x1;
24273  	 	 	 	 $y = $y1;
24274  	 	 	 	 $w = abs($x2 - $x1);
24275  	 	 	 	 $h = abs($y2 - $y1);
24276  	 	 	 	 if (!$clipping) {
24277  	 	 	 	 	 $this->StartTransform();
24278  	 	 	 	 	 $this->SVGTransform($tm);
24279  	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Line', array($x1, $y1, $x2, $y2));
24280  	 	 	 	 	 $this->Line($x1, $y1, $x2, $y2);
24281  	 	 	 	 	 $this->StopTransform();
24282  	 	 	 	 }
24283  	 	 	 	 break;
24284  	 	 	 }
24285  	 	 	 case 'polyline':
24286  	 	 	 case 'polygon': {
24287  	 	 	 	 if ($invisible) {
24288  	 	 	 	 	 break;
24289  	 	 	 	 }
24290  	 	 	 	 $points = (isset($attribs['points'])?$attribs['points']:'0 0');
24291  	 	 	 	 $points = trim($points);
24292  	 	 	 	 // note that point may use a complex syntax not covered here
24293  	 	 	 	 $points = preg_split('/[\,\s]+/si', $points);
24294  	 	 	 	 if (count($points) < 4) {
24295  	 	 	 	 	 break;
24296  	 	 	 	 }
24297  	 	 	 	 $p = array();
24298  	 	 	 	 $xmin = 2147483647;
24299  	 	 	 	 $xmax = 0;
24300  	 	 	 	 $ymin = 2147483647;
24301  	 	 	 	 $ymax = 0;
24302  	 	 	 	 foreach ($points as $key => $val) {
24303  	 	 	 	 	 $p[$key] = $this->getHTMLUnitToUnits($val, 0, $this->svgunit, false);
24304  	 	 	 	 	 if (($key % 2) == 0) {
24305  	 	 	 	 	 	 // X coordinate
24306  	 	 	 	 	 	 $xmin = min($xmin, $p[$key]);
24307  	 	 	 	 	 	 $xmax = max($xmax, $p[$key]);
24308  	 	 	 	 	 } else {
24309  	 	 	 	 	 	 // Y coordinate
24310  	 	 	 	 	 	 $ymin = min($ymin, $p[$key]);
24311  	 	 	 	 	 	 $ymax = max($ymax, $p[$key]);
24312  	 	 	 	 	 }
24313  	 	 	 	 }
24314  	 	 	 	 $x = $xmin;
24315  	 	 	 	 $y = $ymin;
24316  	 	 	 	 $w = ($xmax - $xmin);
24317  	 	 	 	 $h = ($ymax - $ymin);
24318  	 	 	 	 if ($name == 'polyline') {
24319  	 	 	 	 	 $this->StartTransform();
24320  	 	 	 	 	 $this->SVGTransform($tm);
24321  	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'PolyLine', array($p, 'CNZ'));
24322  	 	 	 	 	 if (!empty($obstyle)) {
24323  	 	 	 	 	 	 $this->PolyLine($p, $obstyle, array(), array());
24324  	 	 	 	 	 }
24325  	 	 	 	 	 $this->StopTransform();
24326  	 	 	 	 } else { // polygon
24327  	 	 	 	 	 if ($clipping) {
24328  	 	 	 	 	 	 $this->SVGTransform($tm);
24329  	 	 	 	 	 	 $this->Polygon($p, 'CNZ', array(), array(), true);
24330  	 	 	 	 	 } else {
24331  	 	 	 	 	 	 $this->StartTransform();
24332  	 	 	 	 	 	 $this->SVGTransform($tm);
24333  	 	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h, 'Polygon', array($p, 'CNZ'));
24334  	 	 	 	 	 	 if (!empty($obstyle)) {
24335  	 	 	 	 	 	 	 $this->Polygon($p, $obstyle, array(), array(), true);
24336  	 	 	 	 	 	 }
24337  	 	 	 	 	 	 $this->StopTransform();
24338  	 	 	 	 	 }
24339  	 	 	 	 }
24340  	 	 	 	 break;
24341  	 	 	 }
24342  	 	 	 // image
24343  	 	 	 case 'image': {
24344  	 	 	 	 if ($invisible) {
24345  	 	 	 	 	 break;
24346  	 	 	 	 }
24347  	 	 	 	 if (!isset($attribs['xlink:href']) OR empty($attribs['xlink:href'])) {
24348  	 	 	 	 	 break;
24349  	 	 	 	 }
24350  	 	 	 	 $x = (isset($attribs['x'])?$this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false):0);
24351  	 	 	 	 $y = (isset($attribs['y'])?$this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false):0);
24352  	 	 	 	 $w = (isset($attribs['width'])?$this->getHTMLUnitToUnits($attribs['width'], 0, $this->svgunit, false):0);
24353  	 	 	 	 $h = (isset($attribs['height'])?$this->getHTMLUnitToUnits($attribs['height'], 0, $this->svgunit, false):0);
24354  	 	 	 	 $img = $attribs['xlink:href'];
24355  	 	 	 	 if (!$clipping) {
24356  	 	 	 	 	 $this->StartTransform();
24357  	 	 	 	 	 $this->SVGTransform($tm);
24358  	 	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, $w, $h);
24359  	 	 	 	 	 if (preg_match('/^data:image\/[^;]+;base64,/', $img, $m) > 0) {
24360  	 	 	 	 	 	 // embedded image encoded as base64
24361  	 	 	 	 	 	 $img = '@'.base64_decode(substr($img, strlen($m[0])));
24362  	 	 	 	 	 } else {
24363  	 	 	 	 	 	 // fix image path
24364  	 	 	 	 	 	 if (!TCPDF_STATIC::empty_string($this->svgdir) AND (($img[0] == '.') OR (basename($img) == $img))) {
24365  	 	 	 	 	 	 	 // replace relative path with full server path
24366  	 	 	 	 	 	 	 $img = $this->svgdir.'/'.$img;
24367  	 	 	 	 	 	 }
24368  	 	 	 	 	 	 if (($img[0] == '/') AND !empty($_SERVER['DOCUMENT_ROOT']) AND ($_SERVER['DOCUMENT_ROOT'] != '/')) {
24369  	 	 	 	 	 	 	 $findroot = strpos($img, $_SERVER['DOCUMENT_ROOT']);
24370  	 	 	 	 	 	 	 if (($findroot === false) OR ($findroot > 1)) {
24371  	 	 	 	 	 	 	 	 if (substr($_SERVER['DOCUMENT_ROOT'], -1) == '/') {
24372  	 	 	 	 	 	 	 	 	 $img = substr($_SERVER['DOCUMENT_ROOT'], 0, -1).$img;
24373  	 	 	 	 	 	 	 	 } else {
24374  	 	 	 	 	 	 	 	 	 $img = $_SERVER['DOCUMENT_ROOT'].$img;
24375  	 	 	 	 	 	 	 	 }
24376  	 	 	 	 	 	 	 }
24377  	 	 	 	 	 	 }
24378  	 	 	 	 	 	 $img = urldecode($img);
24379  	 	 	 	 	 	 $testscrtype = @parse_url($img);
24380  	 	 	 	 	 	 if (empty($testscrtype['query'])) {
24381  	 	 	 	 	 	 	 // convert URL to server path
24382  	 	 	 	 	 	 	 $img = str_replace(K_PATH_URL, K_PATH_MAIN, $img);
24383  	 	 	 	 	 	 } elseif (preg_match('|^https?://|', $img) !== 1) {
24384  	 	 	 	 	 	 	 // convert server path to URL
24385  	 	 	 	 	 	 	 $img = str_replace(K_PATH_MAIN, K_PATH_URL, $img);
24386  	 	 	 	 	 	 }
24387  	 	 	 	 	 }
24388  	 	 	 	 	 // get image type
24389  	 	 	 	 	 $imgtype = TCPDF_IMAGES::getImageFileType($img);
24390  	 	 	 	 	 if (($imgtype == 'eps') OR ($imgtype == 'ai')) {
24391  	 	 	 	 	 	 $this->ImageEps($img, $x, $y, $w, $h);
24392  	 	 	 	 	 } elseif ($imgtype == 'svg') {
24393  	 	 	 	 	 	 // store SVG vars
24394  	 	 	 	 	 	 $svggradients = $this->svggradients;
24395  	 	 	 	 	 	 $svggradientid = $this->svggradientid;
24396  	 	 	 	 	 	 $svgdefsmode = $this->svgdefsmode;
24397  	 	 	 	 	 	 $svgdefs = $this->svgdefs;
24398  	 	 	 	 	 	 $svgclipmode = $this->svgclipmode;
24399  	 	 	 	 	 	 $svgclippaths = $this->svgclippaths;
24400  	 	 	 	 	 	 $svgcliptm = $this->svgcliptm;
24401  	 	 	 	 	 	 $svgclipid = $this->svgclipid;
24402  	 	 	 	 	 	 $svgtext = $this->svgtext;
24403  	 	 	 	 	 	 $svgtextmode = $this->svgtextmode;
24404  	 	 	 	 	 	 $this->ImageSVG($img, $x, $y, $w, $h);
24405  	 	 	 	 	 	 // restore SVG vars
24406  	 	 	 	 	 	 $this->svggradients = $svggradients;
24407  	 	 	 	 	 	 $this->svggradientid = $svggradientid;
24408  	 	 	 	 	 	 $this->svgdefsmode = $svgdefsmode;
24409  	 	 	 	 	 	 $this->svgdefs = $svgdefs;
24410  	 	 	 	 	 	 $this->svgclipmode = $svgclipmode;
24411  	 	 	 	 	 	 $this->svgclippaths = $svgclippaths;
24412  	 	 	 	 	 	 $this->svgcliptm = $svgcliptm;
24413  	 	 	 	 	 	 $this->svgclipid = $svgclipid;
24414  	 	 	 	 	 	 $this->svgtext = $svgtext;
24415  	 	 	 	 	 	 $this->svgtextmode = $svgtextmode;
24416  	 	 	 	 	 } else {
24417  	 	 	 	 	 	 $this->Image($img, $x, $y, $w, $h);
24418  	 	 	 	 	 }
24419  	 	 	 	 	 $this->StopTransform();
24420  	 	 	 	 }
24421  	 	 	 	 break;
24422  	 	 	 }
24423  	 	 	 // text
24424  	 	 	 case 'text':
24425  	 	 	 case 'tspan': {
24426  	 	 	 	 if (isset($this->svgtextmode['text-anchor']) AND !empty($this->svgtext)) {
24427  	 	 	 	 	 // @TODO: unsupported feature
24428  	 	 	 	 }
24429  	 	 	 	 // only basic support - advanced features must be implemented
24430  	 	 	 	 $this->svgtextmode['invisible'] = $invisible;
24431  	 	 	 	 if ($invisible) {
24432  	 	 	 	 	 break;
24433  	 	 	 	 }
24434  	 	 	 	 array_push($this->svgstyles, $svgstyle);
24435  	 	 	 	 if (isset($attribs['x'])) {
24436  	 	 	 	 	 $x = $this->getHTMLUnitToUnits($attribs['x'], 0, $this->svgunit, false);
24437  	 	 	 	 } elseif ($name == 'tspan') {
24438  	 	 	 	 	 $x = $this->x;
24439  	 	 	 	 } else {
24440  	 	 	 	 	 $x = 0;
24441  	 	 	 	 }
24442  	 	 	 	 if (isset($attribs['dx'])) {
24443  	 	 	 	 	 $x += $this->getHTMLUnitToUnits($attribs['dx'], 0, $this->svgunit, false);
24444  	 	 	 	 }
24445  	 	 	 	 if (isset($attribs['y'])) {
24446  	 	 	 	 	 $y = $this->getHTMLUnitToUnits($attribs['y'], 0, $this->svgunit, false);
24447  	 	 	 	 } elseif ($name == 'tspan') {
24448  	 	 	 	 	 $y = $this->y;
24449  	 	 	 	 } else {
24450  	 	 	 	 	 $y = 0;
24451  	 	 	 	 }
24452  	 	 	 	 if (isset($attribs['dy'])) {
24453  	 	 	 	 	 $y += $this->getHTMLUnitToUnits($attribs['dy'], 0, $this->svgunit, false);
24454  	 	 	 	 }
24455  	 	 	 	 $svgstyle['text-color'] = $svgstyle['fill'];
24456  	 	 	 	 $this->svgtext = '';
24457  	 	 	 	 if (isset($svgstyle['text-anchor'])) {
24458  	 	 	 	 	 $this->svgtextmode['text-anchor'] = $svgstyle['text-anchor'];
24459  	 	 	 	 } else {
24460  	 	 	 	 	 $this->svgtextmode['text-anchor'] = 'start';
24461  	 	 	 	 }
24462  	 	 	 	 if (isset($svgstyle['direction'])) {
24463  	 	 	 	 	 if ($svgstyle['direction'] == 'rtl') {
24464  	 	 	 	 	 	 $this->svgtextmode['rtl'] = true;
24465  	 	 	 	 	 } else {
24466  	 	 	 	 	 	 $this->svgtextmode['rtl'] = false;
24467  	 	 	 	 	 }
24468  	 	 	 	 } else {
24469  	 	 	 	 	 $this->svgtextmode['rtl'] = false;
24470  	 	 	 	 }
24471  	 	 	 	 if (isset($svgstyle['stroke']) AND ($svgstyle['stroke'] != 'none') AND isset($svgstyle['stroke-width']) AND ($svgstyle['stroke-width'] > 0)) {
24472  	 	 	 	 	 $this->svgtextmode['stroke'] = $this->getHTMLUnitToUnits($svgstyle['stroke-width'], 0, $this->svgunit, false);
24473  	 	 	 	 } else {
24474  	 	 	 	 	 $this->svgtextmode['stroke'] = false;
24475  	 	 	 	 }
24476  	 	 	 	 $this->StartTransform();
24477  	 	 	 	 $this->SVGTransform($tm);
24478  	 	 	 	 $obstyle = $this->setSVGStyles($svgstyle, $prev_svgstyle, $x, $y, 1, 1);
24479  	 	 	 	 $this->x = $x;
24480  	 	 	 	 $this->y = $y;
24481  	 	 	 	 break;
24482  	 	 	 }
24483  	 	 	 // use
24484  	 	 	 case 'use': {
24485  	 	 	 	 if (isset($attribs['xlink:href']) AND !empty($attribs['xlink:href'])) {
24486  	 	 	 	 	 $svgdefid = substr($attribs['xlink:href'], 1);
24487  	 	 	 	 	 if (isset($this->svgdefs[$svgdefid])) {
24488  	 	 	 	 	 	 $use = $this->svgdefs[$svgdefid];
24489  	 	 	 	 	 	 if (isset($attribs['xlink:href'])) {
24490  	 	 	 	 	 	 	 unset($attribs['xlink:href']);
24491  	 	 	 	 	 	 }
24492  	 	 	 	 	 	 if (isset($attribs['id'])) {
24493  	 	 	 	 	 	 	 unset($attribs['id']);
24494  	 	 	 	 	 	 }
24495  	 	 	 	 	 	 if (isset($use['attribs']['x']) AND isset($attribs['x'])) {
24496  	 	 	 	 	 	 	 $attribs['x'] += $use['attribs']['x'];
24497  	 	 	 	 	 	 }
24498  	 	 	 	 	 	 if (isset($use['attribs']['y']) AND isset($attribs['y'])) {
24499  	 	 	 	 	 	 	 $attribs['y'] += $use['attribs']['y'];
24500  	 	 	 	 	 	 }
24501  	 	 	 	 	 	 if (empty($attribs['style'])) {
24502  	 	 	 	 	 	 	 $attribs['style'] = '';
24503  	 	 	 	 	 	 }
24504  	 	 	 	 	 	 if (!empty($use['attribs']['style'])) {
24505  	 	 	 	 	 	 	 // merge styles
24506  	 	 	 	 	 	 	 $attribs['style'] = str_replace(';;',';',';'.$use['attribs']['style'].$attribs['style']);
24507  	 	 	 	 	 	 }
24508  	 	 	 	 	 	 $attribs = array_merge($use['attribs'], $attribs);
24509  	 	 	 	 	 	 $this->startSVGElementHandler($parser, $use['name'], $attribs);
24510  	 	 	 	 	 	 return;
24511  	 	 	 	 	 }
24512  	 	 	 	 }
24513  	 	 	 	 break;
24514  	 	 	 }
24515  	 	 	 default: {
24516  	 	 	 	 break;
24517  	 	 	 }
24518  	 	 } // end of switch
24519  	 	 // process child elements
24520  	 	 if (!empty($attribs['child_elements'])) {
24521  	 	 	 $child_elements = $attribs['child_elements'];
24522  	 	 	 unset($attribs['child_elements']);
24523  	 	 	 foreach($child_elements as $child_element) {
24524  	 	 	 	 if (empty($child_element['attribs']['closing_tag'])) {
24525  	 	 	 	 	 $this->startSVGElementHandler('child-tag', $child_element['name'], $child_element['attribs']);
24526  	 	 	 	 } else {
24527  	 	 	 	 	 if (isset($child_element['attribs']['content'])) {
24528  	 	 	 	 	 	 $this->svgtext = $child_element['attribs']['content'];
24529  	 	 	 	 	 }
24530  	 	 	 	 	 $this->endSVGElementHandler('child-tag', $child_element['name']);
24531  	 	 	 	 }
24532  	 	 	 }
24533  	 	 }
24534  	 }
24535  
24536  	 /**
24537  	  * Sets the closing SVG element handler function for the XML parser.
24538  	  * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24539  	  * @param $name (string) 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.
24540  	  * @author Nicola Asuni
24541  	  * @since 5.0.000 (2010-05-02)
24542  	  * @protected
24543  	  */
24544  	protected function endSVGElementHandler($parser, $name) {
24545  	 	 $name = $this->removeTagNamespace($name);
24546  	 	 if ($this->svgdefsmode AND !in_array($name, array('defs', 'clipPath', 'linearGradient', 'radialGradient', 'stop'))) {;
24547  	 	 	 if (end($this->svgdefs) !== FALSE) {
24548  	 	 	 	 $last_svgdefs_id = key($this->svgdefs);
24549  	 	 	 	 if (isset($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'])) {
24550  	 	 	 	 	 foreach($this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'] as $child_element) {
24551  	 	 	 	 	 	 if (isset($child_element['attribs']['id']) AND ($child_element['name'] == $name)) {
24552  	 	 	 	 	 	 	 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$child_element['attribs']['id'].'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24553  	 	 	 	 	 	 	 return;
24554  	 	 	 	 	 	 }
24555  	 	 	 	 	 }
24556  	 	 	 	 	 if ($this->svgdefs[$last_svgdefs_id]['name'] == $name) {
24557  	 	 	 	 	 	 $this->svgdefs[$last_svgdefs_id]['attribs']['child_elements'][$last_svgdefs_id.'_CLOSE'] = array('name' => $name, 'attribs' => array('closing_tag' => TRUE, 'content' => $this->svgtext));
24558  	 	 	 	 	 	 return;
24559  	 	 	 	 	 }
24560  	 	 	 	 }
24561  	 	 	 }
24562  	 	 	 return;
24563  	 	 }
24564  	 	 switch($name) {
24565  	 	 	 case 'defs': {
24566  	 	 	 	 $this->svgdefsmode = false;
24567  	 	 	 	 break;
24568  	 	 	 }
24569  	 	 	 // clipPath
24570  	 	 	 case 'clipPath': {
24571  	 	 	 	 $this->svgclipmode = false;
24572  	 	 	 	 break;
24573  	 	 	 }
24574  	 	 	 case 'svg': {
24575  	 	 	 	 if (--$this->svg_tag_depth <= 0) {
24576  	 	 	 	 	 break;
24577  	 	 	 	 }
24578  	 	 	 }
24579  	 	 	 case 'g': {
24580  	 	 	 	 // ungroup: remove last style from array
24581  	 	 	 	 array_pop($this->svgstyles);
24582  	 	 	 	 $this->StopTransform();
24583  	 	 	 	 break;
24584  	 	 	 }
24585  	 	 	 case 'text':
24586  	 	 	 case 'tspan': {
24587  	 	 	 	 if ($this->svgtextmode['invisible']) {
24588  	 	 	 	 	 // This implementation must be fixed to following the rule:
24589  	 	 	 	 	 // If the 'visibility' property is set to hidden on a 'tspan', 'tref' or 'altGlyph' element, then the text is invisible but still takes up space in text layout calculations.
24590  	 	 	 	 	 break;
24591  	 	 	 	 }
24592  	 	 	 	 // print text
24593  	 	 	 	 $text = $this->svgtext;
24594  	 	 	 	 //$text = $this->stringTrim($text);
24595  	 	 	 	 $textlen = $this->GetStringWidth($text);
24596  	 	 	 	 if ($this->svgtextmode['text-anchor'] != 'start') {
24597  	 	 	 	 	 // check if string is RTL text
24598  	 	 	 	 	 if ($this->svgtextmode['text-anchor'] == 'end') {
24599  	 	 	 	 	 	 if ($this->svgtextmode['rtl']) {
24600  	 	 	 	 	 	 	 $this->x += $textlen;
24601  	 	 	 	 	 	 } else {
24602  	 	 	 	 	 	 	 $this->x -= $textlen;
24603  	 	 	 	 	 	 }
24604  	 	 	 	 	 } elseif ($this->svgtextmode['text-anchor'] == 'middle') {
24605  	 	 	 	 	 	 if ($this->svgtextmode['rtl']) {
24606  	 	 	 	 	 	 	 $this->x += ($textlen / 2);
24607  	 	 	 	 	 	 } else {
24608  	 	 	 	 	 	 	 $this->x -= ($textlen / 2);
24609  	 	 	 	 	 	 }
24610  	 	 	 	 	 }
24611  	 	 	 	 }
24612  	 	 	 	 $textrendermode = $this->textrendermode;
24613  	 	 	 	 $textstrokewidth = $this->textstrokewidth;
24614  	 	 	 	 $this->setTextRenderingMode($this->svgtextmode['stroke'], true, false);
24615  	 	 	 	 if ($name == 'text') {
24616  	 	 	 	 	 // store current coordinates
24617  	 	 	 	 	 $tmpx = $this->x;
24618  	 	 	 	 	 $tmpy = $this->y;
24619  	 	 	 	 }
24620  	 	 	 	 // print the text
24621  	 	 	 	 $this->Cell($textlen, 0, $text, 0, 0, '', false, '', 0, false, 'L', 'T');
24622  	 	 	 	 if ($name == 'text') {
24623  	 	 	 	 	 // restore coordinates
24624  	 	 	 	 	 $this->x = $tmpx;
24625  	 	 	 	 	 $this->y = $tmpy;
24626  	 	 	 	 }
24627  	 	 	 	 // restore previous rendering mode
24628  	 	 	 	 $this->textrendermode = $textrendermode;
24629  	 	 	 	 $this->textstrokewidth = $textstrokewidth;
24630  	 	 	 	 $this->svgtext = '';
24631  	 	 	 	 $this->StopTransform();
24632  	 	 	 	 if (!$this->svgdefsmode) {
24633  	 	 	 	 	 array_pop($this->svgstyles);
24634  	 	 	 	 }
24635  	 	 	 	 break;
24636  	 	 	 }
24637  	 	 	 default: {
24638  	 	 	 	 break;
24639  	 	 	 }
24640  	 	 }
24641  	 }
24642  
24643  	 /**
24644  	  * Sets the character data handler function for the XML parser.
24645  	  * @param $parser (resource) The first parameter, parser, is a reference to the XML parser calling the handler.
24646  	  * @param $data (string) The second parameter, data, contains the character data as a string.
24647  	  * @author Nicola Asuni
24648  	  * @since 5.0.000 (2010-05-02)
24649  	  * @protected
24650  	  */
24651  	protected function segSVGContentHandler($parser, $data) {
24652  	 	 $this->svgtext .= $data;
24653  	 }
24654  
24655  	 // --- END SVG METHODS -----------------------------------------------------
24656  
24657      /**
24658       * Keeps files in memory, so it doesn't need to downloaded everytime in a loop
24659       * @param string $file
24660       * @return string
24661       */
24662      protected function getCachedFileContents($file)
24663      {
24664          if (!isset($this->fileContentCache[$file])) {
24665              $this->fileContentCache[$file] = TCPDF_STATIC::fileGetContents($file);
24666          }
24667          return $this->fileContentCache[$file];
24668      }
24669  
24670      /**
24671       * Avoid multiple calls to an external server to see if a file exists
24672       * @param string $file
24673       * @return bool
24674       */
24675      protected function fileExists($file)
24676      {
24677          if (isset($this->fileContentCache[$file]) || false !== $this->getImageBuffer($file)) {
24678              return true;
24679          }
24680  
24681          return TCPDF_STATIC::file_exists($file);
24682      }
24683  
24684  } // END OF TCPDF CLASS
24685  
24686  //============================================================+
24687  // END OF FILE
24688  //============================================================+